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>
2015-12-04 18:40:31 -05:00
# include <linux/pm_runtime.h>
2012-06-21 15:54:51 +05:30
# 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>
2016-06-09 12:47:05 +01:00
# include "local.h"
2012-06-21 15:54:51 +05:30
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
}
}
2016-05-23 11:02:22 +01:00
static inline void i2s_disable_irqs ( struct dw_i2s_dev * dev , u32 stream ,
int chan_nr )
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
2016-05-23 11:02:22 +01:00
if ( stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
for ( i = 0 ; i < ( chan_nr / 2 ) ; i + + ) {
irq = i2s_read_reg ( dev - > i2s_base , IMR ( i ) ) ;
i2s_write_reg ( dev - > i2s_base , IMR ( i ) , irq | 0x30 ) ;
}
} else {
for ( i = 0 ; i < ( chan_nr / 2 ) ; i + + ) {
irq = i2s_read_reg ( dev - > i2s_base , IMR ( i ) ) ;
i2s_write_reg ( dev - > i2s_base , IMR ( i ) , irq | 0x03 ) ;
}
}
}
static inline void i2s_enable_irqs ( struct dw_i2s_dev * dev , u32 stream ,
int chan_nr )
{
u32 i , irq ;
if ( stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
for ( i = 0 ; i < ( chan_nr / 2 ) ; i + + ) {
2015-09-29 22:43:17 +08:00
irq = i2s_read_reg ( dev - > i2s_base , IMR ( i ) ) ;
i2s_write_reg ( dev - > i2s_base , IMR ( i ) , irq & ~ 0x30 ) ;
}
} else {
2016-05-23 11:02:22 +01:00
for ( i = 0 ; i < ( chan_nr / 2 ) ; i + + ) {
2015-09-29 22:43:17 +08:00
irq = i2s_read_reg ( dev - > i2s_base , IMR ( i ) ) ;
i2s_write_reg ( dev - > i2s_base , IMR ( i ) , irq & ~ 0x03 ) ;
}
}
2016-05-23 11:02:22 +01:00
}
2016-06-09 12:47:05 +01:00
static irqreturn_t i2s_irq_handler ( int irq , void * dev_id )
{
struct dw_i2s_dev * dev = dev_id ;
bool irq_valid = false ;
u32 isr [ 4 ] ;
int i ;
for ( i = 0 ; i < 4 ; i + + )
isr [ i ] = i2s_read_reg ( dev - > i2s_base , ISR ( i ) ) ;
i2s_clear_irqs ( dev , SNDRV_PCM_STREAM_PLAYBACK ) ;
i2s_clear_irqs ( dev , SNDRV_PCM_STREAM_CAPTURE ) ;
for ( i = 0 ; i < 4 ; i + + ) {
/*
* Check if TX fifo is empty . If empty fill FIFO with samples
* NOTE : Only two channels supported
*/
if ( ( isr [ i ] & ISR_TXFE ) & & ( i = = 0 ) & & dev - > use_pio ) {
dw_pcm_push_tx ( dev ) ;
irq_valid = true ;
}
2016-12-27 14:00:53 +00:00
/*
* Data available . Retrieve samples from FIFO
* NOTE : Only two channels supported
*/
if ( ( isr [ i ] & ISR_RXDA ) & & ( i = = 0 ) & & dev - > use_pio ) {
dw_pcm_pop_rx ( dev ) ;
2016-06-09 12:47:05 +01:00
irq_valid = true ;
2016-12-27 14:00:53 +00:00
}
2016-06-09 12:47:05 +01:00
/* Error Handling: TX */
if ( isr [ i ] & ISR_TXFO ) {
dev_err ( dev - > dev , " TX overrun (ch_id=%d) \n " , i ) ;
irq_valid = true ;
}
/* Error Handling: TX */
if ( isr [ i ] & ISR_RXFO ) {
dev_err ( dev - > dev , " RX overrun (ch_id=%d) \n " , i ) ;
irq_valid = true ;
}
}
if ( irq_valid )
return IRQ_HANDLED ;
else
return IRQ_NONE ;
}
2016-05-23 11:02:22 +01:00
static void i2s_start ( struct dw_i2s_dev * dev ,
struct snd_pcm_substream * substream )
{
struct i2s_clk_config_data * config = & dev - > config ;
i2s_write_reg ( dev - > i2s_base , IER , 1 ) ;
i2s_enable_irqs ( dev , substream - > stream , config - > chan_nr ) ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
i2s_write_reg ( dev - > i2s_base , ITER , 1 ) ;
else
i2s_write_reg ( dev - > i2s_base , IRER , 1 ) ;
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 )
{
i2s_clear_irqs ( dev , substream - > stream ) ;
2016-05-23 11:02:22 +01:00
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
2012-06-21 15:54:51 +05:30
i2s_write_reg ( dev - > i2s_base , ITER , 0 ) ;
2016-05-23 11:02:22 +01:00
else
2012-06-21 15:54:51 +05:30
i2s_write_reg ( dev - > i2s_base , IRER , 0 ) ;
2016-05-23 11:02:22 +01:00
i2s_disable_irqs ( dev , substream - > stream , 8 ) ;
2012-06-21 15:54:51 +05:30
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 ;
}
2015-12-04 18:40:33 -05:00
static void dw_i2s_config ( struct dw_i2s_dev * dev , int stream )
{
2016-05-23 11:02:22 +01:00
u32 ch_reg ;
2015-12-04 18:40:33 -05:00
struct i2s_clk_config_data * config = & dev - > config ;
i2s_disable_channels ( dev , stream ) ;
for ( ch_reg = 0 ; ch_reg < ( config - > chan_nr / 2 ) ; ch_reg + + ) {
if ( stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
i2s_write_reg ( dev - > i2s_base , TCR ( ch_reg ) ,
dev - > xfer_resolution ) ;
2016-04-07 17:53:57 +01:00
i2s_write_reg ( dev - > i2s_base , TFCR ( ch_reg ) ,
dev - > fifo_th - 1 ) ;
2015-12-04 18:40:33 -05:00
i2s_write_reg ( dev - > i2s_base , TER ( ch_reg ) , 1 ) ;
} else {
i2s_write_reg ( dev - > i2s_base , RCR ( ch_reg ) ,
dev - > xfer_resolution ) ;
2016-04-07 17:53:57 +01:00
i2s_write_reg ( dev - > i2s_base , RFCR ( ch_reg ) ,
dev - > fifo_th - 1 ) ;
2015-12-04 18:40:33 -05:00
i2s_write_reg ( dev - > i2s_base , RER ( ch_reg ) , 1 ) ;
}
}
}
2012-06-21 15:54:51 +05:30
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 ;
int ret ;
switch ( params_format ( params ) ) {
case SNDRV_PCM_FORMAT_S16_LE :
config - > data_width = 16 ;
2015-12-04 18:40:33 -05:00
dev - > ccr = 0x00 ;
dev - > xfer_resolution = 0x02 ;
2012-06-21 15:54:51 +05:30
break ;
case SNDRV_PCM_FORMAT_S24_LE :
config - > data_width = 24 ;
2015-12-04 18:40:33 -05:00
dev - > ccr = 0x08 ;
dev - > xfer_resolution = 0x04 ;
2012-06-21 15:54:51 +05:30
break ;
case SNDRV_PCM_FORMAT_S32_LE :
config - > data_width = 32 ;
2015-12-04 18:40:33 -05:00
dev - > ccr = 0x10 ;
dev - > xfer_resolution = 0x05 ;
2012-06-21 15:54:51 +05:30
break ;
default :
2016-06-24 18:14:53 +01:00
dev_err ( dev - > dev , " designware-i2s: unsupported PCM fmt " ) ;
2012-06-21 15:54:51 +05:30
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
}
2015-12-04 18:40:33 -05:00
dw_i2s_config ( dev , substream - > stream ) ;
2012-06-21 15:54:51 +05:30
2015-12-04 18:40:33 -05:00
i2s_write_reg ( dev - > i2s_base , CCR , dev - > ccr ) ;
2012-06-21 15:54:51 +05:30
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 ;
2021-09-21 21:53:13 +01:00
switch ( fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK ) {
2022-05-19 16:42:29 +01:00
case SND_SOC_DAIFMT_BC_FC :
2015-10-23 17:15:41 -04:00
if ( dev - > capability & DW_I2S_SLAVE )
ret = 0 ;
else
ret = - EINVAL ;
break ;
2022-05-19 16:42:29 +01:00
case SND_SOC_DAIFMT_BP_FP :
2015-10-23 17:15:41 -04:00
if ( dev - > capability & DW_I2S_MASTER )
ret = 0 ;
else
ret = - EINVAL ;
break ;
2022-05-19 16:42:29 +01:00
case SND_SOC_DAIFMT_BC_FP :
case SND_SOC_DAIFMT_BP_FC :
2015-10-23 17:15:41 -04:00
ret = - EINVAL ;
break ;
default :
2021-09-21 21:53:13 +01:00
dev_dbg ( dev - > dev , " dwc : Invalid clock provider format \n " ) ;
2015-10-23 17:15:41 -04:00
ret = - EINVAL ;
break ;
}
return ret ;
}
2017-07-13 15:38:43 -05:00
static const struct snd_soc_dai_ops dw_i2s_dai_ops = {
2012-06-21 15:54:51 +05:30
. 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 ,
2022-05-19 16:42:56 +01:00
. set_fmt = dw_i2s_set_fmt ,
2012-06-21 15:54:51 +05:30
} ;
# ifdef CONFIG_PM
2015-12-04 18:40:31 -05:00
static int dw_i2s_runtime_suspend ( struct device * dev )
{
struct dw_i2s_dev * dw_dev = dev_get_drvdata ( dev ) ;
if ( dw_dev - > capability & DW_I2S_MASTER )
clk_disable ( dw_dev - > clk ) ;
return 0 ;
}
static int dw_i2s_runtime_resume ( struct device * dev )
{
struct dw_i2s_dev * dw_dev = dev_get_drvdata ( dev ) ;
2022-03-01 16:47:42 +08:00
int ret ;
2015-12-04 18:40:31 -05:00
2022-03-01 16:47:42 +08:00
if ( dw_dev - > capability & DW_I2S_MASTER ) {
ret = clk_enable ( dw_dev - > clk ) ;
if ( ret )
return ret ;
}
2015-12-04 18:40:31 -05:00
return 0 ;
}
2012-06-21 15:54:51 +05:30
2020-01-20 10:04:37 +09:00
static int dw_i2s_suspend ( struct snd_soc_component * component )
2012-06-21 15:54:51 +05:30
{
2020-01-20 10:04:37 +09:00
struct dw_i2s_dev * dev = snd_soc_component_get_drvdata ( component ) ;
2012-06-21 15:54:51 +05:30
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 ;
}
2020-01-20 10:04:37 +09:00
static int dw_i2s_resume ( struct snd_soc_component * component )
2012-06-21 15:54:51 +05:30
{
2020-01-20 10:04:37 +09:00
struct dw_i2s_dev * dev = snd_soc_component_get_drvdata ( component ) ;
struct snd_soc_dai * dai ;
2022-03-01 16:47:42 +08:00
int stream , ret ;
2012-06-21 15:54:51 +05:30
2022-03-01 16:47:42 +08:00
if ( dev - > capability & DW_I2S_MASTER ) {
ret = clk_enable ( dev - > clk ) ;
if ( ret )
return ret ;
}
2015-12-04 18:40:33 -05:00
2020-01-20 10:04:37 +09:00
for_each_component_dais ( component , dai ) {
2020-02-17 17:28:36 +09:00
for_each_pcm_streams ( stream )
2020-05-15 09:47:57 +09:00
if ( snd_soc_dai_stream_active ( dai , stream ) )
2020-02-17 17:28:36 +09:00
dw_i2s_config ( dev , stream ) ;
2020-01-20 10:04:37 +09:00
}
2012-06-21 15:54:51 +05:30
return 0 ;
}
# else
# define dw_i2s_suspend NULL
# define dw_i2s_resume NULL
# endif
2020-01-20 10:04:37 +09:00
static const struct snd_soc_component_driver dw_i2s_component = {
2022-06-23 13:51:37 +01:00
. name = " dw-i2s " ,
. suspend = dw_i2s_suspend ,
. resume = dw_i2s_resume ,
. legacy_dai_naming = 1 ,
2020-01-20 10:04:37 +09:00
} ;
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 .
*/
2015-12-04 18:40:32 -05:00
u32 comp1 = i2s_read_reg ( dev - > i2s_base , dev - > i2s_reg_comp1 ) ;
u32 comp2 = i2s_read_reg ( dev - > i2s_base , dev - > i2s_reg_comp2 ) ;
2016-04-07 17:53:57 +01:00
u32 fifo_depth = 1 < < ( 1 + COMP1_FIFO_DEPTH_GLOBAL ( comp1 ) ) ;
2014-12-30 10:55:48 +00:00
u32 idx ;
2014-12-19 16:18:07 +00:00
2016-01-08 18:22:05 -05:00
if ( dev - > capability & DWC_I2S_RECORD & &
dev - > quirks & DW_I2S_QUIRK_COMP_PARAM1 )
comp1 = comp1 & ~ BIT ( 5 ) ;
2018-02-16 13:11:15 +05:30
if ( dev - > capability & DWC_I2S_PLAY & &
dev - > quirks & DW_I2S_QUIRK_COMP_PARAM1 )
comp1 = comp1 & ~ BIT ( 6 ) ;
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 ;
2017-06-23 12:35:00 -04:00
if ( dev - > quirks & DW_I2S_QUIRK_16BIT_IDX_OVERRIDE )
idx = 1 ;
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 ;
2017-06-23 12:35:00 -04:00
if ( dev - > quirks & DW_I2S_QUIRK_16BIT_IDX_OVERRIDE )
idx = 1 ;
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 ;
}
2016-04-07 17:53:57 +01:00
dev - > fifo_th = fifo_depth / 2 ;
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 )
{
2015-12-04 18:40:32 -05:00
u32 comp1 = i2s_read_reg ( dev - > i2s_base , dev - > i2s_reg_comp1 ) ;
2014-12-30 10:55:48 +00:00
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 ;
2017-06-23 12:35:00 -04:00
if ( dev - > quirks & DW_I2S_QUIRK_16BIT_IDX_OVERRIDE )
idx = 1 ;
2014-12-30 10:55:48 +00:00
/* 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 . 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 . 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 ;
2016-06-09 12:47:05 +01:00
int ret , irq ;
2012-06-21 15:54:51 +05:30
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 ) ;
2017-08-11 16:04:53 +02:00
if ( ! dev )
2012-06-21 15:54:51 +05:30
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 ;
2021-06-15 21:29:33 +08:00
dev - > i2s_base = devm_platform_get_and_ioremap_resource ( pdev , 0 , & 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
2021-08-25 16:42:03 +01:00
irq = platform_get_irq_optional ( pdev , 0 ) ;
2016-06-09 12:47:05 +01:00
if ( irq > = 0 ) {
ret = devm_request_irq ( & pdev - > dev , irq , i2s_irq_handler , 0 ,
pdev - > name , dev ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " failed to request irq \n " ) ;
return ret ;
}
}
2016-02-01 15:54:37 +00:00
dev - > i2s_reg_comp1 = I2S_COMP_PARAM_1 ;
dev - > i2s_reg_comp2 = I2S_COMP_PARAM_2 ;
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 ;
2015-12-04 18:40:32 -05:00
dev - > quirks = pdata - > quirks ;
if ( dev - > quirks & DW_I2S_QUIRK_COMP_REG_OFFSET ) {
dev - > i2s_reg_comp1 = pdata - > i2s_reg_comp1 ;
dev - > i2s_reg_comp2 = pdata - > i2s_reg_comp2 ;
}
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 ) {
2016-12-13 11:03:49 +00:00
if ( irq > = 0 ) {
2016-06-09 12:47:05 +01:00
ret = dw_pcm_register ( pdev ) ;
2016-12-13 11:03:49 +00:00
dev - > use_pio = true ;
} else {
ret = devm_snd_dmaengine_pcm_register ( & pdev - > dev , NULL ,
0 ) ;
dev - > use_pio = false ;
}
if ( ret ) {
dev_err ( & pdev - > dev , " could not register pcm: %d \n " ,
2016-06-09 12:47:05 +01:00
ret ) ;
2016-12-13 11:03:49 +00:00
goto err_clk_disable ;
2014-12-30 10:55:48 +00:00
}
}
2016-06-09 12:47:05 +01:00
2015-12-04 18:40:31 -05:00
pm_runtime_enable ( & pdev - > dev ) ;
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
2015-12-04 18:40:31 -05:00
pm_runtime_disable ( & pdev - > dev ) ;
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
2015-12-04 18:40:31 -05:00
static const struct dev_pm_ops dwc_pm_ops = {
SET_RUNTIME_PM_OPS ( dw_i2s_runtime_suspend , dw_i2s_runtime_resume , NULL )
} ;
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 ) ,
2015-12-04 18:40:31 -05:00
. pm = & dwc_pm_ops ,
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 " ) ;