2012-05-08 15:57:18 +02:00
/*
* Copyright ( C ) ST - Ericsson SA 2012
*
* Author : Ola Lilja < ola . o . lilja @ stericsson . com > ,
* Roger Nilsson < roger . xr . nilsson @ stericsson . com > ,
* Sandeep Kaushik < sandeep . kaushik @ st . com >
* for ST - Ericsson .
*
* License terms :
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation .
*/
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/delay.h>
# include <linux/slab.h>
2012-10-15 14:13:26 +01:00
# include <linux/io.h>
2012-07-26 17:07:26 +01:00
# include <linux/of.h>
2013-03-21 22:51:06 +01:00
# include <linux/platform_data/asoc-ux500-msp.h>
2012-05-08 15:57:18 +02:00
# include <sound/soc.h>
# include "ux500_msp_i2s.h"
/* Protocol desciptors */
static const struct msp_protdesc prot_descs [ ] = {
{ /* I2S */
MSP_SINGLE_PHASE ,
MSP_SINGLE_PHASE ,
MSP_PHASE2_START_MODE_IMEDIATE ,
MSP_PHASE2_START_MODE_IMEDIATE ,
MSP_BTF_MS_BIT_FIRST ,
MSP_BTF_MS_BIT_FIRST ,
MSP_FRAME_LEN_1 ,
MSP_FRAME_LEN_1 ,
MSP_FRAME_LEN_1 ,
MSP_FRAME_LEN_1 ,
MSP_ELEM_LEN_32 ,
MSP_ELEM_LEN_32 ,
MSP_ELEM_LEN_32 ,
MSP_ELEM_LEN_32 ,
MSP_DELAY_1 ,
MSP_DELAY_1 ,
MSP_RISING_EDGE ,
MSP_FALLING_EDGE ,
MSP_FSYNC_POL_ACT_LO ,
MSP_FSYNC_POL_ACT_LO ,
MSP_SWAP_NONE ,
MSP_SWAP_NONE ,
MSP_COMPRESS_MODE_LINEAR ,
MSP_EXPAND_MODE_LINEAR ,
MSP_FSYNC_IGNORE ,
31 ,
15 ,
32 ,
} , { /* PCM */
MSP_DUAL_PHASE ,
MSP_DUAL_PHASE ,
MSP_PHASE2_START_MODE_FSYNC ,
MSP_PHASE2_START_MODE_FSYNC ,
MSP_BTF_MS_BIT_FIRST ,
MSP_BTF_MS_BIT_FIRST ,
MSP_FRAME_LEN_1 ,
MSP_FRAME_LEN_1 ,
MSP_FRAME_LEN_1 ,
MSP_FRAME_LEN_1 ,
MSP_ELEM_LEN_16 ,
MSP_ELEM_LEN_16 ,
MSP_ELEM_LEN_16 ,
MSP_ELEM_LEN_16 ,
MSP_DELAY_0 ,
MSP_DELAY_0 ,
MSP_RISING_EDGE ,
MSP_FALLING_EDGE ,
MSP_FSYNC_POL_ACT_HI ,
MSP_FSYNC_POL_ACT_HI ,
MSP_SWAP_NONE ,
MSP_SWAP_NONE ,
MSP_COMPRESS_MODE_LINEAR ,
MSP_EXPAND_MODE_LINEAR ,
MSP_FSYNC_IGNORE ,
255 ,
0 ,
256 ,
} , { /* Companded PCM */
MSP_SINGLE_PHASE ,
MSP_SINGLE_PHASE ,
MSP_PHASE2_START_MODE_FSYNC ,
MSP_PHASE2_START_MODE_FSYNC ,
MSP_BTF_MS_BIT_FIRST ,
MSP_BTF_MS_BIT_FIRST ,
MSP_FRAME_LEN_1 ,
MSP_FRAME_LEN_1 ,
MSP_FRAME_LEN_1 ,
MSP_FRAME_LEN_1 ,
MSP_ELEM_LEN_8 ,
MSP_ELEM_LEN_8 ,
MSP_ELEM_LEN_8 ,
MSP_ELEM_LEN_8 ,
MSP_DELAY_0 ,
MSP_DELAY_0 ,
MSP_RISING_EDGE ,
MSP_RISING_EDGE ,
MSP_FSYNC_POL_ACT_HI ,
MSP_FSYNC_POL_ACT_HI ,
MSP_SWAP_NONE ,
MSP_SWAP_NONE ,
MSP_COMPRESS_MODE_LINEAR ,
MSP_EXPAND_MODE_LINEAR ,
MSP_FSYNC_IGNORE ,
255 ,
0 ,
256 ,
} ,
} ;
static void set_prot_desc_tx ( struct ux500_msp * msp ,
struct msp_protdesc * protdesc ,
enum msp_data_size data_size )
{
u32 temp_reg = 0 ;
temp_reg | = MSP_P2_ENABLE_BIT ( protdesc - > tx_phase_mode ) ;
temp_reg | = MSP_P2_START_MODE_BIT ( protdesc - > tx_phase2_start_mode ) ;
temp_reg | = MSP_P1_FRAME_LEN_BITS ( protdesc - > tx_frame_len_1 ) ;
temp_reg | = MSP_P2_FRAME_LEN_BITS ( protdesc - > tx_frame_len_2 ) ;
if ( msp - > def_elem_len ) {
temp_reg | = MSP_P1_ELEM_LEN_BITS ( protdesc - > tx_elem_len_1 ) ;
temp_reg | = MSP_P2_ELEM_LEN_BITS ( protdesc - > tx_elem_len_2 ) ;
} else {
temp_reg | = MSP_P1_ELEM_LEN_BITS ( data_size ) ;
temp_reg | = MSP_P2_ELEM_LEN_BITS ( data_size ) ;
}
temp_reg | = MSP_DATA_DELAY_BITS ( protdesc - > tx_data_delay ) ;
temp_reg | = MSP_SET_ENDIANNES_BIT ( protdesc - > tx_byte_order ) ;
temp_reg | = MSP_FSYNC_POL ( protdesc - > tx_fsync_pol ) ;
temp_reg | = MSP_DATA_WORD_SWAP ( protdesc - > tx_half_word_swap ) ;
temp_reg | = MSP_SET_COMPANDING_MODE ( protdesc - > compression_mode ) ;
temp_reg | = MSP_SET_FSYNC_IGNORE ( protdesc - > frame_sync_ignore ) ;
writel ( temp_reg , msp - > registers + MSP_TCF ) ;
}
static void set_prot_desc_rx ( struct ux500_msp * msp ,
struct msp_protdesc * protdesc ,
enum msp_data_size data_size )
{
u32 temp_reg = 0 ;
temp_reg | = MSP_P2_ENABLE_BIT ( protdesc - > rx_phase_mode ) ;
temp_reg | = MSP_P2_START_MODE_BIT ( protdesc - > rx_phase2_start_mode ) ;
temp_reg | = MSP_P1_FRAME_LEN_BITS ( protdesc - > rx_frame_len_1 ) ;
temp_reg | = MSP_P2_FRAME_LEN_BITS ( protdesc - > rx_frame_len_2 ) ;
if ( msp - > def_elem_len ) {
temp_reg | = MSP_P1_ELEM_LEN_BITS ( protdesc - > rx_elem_len_1 ) ;
temp_reg | = MSP_P2_ELEM_LEN_BITS ( protdesc - > rx_elem_len_2 ) ;
} else {
temp_reg | = MSP_P1_ELEM_LEN_BITS ( data_size ) ;
temp_reg | = MSP_P2_ELEM_LEN_BITS ( data_size ) ;
}
temp_reg | = MSP_DATA_DELAY_BITS ( protdesc - > rx_data_delay ) ;
temp_reg | = MSP_SET_ENDIANNES_BIT ( protdesc - > rx_byte_order ) ;
temp_reg | = MSP_FSYNC_POL ( protdesc - > rx_fsync_pol ) ;
temp_reg | = MSP_DATA_WORD_SWAP ( protdesc - > rx_half_word_swap ) ;
temp_reg | = MSP_SET_COMPANDING_MODE ( protdesc - > expansion_mode ) ;
temp_reg | = MSP_SET_FSYNC_IGNORE ( protdesc - > frame_sync_ignore ) ;
writel ( temp_reg , msp - > registers + MSP_RCF ) ;
}
static int configure_protocol ( struct ux500_msp * msp ,
struct ux500_msp_config * config )
{
struct msp_protdesc * protdesc ;
enum msp_data_size data_size ;
u32 temp_reg = 0 ;
data_size = config - > data_size ;
msp - > def_elem_len = config - > def_elem_len ;
if ( config - > default_protdesc = = 1 ) {
if ( config - > protocol > = MSP_INVALID_PROTOCOL ) {
dev_err ( msp - > dev , " %s: ERROR: Invalid protocol! \n " ,
__func__ ) ;
return - EINVAL ;
}
protdesc =
( struct msp_protdesc * ) & prot_descs [ config - > protocol ] ;
} else {
protdesc = ( struct msp_protdesc * ) & config - > protdesc ;
}
if ( data_size < MSP_DATA_BITS_DEFAULT | | data_size > MSP_DATA_BITS_32 ) {
dev_err ( msp - > dev ,
" %s: ERROR: Invalid data-size requested (data_size = %d)! \n " ,
__func__ , data_size ) ;
return - EINVAL ;
}
if ( config - > direction & MSP_DIR_TX )
set_prot_desc_tx ( msp , protdesc , data_size ) ;
if ( config - > direction & MSP_DIR_RX )
set_prot_desc_rx ( msp , protdesc , data_size ) ;
/* The code below should not be separated. */
temp_reg = readl ( msp - > registers + MSP_GCR ) & ~ TX_CLK_POL_RISING ;
temp_reg | = MSP_TX_CLKPOL_BIT ( ~ protdesc - > tx_clk_pol ) ;
writel ( temp_reg , msp - > registers + MSP_GCR ) ;
temp_reg = readl ( msp - > registers + MSP_GCR ) & ~ RX_CLK_POL_RISING ;
temp_reg | = MSP_RX_CLKPOL_BIT ( protdesc - > rx_clk_pol ) ;
writel ( temp_reg , msp - > registers + MSP_GCR ) ;
return 0 ;
}
static int setup_bitclk ( struct ux500_msp * msp , struct ux500_msp_config * config )
{
u32 reg_val_GCR ;
u32 frame_per = 0 ;
u32 sck_div = 0 ;
u32 frame_width = 0 ;
u32 temp_reg = 0 ;
struct msp_protdesc * protdesc = NULL ;
reg_val_GCR = readl ( msp - > registers + MSP_GCR ) ;
writel ( reg_val_GCR & ~ SRG_ENABLE , msp - > registers + MSP_GCR ) ;
if ( config - > default_protdesc )
protdesc =
( struct msp_protdesc * ) & prot_descs [ config - > protocol ] ;
else
protdesc = ( struct msp_protdesc * ) & config - > protdesc ;
switch ( config - > protocol ) {
case MSP_PCM_PROTOCOL :
case MSP_PCM_COMPAND_PROTOCOL :
frame_width = protdesc - > frame_width ;
sck_div = config - > f_inputclk / ( config - > frame_freq *
( protdesc - > clocks_per_frame ) ) ;
frame_per = protdesc - > frame_period ;
break ;
case MSP_I2S_PROTOCOL :
frame_width = protdesc - > frame_width ;
sck_div = config - > f_inputclk / ( config - > frame_freq *
( protdesc - > clocks_per_frame ) ) ;
frame_per = protdesc - > frame_period ;
break ;
default :
dev_err ( msp - > dev , " %s: ERROR: Unknown protocol (%d)! \n " ,
__func__ ,
config - > protocol ) ;
return - EINVAL ;
}
temp_reg = ( sck_div - 1 ) & SCK_DIV_MASK ;
temp_reg | = FRAME_WIDTH_BITS ( frame_width ) ;
temp_reg | = FRAME_PERIOD_BITS ( frame_per ) ;
writel ( temp_reg , msp - > registers + MSP_SRG ) ;
msp - > f_bitclk = ( config - > f_inputclk ) / ( sck_div + 1 ) ;
/* Enable bit-clock */
udelay ( 100 ) ;
reg_val_GCR = readl ( msp - > registers + MSP_GCR ) ;
writel ( reg_val_GCR | SRG_ENABLE , msp - > registers + MSP_GCR ) ;
udelay ( 100 ) ;
return 0 ;
}
static int configure_multichannel ( struct ux500_msp * msp ,
struct ux500_msp_config * config )
{
struct msp_protdesc * protdesc ;
struct msp_multichannel_config * mcfg ;
u32 reg_val_MCR ;
if ( config - > default_protdesc = = 1 ) {
if ( config - > protocol > = MSP_INVALID_PROTOCOL ) {
dev_err ( msp - > dev ,
" %s: ERROR: Invalid protocol (%d)! \n " ,
__func__ , config - > protocol ) ;
return - EINVAL ;
}
protdesc = ( struct msp_protdesc * )
& prot_descs [ config - > protocol ] ;
} else {
protdesc = ( struct msp_protdesc * ) & config - > protdesc ;
}
mcfg = & config - > multichannel_config ;
if ( mcfg - > tx_multichannel_enable ) {
if ( protdesc - > tx_phase_mode = = MSP_SINGLE_PHASE ) {
reg_val_MCR = readl ( msp - > registers + MSP_MCR ) ;
writel ( reg_val_MCR | ( mcfg - > tx_multichannel_enable ?
1 < < TMCEN_BIT : 0 ) ,
msp - > registers + MSP_MCR ) ;
writel ( mcfg - > tx_channel_0_enable ,
msp - > registers + MSP_TCE0 ) ;
writel ( mcfg - > tx_channel_1_enable ,
msp - > registers + MSP_TCE1 ) ;
writel ( mcfg - > tx_channel_2_enable ,
msp - > registers + MSP_TCE2 ) ;
writel ( mcfg - > tx_channel_3_enable ,
msp - > registers + MSP_TCE3 ) ;
} else {
dev_err ( msp - > dev ,
" %s: ERROR: Only single-phase supported (TX-mode: %d)! \n " ,
__func__ , protdesc - > tx_phase_mode ) ;
return - EINVAL ;
}
}
if ( mcfg - > rx_multichannel_enable ) {
if ( protdesc - > rx_phase_mode = = MSP_SINGLE_PHASE ) {
reg_val_MCR = readl ( msp - > registers + MSP_MCR ) ;
writel ( reg_val_MCR | ( mcfg - > rx_multichannel_enable ?
1 < < RMCEN_BIT : 0 ) ,
msp - > registers + MSP_MCR ) ;
writel ( mcfg - > rx_channel_0_enable ,
msp - > registers + MSP_RCE0 ) ;
writel ( mcfg - > rx_channel_1_enable ,
msp - > registers + MSP_RCE1 ) ;
writel ( mcfg - > rx_channel_2_enable ,
msp - > registers + MSP_RCE2 ) ;
writel ( mcfg - > rx_channel_3_enable ,
msp - > registers + MSP_RCE3 ) ;
} else {
dev_err ( msp - > dev ,
" %s: ERROR: Only single-phase supported (RX-mode: %d)! \n " ,
__func__ , protdesc - > rx_phase_mode ) ;
return - EINVAL ;
}
if ( mcfg - > rx_comparison_enable_mode ) {
reg_val_MCR = readl ( msp - > registers + MSP_MCR ) ;
writel ( reg_val_MCR |
( mcfg - > rx_comparison_enable_mode < < RCMPM_BIT ) ,
msp - > registers + MSP_MCR ) ;
writel ( mcfg - > comparison_mask ,
msp - > registers + MSP_RCM ) ;
writel ( mcfg - > comparison_value ,
msp - > registers + MSP_RCV ) ;
}
}
return 0 ;
}
static int enable_msp ( struct ux500_msp * msp , struct ux500_msp_config * config )
{
2013-05-24 12:39:15 +02:00
int status = 0 ;
2012-05-08 15:57:18 +02:00
u32 reg_val_DMACR , reg_val_GCR ;
/* Configure msp with protocol dependent settings */
configure_protocol ( msp , config ) ;
setup_bitclk ( msp , config ) ;
if ( config - > multichannel_configured = = 1 ) {
status = configure_multichannel ( msp , config ) ;
if ( status )
dev_warn ( msp - > dev ,
" %s: WARN: configure_multichannel failed (%d)! \n " ,
__func__ , status ) ;
}
/* Make sure the correct DMA-directions are configured */
2013-06-12 09:57:57 +02:00
if ( ( config - > direction & MSP_DIR_RX ) & &
! msp - > capture_dma_data . dma_cfg ) {
2012-05-08 15:57:18 +02:00
dev_err ( msp - > dev , " %s: ERROR: MSP RX-mode is not configured! " ,
__func__ ) ;
return - EINVAL ;
}
2013-06-12 09:57:57 +02:00
if ( ( config - > direction = = MSP_DIR_TX ) & &
! msp - > playback_dma_data . dma_cfg ) {
2012-05-08 15:57:18 +02:00
dev_err ( msp - > dev , " %s: ERROR: MSP TX-mode is not configured! " ,
__func__ ) ;
return - EINVAL ;
}
reg_val_DMACR = readl ( msp - > registers + MSP_DMACR ) ;
if ( config - > direction & MSP_DIR_RX )
reg_val_DMACR | = RX_DMA_ENABLE ;
if ( config - > direction & MSP_DIR_TX )
reg_val_DMACR | = TX_DMA_ENABLE ;
writel ( reg_val_DMACR , msp - > registers + MSP_DMACR ) ;
writel ( config - > iodelay , msp - > registers + MSP_IODLY ) ;
/* Enable frame generation logic */
reg_val_GCR = readl ( msp - > registers + MSP_GCR ) ;
writel ( reg_val_GCR | FRAME_GEN_ENABLE , msp - > registers + MSP_GCR ) ;
return status ;
}
static void flush_fifo_rx ( struct ux500_msp * msp )
{
u32 reg_val_DR , reg_val_GCR , reg_val_FLR ;
u32 limit = 32 ;
reg_val_GCR = readl ( msp - > registers + MSP_GCR ) ;
writel ( reg_val_GCR | RX_ENABLE , msp - > registers + MSP_GCR ) ;
reg_val_FLR = readl ( msp - > registers + MSP_FLR ) ;
while ( ! ( reg_val_FLR & RX_FIFO_EMPTY ) & & limit - - ) {
reg_val_DR = readl ( msp - > registers + MSP_DR ) ;
reg_val_FLR = readl ( msp - > registers + MSP_FLR ) ;
}
writel ( reg_val_GCR , msp - > registers + MSP_GCR ) ;
}
static void flush_fifo_tx ( struct ux500_msp * msp )
{
u32 reg_val_TSTDR , reg_val_GCR , reg_val_FLR ;
u32 limit = 32 ;
reg_val_GCR = readl ( msp - > registers + MSP_GCR ) ;
writel ( reg_val_GCR | TX_ENABLE , msp - > registers + MSP_GCR ) ;
writel ( MSP_ITCR_ITEN | MSP_ITCR_TESTFIFO , msp - > registers + MSP_ITCR ) ;
reg_val_FLR = readl ( msp - > registers + MSP_FLR ) ;
while ( ! ( reg_val_FLR & TX_FIFO_EMPTY ) & & limit - - ) {
reg_val_TSTDR = readl ( msp - > registers + MSP_TSTDR ) ;
reg_val_FLR = readl ( msp - > registers + MSP_FLR ) ;
}
writel ( 0x0 , msp - > registers + MSP_ITCR ) ;
writel ( reg_val_GCR , msp - > registers + MSP_GCR ) ;
}
int ux500_msp_i2s_open ( struct ux500_msp * msp ,
struct ux500_msp_config * config )
{
u32 old_reg , new_reg , mask ;
int res ;
unsigned int tx_sel , rx_sel , tx_busy , rx_busy ;
if ( in_interrupt ( ) ) {
dev_err ( msp - > dev ,
" %s: ERROR: Open called in interrupt context! \n " ,
__func__ ) ;
return - 1 ;
}
tx_sel = ( config - > direction & MSP_DIR_TX ) > 0 ;
rx_sel = ( config - > direction & MSP_DIR_RX ) > 0 ;
if ( ! tx_sel & & ! rx_sel ) {
dev_err ( msp - > dev , " %s: Error: No direction selected! \n " ,
__func__ ) ;
return - EINVAL ;
}
tx_busy = ( msp - > dir_busy & MSP_DIR_TX ) > 0 ;
rx_busy = ( msp - > dir_busy & MSP_DIR_RX ) > 0 ;
if ( tx_busy & & tx_sel ) {
dev_err ( msp - > dev , " %s: Error: TX is in use! \n " , __func__ ) ;
return - EBUSY ;
}
if ( rx_busy & & rx_sel ) {
dev_err ( msp - > dev , " %s: Error: RX is in use! \n " , __func__ ) ;
return - EBUSY ;
}
msp - > dir_busy | = ( tx_sel ? MSP_DIR_TX : 0 ) | ( rx_sel ? MSP_DIR_RX : 0 ) ;
/* First do the global config register */
mask = RX_CLK_SEL_MASK | TX_CLK_SEL_MASK | RX_FSYNC_MASK |
TX_FSYNC_MASK | RX_SYNC_SEL_MASK | TX_SYNC_SEL_MASK |
RX_FIFO_ENABLE_MASK | TX_FIFO_ENABLE_MASK | SRG_CLK_SEL_MASK |
LOOPBACK_MASK | TX_EXTRA_DELAY_MASK ;
new_reg = ( config - > tx_clk_sel | config - > rx_clk_sel |
config - > rx_fsync_pol | config - > tx_fsync_pol |
config - > rx_fsync_sel | config - > tx_fsync_sel |
config - > rx_fifo_config | config - > tx_fifo_config |
config - > srg_clk_sel | config - > loopback_enable |
config - > tx_data_enable ) ;
old_reg = readl ( msp - > registers + MSP_GCR ) ;
old_reg & = ~ mask ;
new_reg | = old_reg ;
writel ( new_reg , msp - > registers + MSP_GCR ) ;
res = enable_msp ( msp , config ) ;
if ( res < 0 ) {
dev_err ( msp - > dev , " %s: ERROR: enable_msp failed (%d)! \n " ,
__func__ , res ) ;
return - EBUSY ;
}
if ( config - > loopback_enable & 0x80 )
msp - > loopback_enable = 1 ;
/* Flush FIFOs */
flush_fifo_tx ( msp ) ;
flush_fifo_rx ( msp ) ;
msp - > msp_state = MSP_STATE_CONFIGURED ;
return 0 ;
}
static void disable_msp_rx ( struct ux500_msp * msp )
{
u32 reg_val_GCR , reg_val_DMACR , reg_val_IMSC ;
reg_val_GCR = readl ( msp - > registers + MSP_GCR ) ;
writel ( reg_val_GCR & ~ RX_ENABLE , msp - > registers + MSP_GCR ) ;
reg_val_DMACR = readl ( msp - > registers + MSP_DMACR ) ;
writel ( reg_val_DMACR & ~ RX_DMA_ENABLE , msp - > registers + MSP_DMACR ) ;
reg_val_IMSC = readl ( msp - > registers + MSP_IMSC ) ;
writel ( reg_val_IMSC &
~ ( RX_SERVICE_INT | RX_OVERRUN_ERROR_INT ) ,
msp - > registers + MSP_IMSC ) ;
msp - > dir_busy & = ~ MSP_DIR_RX ;
}
static void disable_msp_tx ( struct ux500_msp * msp )
{
u32 reg_val_GCR , reg_val_DMACR , reg_val_IMSC ;
reg_val_GCR = readl ( msp - > registers + MSP_GCR ) ;
writel ( reg_val_GCR & ~ TX_ENABLE , msp - > registers + MSP_GCR ) ;
reg_val_DMACR = readl ( msp - > registers + MSP_DMACR ) ;
writel ( reg_val_DMACR & ~ TX_DMA_ENABLE , msp - > registers + MSP_DMACR ) ;
reg_val_IMSC = readl ( msp - > registers + MSP_IMSC ) ;
writel ( reg_val_IMSC &
~ ( TX_SERVICE_INT | TX_UNDERRUN_ERR_INT ) ,
msp - > registers + MSP_IMSC ) ;
msp - > dir_busy & = ~ MSP_DIR_TX ;
}
static int disable_msp ( struct ux500_msp * msp , unsigned int dir )
{
u32 reg_val_GCR ;
int status = 0 ;
unsigned int disable_tx , disable_rx ;
reg_val_GCR = readl ( msp - > registers + MSP_GCR ) ;
disable_tx = dir & MSP_DIR_TX ;
disable_rx = dir & MSP_DIR_TX ;
if ( disable_tx & & disable_rx ) {
reg_val_GCR = readl ( msp - > registers + MSP_GCR ) ;
writel ( reg_val_GCR | LOOPBACK_MASK ,
msp - > registers + MSP_GCR ) ;
/* Flush TX-FIFO */
flush_fifo_tx ( msp ) ;
/* Disable TX-channel */
writel ( ( readl ( msp - > registers + MSP_GCR ) &
( ~ TX_ENABLE ) ) , msp - > registers + MSP_GCR ) ;
/* Flush RX-FIFO */
flush_fifo_rx ( msp ) ;
/* Disable Loopback and Receive channel */
writel ( ( readl ( msp - > registers + MSP_GCR ) &
( ~ ( RX_ENABLE | LOOPBACK_MASK ) ) ) ,
msp - > registers + MSP_GCR ) ;
disable_msp_tx ( msp ) ;
disable_msp_rx ( msp ) ;
} else if ( disable_tx )
disable_msp_tx ( msp ) ;
else if ( disable_rx )
disable_msp_rx ( msp ) ;
return status ;
}
int ux500_msp_i2s_trigger ( struct ux500_msp * msp , int cmd , int direction )
{
u32 reg_val_GCR , enable_bit ;
if ( msp - > msp_state = = MSP_STATE_IDLE ) {
dev_err ( msp - > dev , " %s: ERROR: MSP is not configured! \n " ,
__func__ ) ;
return - EINVAL ;
}
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
case SNDRV_PCM_TRIGGER_RESUME :
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
if ( direction = = SNDRV_PCM_STREAM_PLAYBACK )
enable_bit = TX_ENABLE ;
else
enable_bit = RX_ENABLE ;
reg_val_GCR = readl ( msp - > registers + MSP_GCR ) ;
writel ( reg_val_GCR | enable_bit , msp - > registers + MSP_GCR ) ;
break ;
case SNDRV_PCM_TRIGGER_STOP :
case SNDRV_PCM_TRIGGER_SUSPEND :
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
if ( direction = = SNDRV_PCM_STREAM_PLAYBACK )
disable_msp_tx ( msp ) ;
else
disable_msp_rx ( msp ) ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
int ux500_msp_i2s_close ( struct ux500_msp * msp , unsigned int dir )
{
2013-05-24 12:39:15 +02:00
int status = 0 ;
2012-05-08 15:57:18 +02:00
dev_dbg ( msp - > dev , " %s: Enter (dir = 0x%01x). \n " , __func__ , dir ) ;
status = disable_msp ( msp , dir ) ;
if ( msp - > dir_busy = = 0 ) {
/* disable sample rate and frame generators */
msp - > msp_state = MSP_STATE_IDLE ;
writel ( ( readl ( msp - > registers + MSP_GCR ) &
( ~ ( FRAME_GEN_ENABLE | SRG_ENABLE ) ) ) ,
msp - > registers + MSP_GCR ) ;
2012-09-14 16:16:08 +01:00
2012-05-08 15:57:18 +02:00
writel ( 0 , msp - > registers + MSP_GCR ) ;
writel ( 0 , msp - > registers + MSP_TCF ) ;
writel ( 0 , msp - > registers + MSP_RCF ) ;
writel ( 0 , msp - > registers + MSP_DMACR ) ;
writel ( 0 , msp - > registers + MSP_SRG ) ;
writel ( 0 , msp - > registers + MSP_MCR ) ;
writel ( 0 , msp - > registers + MSP_RCM ) ;
writel ( 0 , msp - > registers + MSP_RCV ) ;
writel ( 0 , msp - > registers + MSP_TCE0 ) ;
writel ( 0 , msp - > registers + MSP_TCE1 ) ;
writel ( 0 , msp - > registers + MSP_TCE2 ) ;
writel ( 0 , msp - > registers + MSP_TCE3 ) ;
writel ( 0 , msp - > registers + MSP_RCE0 ) ;
writel ( 0 , msp - > registers + MSP_RCE1 ) ;
writel ( 0 , msp - > registers + MSP_RCE2 ) ;
writel ( 0 , msp - > registers + MSP_RCE3 ) ;
}
return status ;
}
2014-01-08 18:13:35 +08:00
static int ux500_msp_i2s_of_init_msp ( struct platform_device * pdev ,
struct ux500_msp * msp ,
struct msp_i2s_platform_data * * platform_data )
2013-12-19 15:55:04 +00:00
{
struct msp_i2s_platform_data * pdata ;
* platform_data = devm_kzalloc ( & pdev - > dev ,
sizeof ( struct msp_i2s_platform_data ) ,
GFP_KERNEL ) ;
pdata = * platform_data ;
if ( ! pdata )
return - ENOMEM ;
msp - > playback_dma_data . dma_cfg = devm_kzalloc ( & pdev - > dev ,
sizeof ( struct stedma40_chan_cfg ) ,
GFP_KERNEL ) ;
if ( ! msp - > playback_dma_data . dma_cfg )
return - ENOMEM ;
msp - > capture_dma_data . dma_cfg = devm_kzalloc ( & pdev - > dev ,
sizeof ( struct stedma40_chan_cfg ) ,
GFP_KERNEL ) ;
if ( ! msp - > capture_dma_data . dma_cfg )
return - ENOMEM ;
return 0 ;
}
2012-05-08 15:57:18 +02:00
int ux500_msp_i2s_init_msp ( struct platform_device * pdev ,
struct ux500_msp * * msp_p ,
struct msp_i2s_platform_data * platform_data )
{
struct resource * res = NULL ;
2012-07-26 17:07:26 +01:00
struct device_node * np = pdev - > dev . of_node ;
2012-05-08 15:57:18 +02:00
struct ux500_msp * msp ;
2013-12-19 15:55:04 +00:00
int ret ;
2012-05-08 15:57:18 +02:00
* msp_p = devm_kzalloc ( & pdev - > dev , sizeof ( struct ux500_msp ) , GFP_KERNEL ) ;
msp = * msp_p ;
2012-07-26 11:28:37 +01:00
if ( ! msp )
return - ENOMEM ;
2012-05-08 15:57:18 +02:00
2013-12-19 15:55:04 +00:00
if ( ! platform_data ) {
if ( np ) {
ret = ux500_msp_i2s_of_init_msp ( pdev , msp ,
& platform_data ) ;
if ( ret )
return ret ;
} else
2012-10-15 14:13:26 +01:00
return - EINVAL ;
2013-12-19 15:55:04 +00:00
} else {
msp - > playback_dma_data . dma_cfg = platform_data - > msp_i2s_dma_tx ;
msp - > capture_dma_data . dma_cfg = platform_data - > msp_i2s_dma_rx ;
msp - > id = platform_data - > id ;
}
2012-07-26 17:07:26 +01:00
2012-05-08 15:57:18 +02:00
msp - > dev = & pdev - > dev ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( res = = NULL ) {
dev_err ( & pdev - > dev , " %s: ERROR: Unable to get resource! \n " ,
__func__ ) ;
2012-08-19 09:02:53 +02:00
return - ENOMEM ;
2012-05-08 15:57:18 +02:00
}
2013-06-12 09:57:58 +02:00
msp - > playback_dma_data . tx_rx_addr = res - > start + MSP_DR ;
msp - > capture_dma_data . tx_rx_addr = res - > start + MSP_DR ;
2012-08-19 09:02:53 +02:00
msp - > registers = devm_ioremap ( & pdev - > dev , res - > start ,
resource_size ( res ) ) ;
2012-05-08 15:57:18 +02:00
if ( msp - > registers = = NULL ) {
dev_err ( & pdev - > dev , " %s: ERROR: ioremap failed! \n " , __func__ ) ;
2012-08-19 09:02:53 +02:00
return - ENOMEM ;
2012-05-08 15:57:18 +02:00
}
msp - > msp_state = MSP_STATE_IDLE ;
msp - > loopback_enable = 0 ;
return 0 ;
}
void ux500_msp_i2s_cleanup_msp ( struct platform_device * pdev ,
struct ux500_msp * msp )
{
dev_dbg ( msp - > dev , " %s: Enter (id = %d). \n " , __func__ , msp - > id ) ;
}
2012-06-13 10:09:51 +02:00
MODULE_LICENSE ( " GPL v2 " ) ;