2012-06-21 15:54:51 +05:30
/*
* ALSA SoC Synopsys I2S Audio Layer
*
2013-06-11 09:29:08 +05:30
* sound / soc / dwc / designware_i2s . c
2012-06-21 15:54:51 +05:30
*
* Copyright ( C ) 2010 ST Microelectronics
2014-09-05 16:47:04 +05:30
* Rajeev Kumar < rajeevkumar . linux @ gmail . com >
2012-06-21 15:54:51 +05:30
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed " as is " without any
* warranty of any kind , whether express or implied .
*/
# include <linux/clk.h>
# include <linux/device.h>
# include <linux/init.h>
# include <linux/io.h>
# include <linux/interrupt.h>
# include <linux/module.h>
# include <linux/slab.h>
# include <sound/designware_i2s.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/soc.h>
2014-12-30 10:55:48 +00:00
# include <sound/dmaengine_pcm.h>
2012-06-21 15:54:51 +05:30
/* common register for all channel */
# define IER 0x000
# define IRER 0x004
# define ITER 0x008
# define CER 0x00C
# define CCR 0x010
# define RXFFR 0x014
# define TXFFR 0x018
/* I2STxRxRegisters for all channels */
# define LRBR_LTHR(x) (0x40 * x + 0x020)
# define RRBR_RTHR(x) (0x40 * x + 0x024)
# define RER(x) (0x40 * x + 0x028)
# define TER(x) (0x40 * x + 0x02C)
# define RCR(x) (0x40 * x + 0x030)
# define TCR(x) (0x40 * x + 0x034)
# define ISR(x) (0x40 * x + 0x038)
# define IMR(x) (0x40 * x + 0x03C)
# define ROR(x) (0x40 * x + 0x040)
# define TOR(x) (0x40 * x + 0x044)
# define RFCR(x) (0x40 * x + 0x048)
# define TFCR(x) (0x40 * x + 0x04C)
# define RFF(x) (0x40 * x + 0x050)
# define TFF(x) (0x40 * x + 0x054)
/* I2SCOMPRegisters */
# define I2S_COMP_PARAM_2 0x01F0
# define I2S_COMP_PARAM_1 0x01F4
# define I2S_COMP_VERSION 0x01F8
# define I2S_COMP_TYPE 0x01FC
2014-12-30 10:55:45 +00:00
/*
* Component parameter register fields - define the I2S block ' s
* configuration .
*/
# define COMP1_TX_WORDSIZE_3(r) (((r) & GENMASK(27, 25)) >> 25)
# define COMP1_TX_WORDSIZE_2(r) (((r) & GENMASK(24, 22)) >> 22)
# define COMP1_TX_WORDSIZE_1(r) (((r) & GENMASK(21, 19)) >> 19)
# define COMP1_TX_WORDSIZE_0(r) (((r) & GENMASK(18, 16)) >> 16)
# define COMP1_TX_CHANNELS(r) (((r) & GENMASK(10, 9)) >> 9)
# define COMP1_RX_CHANNELS(r) (((r) & GENMASK(8, 7)) >> 7)
# define COMP1_RX_ENABLED(r) (((r) & BIT(6)) >> 6)
# define COMP1_TX_ENABLED(r) (((r) & BIT(5)) >> 5)
# define COMP1_MODE_EN(r) (((r) & BIT(4)) >> 4)
# define COMP1_FIFO_DEPTH_GLOBAL(r) (((r) & GENMASK(3, 2)) >> 2)
# define COMP1_APB_DATA_WIDTH(r) (((r) & GENMASK(1, 0)) >> 0)
# define COMP2_RX_WORDSIZE_3(r) (((r) & GENMASK(12, 10)) >> 10)
# define COMP2_RX_WORDSIZE_2(r) (((r) & GENMASK(9, 7)) >> 7)
# define COMP2_RX_WORDSIZE_1(r) (((r) & GENMASK(5, 3)) >> 3)
# define COMP2_RX_WORDSIZE_0(r) (((r) & GENMASK(2, 0)) >> 0)
/* Number of entries in WORDSIZE and DATA_WIDTH parameter registers */
# define COMP_MAX_WORDSIZE (1 << 3)
# define COMP_MAX_DATA_WIDTH (1 << 2)
2012-06-21 15:54:51 +05:30
# define MAX_CHANNEL_NUM 8
# define MIN_CHANNEL_NUM 2
2014-12-30 10:55:48 +00:00
union dw_i2s_snd_dma_data {
struct i2s_dma_data pd ;
struct snd_dmaengine_dai_dma_data dt ;
} ;
2012-06-21 15:54:51 +05:30
struct dw_i2s_dev {
void __iomem * i2s_base ;
struct clk * clk ;
int active ;
unsigned int capability ;
struct device * dev ;
/* data related to DMA transfers b/w i2s and DMAC */
2014-12-30 10:55:48 +00:00
union dw_i2s_snd_dma_data play_dma_data ;
union dw_i2s_snd_dma_data capture_dma_data ;
2012-06-21 15:54:51 +05:30
struct i2s_clk_config_data config ;
int ( * i2s_clk_cfg ) ( struct i2s_clk_config_data * config ) ;
} ;
2012-06-28 13:11:47 +01:00
static inline void i2s_write_reg ( void __iomem * io_base , int reg , u32 val )
2012-06-21 15:54:51 +05:30
{
writel ( val , io_base + reg ) ;
}
2012-06-28 13:11:47 +01:00
static inline u32 i2s_read_reg ( void __iomem * io_base , int reg )
2012-06-21 15:54:51 +05:30
{
return readl ( io_base + reg ) ;
}
static inline void i2s_disable_channels ( struct dw_i2s_dev * dev , u32 stream )
{
u32 i = 0 ;
if ( stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
for ( i = 0 ; i < 4 ; i + + )
i2s_write_reg ( dev - > i2s_base , TER ( i ) , 0 ) ;
} else {
for ( i = 0 ; i < 4 ; i + + )
i2s_write_reg ( dev - > i2s_base , RER ( i ) , 0 ) ;
}
}
static inline void i2s_clear_irqs ( struct dw_i2s_dev * dev , u32 stream )
{
u32 i = 0 ;
if ( stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
for ( i = 0 ; i < 4 ; i + + )
2015-10-02 15:18:41 +08:00
i2s_read_reg ( dev - > i2s_base , TOR ( i ) ) ;
2012-06-21 15:54:51 +05:30
} else {
for ( i = 0 ; i < 4 ; i + + )
2015-10-02 15:18:41 +08:00
i2s_read_reg ( dev - > i2s_base , ROR ( i ) ) ;
2012-06-21 15:54:51 +05:30
}
}
2012-07-04 19:04:11 +01:00
static void i2s_start ( struct dw_i2s_dev * dev ,
struct snd_pcm_substream * substream )
2012-06-21 15:54:51 +05:30
{
2015-09-29 22:43:17 +08:00
u32 i , irq ;
2012-06-21 15:54:51 +05:30
i2s_write_reg ( dev - > i2s_base , IER , 1 ) ;
2015-09-29 22:43:17 +08:00
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
for ( i = 0 ; i < 4 ; i + + ) {
irq = i2s_read_reg ( dev - > i2s_base , IMR ( i ) ) ;
i2s_write_reg ( dev - > i2s_base , IMR ( i ) , irq & ~ 0x30 ) ;
}
2012-06-21 15:54:51 +05:30
i2s_write_reg ( dev - > i2s_base , ITER , 1 ) ;
2015-09-29 22:43:17 +08:00
} else {
for ( i = 0 ; i < 4 ; i + + ) {
irq = i2s_read_reg ( dev - > i2s_base , IMR ( i ) ) ;
i2s_write_reg ( dev - > i2s_base , IMR ( i ) , irq & ~ 0x03 ) ;
}
2012-06-21 15:54:51 +05:30
i2s_write_reg ( dev - > i2s_base , IRER , 1 ) ;
2015-09-29 22:43:17 +08:00
}
2012-06-21 15:54:51 +05:30
i2s_write_reg ( dev - > i2s_base , CER , 1 ) ;
}
static void i2s_stop ( struct dw_i2s_dev * dev ,
struct snd_pcm_substream * substream )
{
u32 i = 0 , irq ;
i2s_clear_irqs ( dev , substream - > stream ) ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
i2s_write_reg ( dev - > i2s_base , ITER , 0 ) ;
for ( i = 0 ; i < 4 ; i + + ) {
irq = i2s_read_reg ( dev - > i2s_base , IMR ( i ) ) ;
i2s_write_reg ( dev - > i2s_base , IMR ( i ) , irq | 0x30 ) ;
}
} else {
i2s_write_reg ( dev - > i2s_base , IRER , 0 ) ;
for ( i = 0 ; i < 4 ; i + + ) {
irq = i2s_read_reg ( dev - > i2s_base , IMR ( i ) ) ;
i2s_write_reg ( dev - > i2s_base , IMR ( i ) , irq | 0x03 ) ;
}
}
if ( ! dev - > active ) {
i2s_write_reg ( dev - > i2s_base , CER , 0 ) ;
i2s_write_reg ( dev - > i2s_base , IER , 0 ) ;
}
}
static int dw_i2s_startup ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * cpu_dai )
{
struct dw_i2s_dev * dev = snd_soc_dai_get_drvdata ( cpu_dai ) ;
2014-12-30 10:55:48 +00:00
union dw_i2s_snd_dma_data * dma_data = NULL ;
2012-06-21 15:54:51 +05:30
if ( ! ( dev - > capability & DWC_I2S_RECORD ) & &
( substream - > stream = = SNDRV_PCM_STREAM_CAPTURE ) )
return - EINVAL ;
if ( ! ( dev - > capability & DWC_I2S_PLAY ) & &
( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) )
return - EINVAL ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
dma_data = & dev - > play_dma_data ;
else if ( substream - > stream = = SNDRV_PCM_STREAM_CAPTURE )
dma_data = & dev - > capture_dma_data ;
snd_soc_dai_set_dma_data ( cpu_dai , substream , ( void * ) dma_data ) ;
return 0 ;
}
static int dw_i2s_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params , struct snd_soc_dai * dai )
{
struct dw_i2s_dev * dev = snd_soc_dai_get_drvdata ( dai ) ;
struct i2s_clk_config_data * config = & dev - > config ;
u32 ccr , xfer_resolution , ch_reg , irq ;
int ret ;
switch ( params_format ( params ) ) {
case SNDRV_PCM_FORMAT_S16_LE :
config - > data_width = 16 ;
ccr = 0x00 ;
xfer_resolution = 0x02 ;
break ;
case SNDRV_PCM_FORMAT_S24_LE :
config - > data_width = 24 ;
ccr = 0x08 ;
xfer_resolution = 0x04 ;
break ;
case SNDRV_PCM_FORMAT_S32_LE :
config - > data_width = 32 ;
ccr = 0x10 ;
xfer_resolution = 0x05 ;
break ;
default :
dev_err ( dev - > dev , " designware-i2s: unsuppted PCM fmt " ) ;
return - EINVAL ;
}
config - > chan_nr = params_channels ( params ) ;
switch ( config - > chan_nr ) {
case EIGHT_CHANNEL_SUPPORT :
case SIX_CHANNEL_SUPPORT :
case FOUR_CHANNEL_SUPPORT :
case TWO_CHANNEL_SUPPORT :
break ;
default :
dev_err ( dev - > dev , " channel not supported \n " ) ;
2013-01-25 09:43:43 +03:00
return - EINVAL ;
2012-06-21 15:54:51 +05:30
}
i2s_disable_channels ( dev , substream - > stream ) ;
2014-12-19 16:18:06 +00:00
for ( ch_reg = 0 ; ch_reg < ( config - > chan_nr / 2 ) ; ch_reg + + ) {
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
i2s_write_reg ( dev - > i2s_base , TCR ( ch_reg ) ,
xfer_resolution ) ;
i2s_write_reg ( dev - > i2s_base , TFCR ( ch_reg ) , 0x02 ) ;
irq = i2s_read_reg ( dev - > i2s_base , IMR ( ch_reg ) ) ;
i2s_write_reg ( dev - > i2s_base , IMR ( ch_reg ) , irq & ~ 0x30 ) ;
i2s_write_reg ( dev - > i2s_base , TER ( ch_reg ) , 1 ) ;
} else {
i2s_write_reg ( dev - > i2s_base , RCR ( ch_reg ) ,
xfer_resolution ) ;
i2s_write_reg ( dev - > i2s_base , RFCR ( ch_reg ) , 0x07 ) ;
irq = i2s_read_reg ( dev - > i2s_base , IMR ( ch_reg ) ) ;
i2s_write_reg ( dev - > i2s_base , IMR ( ch_reg ) , irq & ~ 0x03 ) ;
i2s_write_reg ( dev - > i2s_base , RER ( ch_reg ) , 1 ) ;
}
2012-06-21 15:54:51 +05:30
}
i2s_write_reg ( dev - > i2s_base , CCR , ccr ) ;
config - > sample_rate = params_rate ( params ) ;
2015-09-25 17:48:22 -04:00
if ( dev - > capability & DW_I2S_MASTER ) {
if ( dev - > i2s_clk_cfg ) {
ret = dev - > i2s_clk_cfg ( config ) ;
if ( ret < 0 ) {
dev_err ( dev - > dev , " runtime audio clk config fail \n " ) ;
return ret ;
}
} else {
u32 bitclk = config - > sample_rate *
config - > data_width * 2 ;
ret = clk_set_rate ( dev - > clk , bitclk ) ;
if ( ret ) {
dev_err ( dev - > dev , " Can't set I2S clock rate: %d \n " ,
ret ) ;
return ret ;
}
2014-12-30 10:55:48 +00:00
}
2012-06-21 15:54:51 +05:30
}
return 0 ;
}
static void dw_i2s_shutdown ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
snd_soc_dai_set_dma_data ( dai , substream , NULL ) ;
}
2014-12-19 16:18:05 +00:00
static int dw_i2s_prepare ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
struct dw_i2s_dev * dev = snd_soc_dai_get_drvdata ( dai ) ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
i2s_write_reg ( dev - > i2s_base , TXFFR , 1 ) ;
else
i2s_write_reg ( dev - > i2s_base , RXFFR , 1 ) ;
return 0 ;
}
2012-06-21 15:54:51 +05:30
static int dw_i2s_trigger ( struct snd_pcm_substream * substream ,
int cmd , struct snd_soc_dai * dai )
{
struct dw_i2s_dev * dev = snd_soc_dai_get_drvdata ( dai ) ;
int ret = 0 ;
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
case SNDRV_PCM_TRIGGER_RESUME :
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
dev - > active + + ;
i2s_start ( dev , substream ) ;
break ;
case SNDRV_PCM_TRIGGER_STOP :
case SNDRV_PCM_TRIGGER_SUSPEND :
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
dev - > active - - ;
i2s_stop ( dev , substream ) ;
break ;
default :
ret = - EINVAL ;
break ;
}
return ret ;
}
2015-10-23 17:15:41 -04:00
static int dw_i2s_set_fmt ( struct snd_soc_dai * cpu_dai , unsigned int fmt )
{
struct dw_i2s_dev * dev = snd_soc_dai_get_drvdata ( cpu_dai ) ;
int ret = 0 ;
switch ( fmt & SND_SOC_DAIFMT_MASTER_MASK ) {
case SND_SOC_DAIFMT_CBM_CFM :
if ( dev - > capability & DW_I2S_SLAVE )
ret = 0 ;
else
ret = - EINVAL ;
break ;
case SND_SOC_DAIFMT_CBS_CFS :
if ( dev - > capability & DW_I2S_MASTER )
ret = 0 ;
else
ret = - EINVAL ;
break ;
case SND_SOC_DAIFMT_CBM_CFS :
case SND_SOC_DAIFMT_CBS_CFM :
ret = - EINVAL ;
break ;
default :
dev_dbg ( dev - > dev , " dwc : Invalid master/slave format \n " ) ;
ret = - EINVAL ;
break ;
}
return ret ;
}
2012-06-21 15:54:51 +05:30
static struct snd_soc_dai_ops dw_i2s_dai_ops = {
. startup = dw_i2s_startup ,
. shutdown = dw_i2s_shutdown ,
. hw_params = dw_i2s_hw_params ,
2014-12-19 16:18:05 +00:00
. prepare = dw_i2s_prepare ,
2012-06-21 15:54:51 +05:30
. trigger = dw_i2s_trigger ,
2015-10-23 17:15:41 -04:00
. set_fmt = dw_i2s_set_fmt ,
2012-06-21 15:54:51 +05:30
} ;
2013-03-21 03:31:30 -07:00
static const struct snd_soc_component_driver dw_i2s_component = {
. name = " dw-i2s " ,
} ;
2012-06-21 15:54:51 +05:30
# ifdef CONFIG_PM
static int dw_i2s_suspend ( struct snd_soc_dai * dai )
{
struct dw_i2s_dev * dev = snd_soc_dai_get_drvdata ( dai ) ;
2015-09-25 17:48:22 -04:00
if ( dev - > capability & DW_I2S_MASTER )
clk_disable ( dev - > clk ) ;
2012-06-21 15:54:51 +05:30
return 0 ;
}
static int dw_i2s_resume ( struct snd_soc_dai * dai )
{
struct dw_i2s_dev * dev = snd_soc_dai_get_drvdata ( dai ) ;
2015-09-25 17:48:22 -04:00
if ( dev - > capability & DW_I2S_MASTER )
clk_enable ( dev - > clk ) ;
2012-06-21 15:54:51 +05:30
return 0 ;
}
# else
# define dw_i2s_suspend NULL
# define dw_i2s_resume NULL
# endif
2014-12-30 10:55:45 +00:00
/*
* The following tables allow a direct lookup of various parameters
* defined in the I2S block ' s configuration in terms of sound system
* parameters . Each table is sized to the number of entries possible
* according to the number of configuration bits describing an I2S
* block parameter .
*/
2014-12-30 10:55:48 +00:00
/* Maximum bit resolution of a channel - not uniformly spaced */
static const u32 fifo_width [ COMP_MAX_WORDSIZE ] = {
12 , 16 , 20 , 24 , 32 , 0 , 0 , 0
} ;
2014-12-30 10:55:45 +00:00
/* Width of (DMA) bus */
static const u32 bus_widths [ COMP_MAX_DATA_WIDTH ] = {
DMA_SLAVE_BUSWIDTH_1_BYTE ,
DMA_SLAVE_BUSWIDTH_2_BYTES ,
DMA_SLAVE_BUSWIDTH_4_BYTES ,
DMA_SLAVE_BUSWIDTH_UNDEFINED
} ;
/* PCM format to support channel resolution */
static const u32 formats [ COMP_MAX_WORDSIZE ] = {
SNDRV_PCM_FMTBIT_S16_LE ,
SNDRV_PCM_FMTBIT_S16_LE ,
SNDRV_PCM_FMTBIT_S24_LE ,
SNDRV_PCM_FMTBIT_S24_LE ,
SNDRV_PCM_FMTBIT_S32_LE ,
0 ,
0 ,
0
} ;
2014-12-30 10:55:48 +00:00
static int dw_configure_dai ( struct dw_i2s_dev * dev ,
2014-12-19 16:18:07 +00:00
struct snd_soc_dai_driver * dw_i2s_dai ,
2014-12-30 10:55:48 +00:00
unsigned int rates )
2014-12-19 16:18:07 +00:00
{
2014-12-30 10:55:45 +00:00
/*
* Read component parameter registers to extract
* the I2S block ' s configuration .
*/
u32 comp1 = i2s_read_reg ( dev - > i2s_base , I2S_COMP_PARAM_1 ) ;
u32 comp2 = i2s_read_reg ( dev - > i2s_base , I2S_COMP_PARAM_2 ) ;
2014-12-30 10:55:48 +00:00
u32 idx ;
2014-12-19 16:18:07 +00:00
2014-12-30 10:55:45 +00:00
if ( COMP1_TX_ENABLED ( comp1 ) ) {
2014-12-19 16:18:07 +00:00
dev_dbg ( dev - > dev , " designware: play supported \n " ) ;
2014-12-30 10:55:45 +00:00
idx = COMP1_TX_WORDSIZE_0 ( comp1 ) ;
if ( WARN_ON ( idx > = ARRAY_SIZE ( formats ) ) )
return - EINVAL ;
2014-12-19 16:18:07 +00:00
dw_i2s_dai - > playback . channels_min = MIN_CHANNEL_NUM ;
2014-12-30 10:55:45 +00:00
dw_i2s_dai - > playback . channels_max =
1 < < ( COMP1_TX_CHANNELS ( comp1 ) + 1 ) ;
dw_i2s_dai - > playback . formats = formats [ idx ] ;
2014-12-30 10:55:48 +00:00
dw_i2s_dai - > playback . rates = rates ;
2014-12-19 16:18:07 +00:00
}
2014-12-30 10:55:45 +00:00
if ( COMP1_RX_ENABLED ( comp1 ) ) {
2014-12-19 16:18:07 +00:00
dev_dbg ( dev - > dev , " designware: record supported \n " ) ;
2014-12-30 10:55:45 +00:00
idx = COMP2_RX_WORDSIZE_0 ( comp2 ) ;
if ( WARN_ON ( idx > = ARRAY_SIZE ( formats ) ) )
return - EINVAL ;
2014-12-19 16:18:07 +00:00
dw_i2s_dai - > capture . channels_min = MIN_CHANNEL_NUM ;
2014-12-30 10:55:45 +00:00
dw_i2s_dai - > capture . channels_max =
1 < < ( COMP1_RX_CHANNELS ( comp1 ) + 1 ) ;
dw_i2s_dai - > capture . formats = formats [ idx ] ;
2014-12-30 10:55:48 +00:00
dw_i2s_dai - > capture . rates = rates ;
}
2015-09-25 17:48:22 -04:00
if ( COMP1_MODE_EN ( comp1 ) ) {
dev_dbg ( dev - > dev , " designware: i2s master mode supported \n " ) ;
dev - > capability | = DW_I2S_MASTER ;
} else {
dev_dbg ( dev - > dev , " designware: i2s slave mode supported \n " ) ;
dev - > capability | = DW_I2S_SLAVE ;
}
2014-12-30 10:55:48 +00:00
return 0 ;
}
static int dw_configure_dai_by_pd ( struct dw_i2s_dev * dev ,
struct snd_soc_dai_driver * dw_i2s_dai ,
struct resource * res ,
const struct i2s_platform_data * pdata )
{
u32 comp1 = i2s_read_reg ( dev - > i2s_base , I2S_COMP_PARAM_1 ) ;
u32 idx = COMP1_APB_DATA_WIDTH ( comp1 ) ;
int ret ;
if ( WARN_ON ( idx > = ARRAY_SIZE ( bus_widths ) ) )
return - EINVAL ;
ret = dw_configure_dai ( dev , dw_i2s_dai , pdata - > snd_rates ) ;
if ( ret < 0 )
return ret ;
/* Set DMA slaves info */
dev - > play_dma_data . pd . data = pdata - > play_dma_data ;
dev - > capture_dma_data . pd . data = pdata - > capture_dma_data ;
dev - > play_dma_data . pd . addr = res - > start + I2S_TXDMA ;
dev - > capture_dma_data . pd . addr = res - > start + I2S_RXDMA ;
dev - > play_dma_data . pd . max_burst = 16 ;
dev - > capture_dma_data . pd . max_burst = 16 ;
dev - > play_dma_data . pd . addr_width = bus_widths [ idx ] ;
dev - > capture_dma_data . pd . addr_width = bus_widths [ idx ] ;
dev - > play_dma_data . pd . filter = pdata - > filter ;
dev - > capture_dma_data . pd . filter = pdata - > filter ;
return 0 ;
}
static int dw_configure_dai_by_dt ( struct dw_i2s_dev * dev ,
struct snd_soc_dai_driver * dw_i2s_dai ,
struct resource * res )
{
u32 comp1 = i2s_read_reg ( dev - > i2s_base , I2S_COMP_PARAM_1 ) ;
u32 comp2 = i2s_read_reg ( dev - > i2s_base , I2S_COMP_PARAM_2 ) ;
u32 fifo_depth = 1 < < ( 1 + COMP1_FIFO_DEPTH_GLOBAL ( comp1 ) ) ;
u32 idx = COMP1_APB_DATA_WIDTH ( comp1 ) ;
u32 idx2 ;
int ret ;
if ( WARN_ON ( idx > = ARRAY_SIZE ( bus_widths ) ) )
return - EINVAL ;
ret = dw_configure_dai ( dev , dw_i2s_dai , SNDRV_PCM_RATE_8000_192000 ) ;
if ( ret < 0 )
return ret ;
if ( COMP1_TX_ENABLED ( comp1 ) ) {
idx2 = COMP1_TX_WORDSIZE_0 ( comp1 ) ;
dev - > capability | = DWC_I2S_PLAY ;
dev - > play_dma_data . dt . addr = res - > start + I2S_TXDMA ;
dev - > play_dma_data . dt . addr_width = bus_widths [ idx ] ;
dev - > play_dma_data . dt . chan_name = " TX " ;
dev - > play_dma_data . dt . fifo_size = fifo_depth *
( fifo_width [ idx2 ] ) > > 8 ;
dev - > play_dma_data . dt . maxburst = 16 ;
}
if ( COMP1_RX_ENABLED ( comp1 ) ) {
idx2 = COMP2_RX_WORDSIZE_0 ( comp2 ) ;
dev - > capability | = DWC_I2S_RECORD ;
dev - > capture_dma_data . dt . addr = res - > start + I2S_RXDMA ;
dev - > capture_dma_data . dt . addr_width = bus_widths [ idx ] ;
dev - > capture_dma_data . dt . chan_name = " RX " ;
dev - > capture_dma_data . dt . fifo_size = fifo_depth *
( fifo_width [ idx2 ] > > 8 ) ;
dev - > capture_dma_data . dt . maxburst = 16 ;
2014-12-19 16:18:07 +00:00
}
2014-12-30 10:55:45 +00:00
return 0 ;
2014-12-30 10:55:48 +00:00
2014-12-19 16:18:07 +00:00
}
2012-06-21 15:54:51 +05:30
static int dw_i2s_probe ( struct platform_device * pdev )
{
const struct i2s_platform_data * pdata = pdev - > dev . platform_data ;
struct dw_i2s_dev * dev ;
struct resource * res ;
int ret ;
struct snd_soc_dai_driver * dw_i2s_dai ;
2015-09-25 17:48:22 -04:00
const char * clk_id ;
2012-06-21 15:54:51 +05:30
dev = devm_kzalloc ( & pdev - > dev , sizeof ( * dev ) , GFP_KERNEL ) ;
if ( ! dev ) {
dev_warn ( & pdev - > dev , " kzalloc fail \n " ) ;
return - ENOMEM ;
}
2014-12-03 16:38:46 +00:00
dw_i2s_dai = devm_kzalloc ( & pdev - > dev , sizeof ( * dw_i2s_dai ) , GFP_KERNEL ) ;
2014-12-12 09:25:00 +00:00
if ( ! dw_i2s_dai )
2012-06-21 15:54:51 +05:30
return - ENOMEM ;
2014-12-03 16:38:46 +00:00
dw_i2s_dai - > ops = & dw_i2s_dai_ops ;
dw_i2s_dai - > suspend = dw_i2s_suspend ;
dw_i2s_dai - > resume = dw_i2s_resume ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
dev - > i2s_base = devm_ioremap_resource ( & pdev - > dev , res ) ;
2014-12-12 09:25:00 +00:00
if ( IS_ERR ( dev - > i2s_base ) )
2014-12-03 16:38:46 +00:00
return PTR_ERR ( dev - > i2s_base ) ;
2014-12-19 16:18:07 +00:00
dev - > dev = & pdev - > dev ;
2015-09-25 17:48:22 -04:00
2014-12-30 10:55:48 +00:00
if ( pdata ) {
2015-09-25 17:48:22 -04:00
dev - > capability = pdata - > cap ;
clk_id = NULL ;
2014-12-30 10:55:48 +00:00
ret = dw_configure_dai_by_pd ( dev , dw_i2s_dai , res , pdata ) ;
2015-09-25 17:48:22 -04:00
} else {
clk_id = " i2sclk " ;
ret = dw_configure_dai_by_dt ( dev , dw_i2s_dai , res ) ;
}
if ( ret < 0 )
return ret ;
2014-12-30 10:55:48 +00:00
2015-09-25 17:48:22 -04:00
if ( dev - > capability & DW_I2S_MASTER ) {
if ( pdata ) {
dev - > i2s_clk_cfg = pdata - > i2s_clk_cfg ;
if ( ! dev - > i2s_clk_cfg ) {
dev_err ( & pdev - > dev , " no clock configure method \n " ) ;
return - ENODEV ;
}
2014-12-30 10:55:48 +00:00
}
2015-09-25 17:48:22 -04:00
dev - > clk = devm_clk_get ( & pdev - > dev , clk_id ) ;
2012-06-21 15:54:51 +05:30
2015-09-25 17:48:22 -04:00
if ( IS_ERR ( dev - > clk ) )
return PTR_ERR ( dev - > clk ) ;
ret = clk_prepare_enable ( dev - > clk ) ;
2014-12-30 10:55:48 +00:00
if ( ret < 0 )
return ret ;
}
2012-06-21 15:54:51 +05:30
dev_set_drvdata ( & pdev - > dev , dev ) ;
2014-12-30 10:55:46 +00:00
ret = devm_snd_soc_register_component ( & pdev - > dev , & dw_i2s_component ,
2013-03-21 03:31:30 -07:00
dw_i2s_dai , 1 ) ;
2012-06-21 15:54:51 +05:30
if ( ret ! = 0 ) {
dev_err ( & pdev - > dev , " not able to register dai \n " ) ;
2013-08-26 09:25:15 -03:00
goto err_clk_disable ;
2012-06-21 15:54:51 +05:30
}
2014-12-30 10:55:48 +00:00
if ( ! pdata ) {
ret = devm_snd_dmaengine_pcm_register ( & pdev - > dev , NULL , 0 ) ;
if ( ret ) {
dev_err ( & pdev - > dev ,
" Could not register PCM: %d \n " , ret ) ;
goto err_clk_disable ;
}
}
2012-06-21 15:54:51 +05:30
return 0 ;
err_clk_disable :
2015-09-25 17:48:22 -04:00
if ( dev - > capability & DW_I2S_MASTER )
clk_disable_unprepare ( dev - > clk ) ;
2012-06-21 15:54:51 +05:30
return ret ;
}
static int dw_i2s_remove ( struct platform_device * pdev )
{
struct dw_i2s_dev * dev = dev_get_drvdata ( & pdev - > dev ) ;
2015-09-25 17:48:22 -04:00
if ( dev - > capability & DW_I2S_MASTER )
clk_disable_unprepare ( dev - > clk ) ;
2012-06-21 15:54:51 +05:30
return 0 ;
}
2014-12-30 10:55:48 +00:00
# ifdef CONFIG_OF
static const struct of_device_id dw_i2s_of_match [ ] = {
{ . compatible = " snps,designware-i2s " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , dw_i2s_of_match ) ;
# endif
2012-06-21 15:54:51 +05:30
static struct platform_driver dw_i2s_driver = {
. probe = dw_i2s_probe ,
. remove = dw_i2s_remove ,
. driver = {
. name = " designware-i2s " ,
2014-12-30 10:55:48 +00:00
. of_match_table = of_match_ptr ( dw_i2s_of_match ) ,
2012-06-21 15:54:51 +05:30
} ,
} ;
module_platform_driver ( dw_i2s_driver ) ;
2014-09-09 12:27:19 +05:30
MODULE_AUTHOR ( " Rajeev Kumar <rajeevkumar.linux@gmail.com> " ) ;
2012-06-21 15:54:51 +05:30
MODULE_DESCRIPTION ( " DESIGNWARE I2S SoC Interface " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:designware_i2s " ) ;