2019-06-04 10:11:33 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2014-07-05 19:13:03 +08:00
/* sound/soc/rockchip/rockchip_i2s.c
*
* ALSA SoC Audio Layer - Rockchip I2S Controller driver
*
* Copyright ( c ) 2014 Rockchip Electronics Co . Ltd .
* Author : Jianqun < jay . xu @ rock - chips . com >
*/
2014-07-11 19:40:05 +08:00
# include <linux/module.h>
2016-04-11 17:26:03 +08:00
# include <linux/mfd/syscon.h>
2014-07-05 19:13:03 +08:00
# include <linux/delay.h>
# include <linux/of_gpio.h>
2016-04-11 17:26:03 +08:00
# include <linux/of_device.h>
2014-07-05 19:13:03 +08:00
# include <linux/clk.h>
# include <linux/pm_runtime.h>
# include <linux/regmap.h>
# include <sound/pcm_params.h>
# include <sound/dmaengine_pcm.h>
# include "rockchip_i2s.h"
2018-06-08 16:31:09 +08:00
# include "rockchip_pcm.h"
2014-07-05 19:13:03 +08:00
# define DRV_NAME "rockchip-i2s"
2016-04-11 17:26:03 +08:00
struct rk_i2s_pins {
u32 reg_offset ;
u32 shift ;
} ;
2014-07-05 19:13:03 +08:00
struct rk_i2s_dev {
struct device * dev ;
struct clk * hclk ;
struct clk * mclk ;
struct snd_dmaengine_dai_dma_data capture_dma_data ;
struct snd_dmaengine_dai_dma_data playback_dma_data ;
struct regmap * regmap ;
2016-04-11 17:26:03 +08:00
struct regmap * grf ;
2014-07-05 19:13:03 +08:00
2016-05-04 17:21:56 +01:00
/*
* Used to indicate the tx / rx status .
* I2S controller hopes to start the tx and rx together ,
* also to stop them when they are both try to stop .
*/
bool tx_start ;
bool rx_start ;
2015-11-06 19:38:14 +08:00
bool is_master_mode ;
2016-04-11 17:26:03 +08:00
const struct rk_i2s_pins * pins ;
2014-07-05 19:13:03 +08:00
} ;
static int i2s_runtime_suspend ( struct device * dev )
{
struct rk_i2s_dev * i2s = dev_get_drvdata ( dev ) ;
2016-09-07 14:27:33 +08:00
regcache_cache_only ( i2s - > regmap , true ) ;
2014-07-05 19:13:03 +08:00
clk_disable_unprepare ( i2s - > mclk ) ;
return 0 ;
}
static int i2s_runtime_resume ( struct device * dev )
{
struct rk_i2s_dev * i2s = dev_get_drvdata ( dev ) ;
int ret ;
ret = clk_prepare_enable ( i2s - > mclk ) ;
if ( ret ) {
dev_err ( i2s - > dev , " clock enable failed %d \n " , ret ) ;
return ret ;
}
2016-09-07 14:27:33 +08:00
regcache_cache_only ( i2s - > regmap , false ) ;
regcache_mark_dirty ( i2s - > regmap ) ;
ret = regcache_sync ( i2s - > regmap ) ;
if ( ret )
clk_disable_unprepare ( i2s - > mclk ) ;
return ret ;
2014-07-05 19:13:03 +08:00
}
static inline struct rk_i2s_dev * to_info ( struct snd_soc_dai * dai )
{
return snd_soc_dai_get_drvdata ( dai ) ;
}
static void rockchip_snd_txctrl ( struct rk_i2s_dev * i2s , int on )
{
unsigned int val = 0 ;
int retry = 10 ;
if ( on ) {
regmap_update_bits ( i2s - > regmap , I2S_DMACR ,
I2S_DMACR_TDE_ENABLE , I2S_DMACR_TDE_ENABLE ) ;
regmap_update_bits ( i2s - > regmap , I2S_XFER ,
2016-05-04 17:21:57 +01:00
I2S_XFER_TXS_START | I2S_XFER_RXS_START ,
I2S_XFER_TXS_START | I2S_XFER_RXS_START ) ;
2016-05-04 17:21:56 +01:00
i2s - > tx_start = true ;
2014-07-05 19:13:03 +08:00
} else {
2016-05-04 17:21:56 +01:00
i2s - > tx_start = false ;
2014-07-05 19:13:03 +08:00
regmap_update_bits ( i2s - > regmap , I2S_DMACR ,
2014-07-12 09:02:13 +08:00
I2S_DMACR_TDE_ENABLE , I2S_DMACR_TDE_DISABLE ) ;
2014-07-05 19:13:03 +08:00
2016-05-04 17:21:57 +01:00
if ( ! i2s - > rx_start ) {
regmap_update_bits ( i2s - > regmap , I2S_XFER ,
I2S_XFER_TXS_START |
I2S_XFER_RXS_START ,
I2S_XFER_TXS_STOP |
I2S_XFER_RXS_STOP ) ;
2014-07-05 19:13:03 +08:00
2017-06-09 16:52:46 +08:00
udelay ( 150 ) ;
2016-05-04 17:21:57 +01:00
regmap_update_bits ( i2s - > regmap , I2S_CLR ,
I2S_CLR_TXC | I2S_CLR_RXC ,
I2S_CLR_TXC | I2S_CLR_RXC ) ;
2014-07-05 19:13:03 +08:00
2015-12-09 10:32:26 +00:00
regmap_read ( i2s - > regmap , I2S_CLR , & val ) ;
2016-05-04 17:21:57 +01:00
/* Should wait for clear operation to finish */
while ( val ) {
regmap_read ( i2s - > regmap , I2S_CLR , & val ) ;
retry - - ;
if ( ! retry ) {
dev_warn ( i2s - > dev , " fail to clear \n " ) ;
break ;
}
2014-07-05 19:13:03 +08:00
}
}
}
}
static void rockchip_snd_rxctrl ( struct rk_i2s_dev * i2s , int on )
{
unsigned int val = 0 ;
int retry = 10 ;
if ( on ) {
regmap_update_bits ( i2s - > regmap , I2S_DMACR ,
I2S_DMACR_RDE_ENABLE , I2S_DMACR_RDE_ENABLE ) ;
regmap_update_bits ( i2s - > regmap , I2S_XFER ,
2016-05-04 17:21:57 +01:00
I2S_XFER_TXS_START | I2S_XFER_RXS_START ,
I2S_XFER_TXS_START | I2S_XFER_RXS_START ) ;
2016-05-04 17:21:56 +01:00
i2s - > rx_start = true ;
2014-07-05 19:13:03 +08:00
} else {
2016-05-04 17:21:56 +01:00
i2s - > rx_start = false ;
2014-07-05 19:13:03 +08:00
regmap_update_bits ( i2s - > regmap , I2S_DMACR ,
I2S_DMACR_RDE_ENABLE , I2S_DMACR_RDE_DISABLE ) ;
2016-05-04 17:21:57 +01:00
if ( ! i2s - > tx_start ) {
regmap_update_bits ( i2s - > regmap , I2S_XFER ,
I2S_XFER_TXS_START |
I2S_XFER_RXS_START ,
I2S_XFER_TXS_STOP |
I2S_XFER_RXS_STOP ) ;
2014-07-05 19:13:03 +08:00
2017-06-09 16:52:46 +08:00
udelay ( 150 ) ;
2016-05-04 17:21:57 +01:00
regmap_update_bits ( i2s - > regmap , I2S_CLR ,
I2S_CLR_TXC | I2S_CLR_RXC ,
I2S_CLR_TXC | I2S_CLR_RXC ) ;
2014-07-05 19:13:03 +08:00
2015-12-09 10:32:26 +00:00
regmap_read ( i2s - > regmap , I2S_CLR , & val ) ;
2016-05-04 17:21:57 +01:00
/* Should wait for clear operation to finish */
while ( val ) {
regmap_read ( i2s - > regmap , I2S_CLR , & val ) ;
retry - - ;
if ( ! retry ) {
dev_warn ( i2s - > dev , " fail to clear \n " ) ;
break ;
}
2014-07-05 19:13:03 +08:00
}
}
}
}
static int rockchip_i2s_set_fmt ( struct snd_soc_dai * cpu_dai ,
unsigned int fmt )
{
struct rk_i2s_dev * i2s = to_info ( cpu_dai ) ;
unsigned int mask = 0 , val = 0 ;
2014-09-13 08:41:03 +08:00
mask = I2S_CKR_MSS_MASK ;
2014-07-05 19:13:03 +08:00
switch ( fmt & SND_SOC_DAIFMT_MASTER_MASK ) {
case SND_SOC_DAIFMT_CBS_CFS :
2014-09-13 08:41:03 +08:00
/* Set source clock in Master mode */
val = I2S_CKR_MSS_MASTER ;
2015-11-06 19:38:14 +08:00
i2s - > is_master_mode = true ;
2014-07-05 19:13:03 +08:00
break ;
case SND_SOC_DAIFMT_CBM_CFM :
2014-09-13 08:41:03 +08:00
val = I2S_CKR_MSS_SLAVE ;
2015-11-06 19:38:14 +08:00
i2s - > is_master_mode = false ;
2014-07-05 19:13:03 +08:00
break ;
default :
return - EINVAL ;
}
regmap_update_bits ( i2s - > regmap , I2S_CKR , mask , val ) ;
2017-06-09 16:52:48 +08:00
mask = I2S_CKR_CKP_MASK ;
switch ( fmt & SND_SOC_DAIFMT_INV_MASK ) {
case SND_SOC_DAIFMT_NB_NF :
val = I2S_CKR_CKP_NEG ;
break ;
case SND_SOC_DAIFMT_IB_NF :
val = I2S_CKR_CKP_POS ;
break ;
default :
return - EINVAL ;
}
regmap_update_bits ( i2s - > regmap , I2S_CKR , mask , val ) ;
mask = I2S_TXCR_IBM_MASK | I2S_TXCR_TFS_MASK | I2S_TXCR_PBM_MASK ;
2014-07-05 19:13:03 +08:00
switch ( fmt & SND_SOC_DAIFMT_FORMAT_MASK ) {
case SND_SOC_DAIFMT_RIGHT_J :
val = I2S_TXCR_IBM_RSJM ;
break ;
case SND_SOC_DAIFMT_LEFT_J :
val = I2S_TXCR_IBM_LSJM ;
break ;
case SND_SOC_DAIFMT_I2S :
val = I2S_TXCR_IBM_NORMAL ;
break ;
2017-06-09 16:52:48 +08:00
case SND_SOC_DAIFMT_DSP_A : /* PCM no delay mode */
val = I2S_TXCR_TFS_PCM ;
break ;
case SND_SOC_DAIFMT_DSP_B : /* PCM delay 1 mode */
val = I2S_TXCR_TFS_PCM | I2S_TXCR_PBM_MODE ( 1 ) ;
break ;
2014-07-05 19:13:03 +08:00
default :
return - EINVAL ;
}
regmap_update_bits ( i2s - > regmap , I2S_TXCR , mask , val ) ;
2017-06-09 16:52:48 +08:00
mask = I2S_RXCR_IBM_MASK | I2S_RXCR_TFS_MASK | I2S_RXCR_PBM_MASK ;
2014-07-05 19:13:03 +08:00
switch ( fmt & SND_SOC_DAIFMT_FORMAT_MASK ) {
case SND_SOC_DAIFMT_RIGHT_J :
val = I2S_RXCR_IBM_RSJM ;
break ;
case SND_SOC_DAIFMT_LEFT_J :
val = I2S_RXCR_IBM_LSJM ;
break ;
case SND_SOC_DAIFMT_I2S :
val = I2S_RXCR_IBM_NORMAL ;
break ;
2017-06-09 16:52:48 +08:00
case SND_SOC_DAIFMT_DSP_A : /* PCM no delay mode */
val = I2S_RXCR_TFS_PCM ;
break ;
case SND_SOC_DAIFMT_DSP_B : /* PCM delay 1 mode */
val = I2S_RXCR_TFS_PCM | I2S_RXCR_PBM_MODE ( 1 ) ;
break ;
2014-07-05 19:13:03 +08:00
default :
return - EINVAL ;
}
regmap_update_bits ( i2s - > regmap , I2S_RXCR , mask , val ) ;
return 0 ;
}
static int rockchip_i2s_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params ,
struct snd_soc_dai * dai )
{
struct rk_i2s_dev * i2s = to_info ( dai ) ;
2015-10-08 20:40:09 +08:00
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
2014-07-05 19:13:03 +08:00
unsigned int val = 0 ;
2015-11-06 19:38:14 +08:00
unsigned int mclk_rate , bclk_rate , div_bclk , div_lrck ;
if ( i2s - > is_master_mode ) {
mclk_rate = clk_get_rate ( i2s - > mclk ) ;
bclk_rate = 2 * 32 * params_rate ( params ) ;
if ( bclk_rate & & mclk_rate % bclk_rate )
return - EINVAL ;
div_bclk = mclk_rate / bclk_rate ;
div_lrck = bclk_rate / params_rate ( params ) ;
regmap_update_bits ( i2s - > regmap , I2S_CKR ,
I2S_CKR_MDIV_MASK ,
I2S_CKR_MDIV ( div_bclk ) ) ;
regmap_update_bits ( i2s - > regmap , I2S_CKR ,
I2S_CKR_TSD_MASK |
I2S_CKR_RSD_MASK ,
I2S_CKR_TSD ( div_lrck ) |
I2S_CKR_RSD ( div_lrck ) ) ;
}
2014-07-05 19:13:03 +08:00
switch ( params_format ( params ) ) {
case SNDRV_PCM_FORMAT_S8 :
val | = I2S_TXCR_VDW ( 8 ) ;
break ;
case SNDRV_PCM_FORMAT_S16_LE :
val | = I2S_TXCR_VDW ( 16 ) ;
break ;
case SNDRV_PCM_FORMAT_S20_3LE :
val | = I2S_TXCR_VDW ( 20 ) ;
break ;
case SNDRV_PCM_FORMAT_S24_LE :
val | = I2S_TXCR_VDW ( 24 ) ;
break ;
2016-01-09 23:47:58 +01:00
case SNDRV_PCM_FORMAT_S32_LE :
val | = I2S_TXCR_VDW ( 32 ) ;
break ;
2014-07-05 19:13:03 +08:00
default :
return - EINVAL ;
}
2015-10-08 20:40:07 +08:00
switch ( params_channels ( params ) ) {
case 8 :
val | = I2S_CHN_8 ;
break ;
case 6 :
val | = I2S_CHN_6 ;
break ;
case 4 :
val | = I2S_CHN_4 ;
break ;
case 2 :
2018-01-05 14:12:42 -08:00
case 1 :
2015-10-08 20:40:07 +08:00
val | = I2S_CHN_2 ;
break ;
default :
dev_err ( i2s - > dev , " invalid channel: %d \n " ,
params_channels ( params ) ) ;
return - EINVAL ;
}
if ( substream - > stream = = SNDRV_PCM_STREAM_CAPTURE )
regmap_update_bits ( i2s - > regmap , I2S_RXCR ,
I2S_RXCR_VDW_MASK | I2S_RXCR_CSR_MASK ,
val ) ;
else
regmap_update_bits ( i2s - > regmap , I2S_TXCR ,
I2S_TXCR_VDW_MASK | I2S_TXCR_CSR_MASK ,
val ) ;
2016-04-11 17:26:03 +08:00
if ( ! IS_ERR ( i2s - > grf ) & & i2s - > pins ) {
regmap_read ( i2s - > regmap , I2S_TXCR , & val ) ;
val & = I2S_TXCR_CSR_MASK ;
switch ( val ) {
case I2S_CHN_4 :
val = I2S_IO_4CH_OUT_6CH_IN ;
break ;
case I2S_CHN_6 :
val = I2S_IO_6CH_OUT_4CH_IN ;
break ;
case I2S_CHN_8 :
val = I2S_IO_8CH_OUT_2CH_IN ;
break ;
default :
val = I2S_IO_2CH_OUT_8CH_IN ;
break ;
}
val < < = i2s - > pins - > shift ;
val | = ( I2S_IO_DIRECTION_MASK < < i2s - > pins - > shift ) < < 16 ;
regmap_write ( i2s - > grf , i2s - > pins - > reg_offset , val ) ;
}
2014-12-24 17:37:01 +08:00
regmap_update_bits ( i2s - > regmap , I2S_DMACR , I2S_DMACR_TDL_MASK ,
I2S_DMACR_TDL ( 16 ) ) ;
regmap_update_bits ( i2s - > regmap , I2S_DMACR , I2S_DMACR_RDL_MASK ,
I2S_DMACR_RDL ( 16 ) ) ;
2014-07-05 19:13:03 +08:00
2015-10-08 20:40:09 +08:00
val = I2S_CKR_TRCM_TXRX ;
2016-05-24 11:47:46 +08:00
if ( dai - > driver - > symmetric_rates & & rtd - > dai_link - > symmetric_rates )
val = I2S_CKR_TRCM_TXONLY ;
2015-10-08 20:40:09 +08:00
regmap_update_bits ( i2s - > regmap , I2S_CKR ,
I2S_CKR_TRCM_MASK ,
val ) ;
2014-07-05 19:13:03 +08:00
return 0 ;
}
static int rockchip_i2s_trigger ( struct snd_pcm_substream * substream ,
int cmd , struct snd_soc_dai * dai )
{
struct rk_i2s_dev * i2s = to_info ( dai ) ;
int ret = 0 ;
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
case SNDRV_PCM_TRIGGER_RESUME :
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
if ( substream - > stream = = SNDRV_PCM_STREAM_CAPTURE )
rockchip_snd_rxctrl ( i2s , 1 ) ;
else
rockchip_snd_txctrl ( i2s , 1 ) ;
break ;
case SNDRV_PCM_TRIGGER_SUSPEND :
case SNDRV_PCM_TRIGGER_STOP :
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
if ( substream - > stream = = SNDRV_PCM_STREAM_CAPTURE )
rockchip_snd_rxctrl ( i2s , 0 ) ;
else
rockchip_snd_txctrl ( i2s , 0 ) ;
break ;
default :
ret = - EINVAL ;
break ;
}
return ret ;
}
static int rockchip_i2s_set_sysclk ( struct snd_soc_dai * cpu_dai , int clk_id ,
unsigned int freq , int dir )
{
struct rk_i2s_dev * i2s = to_info ( cpu_dai ) ;
int ret ;
ret = clk_set_rate ( i2s - > mclk , freq ) ;
if ( ret )
dev_err ( i2s - > dev , " Fail to set mclk %d \n " , ret ) ;
return ret ;
}
2014-09-13 08:41:38 +08:00
static int rockchip_i2s_dai_probe ( struct snd_soc_dai * dai )
{
struct rk_i2s_dev * i2s = snd_soc_dai_get_drvdata ( dai ) ;
dai - > capture_dma_data = & i2s - > capture_dma_data ;
dai - > playback_dma_data = & i2s - > playback_dma_data ;
return 0 ;
}
2014-07-05 19:13:03 +08:00
static const struct snd_soc_dai_ops rockchip_i2s_dai_ops = {
. hw_params = rockchip_i2s_hw_params ,
. set_sysclk = rockchip_i2s_set_sysclk ,
. set_fmt = rockchip_i2s_set_fmt ,
. trigger = rockchip_i2s_trigger ,
} ;
static struct snd_soc_dai_driver rockchip_i2s_dai = {
2014-09-13 08:41:38 +08:00
. probe = rockchip_i2s_dai_probe ,
2014-07-05 19:13:03 +08:00
. playback = {
2014-09-13 08:41:38 +08:00
. stream_name = " Playback " ,
2014-07-05 19:13:03 +08:00
. channels_min = 2 ,
. channels_max = 8 ,
. rates = SNDRV_PCM_RATE_8000_192000 ,
. formats = ( SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S20_3LE |
2016-01-09 23:47:58 +01:00
SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE ) ,
2014-07-05 19:13:03 +08:00
} ,
. capture = {
2014-09-13 08:41:38 +08:00
. stream_name = " Capture " ,
2018-01-05 14:12:42 -08:00
. channels_min = 1 ,
2014-07-05 19:13:03 +08:00
. channels_max = 2 ,
. rates = SNDRV_PCM_RATE_8000_192000 ,
. formats = ( SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S20_3LE |
2016-01-09 23:47:58 +01:00
SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE ) ,
2014-07-05 19:13:03 +08:00
} ,
. ops = & rockchip_i2s_dai_ops ,
2015-01-08 10:49:59 +08:00
. symmetric_rates = 1 ,
2014-07-05 19:13:03 +08:00
} ;
static const struct snd_soc_component_driver rockchip_i2s_component = {
. name = DRV_NAME ,
} ;
static bool rockchip_i2s_wr_reg ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case I2S_TXCR :
case I2S_RXCR :
case I2S_CKR :
case I2S_DMACR :
case I2S_INTCR :
case I2S_XFER :
case I2S_CLR :
case I2S_TXDR :
return true ;
default :
return false ;
}
}
static bool rockchip_i2s_rd_reg ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case I2S_TXCR :
case I2S_RXCR :
case I2S_CKR :
case I2S_DMACR :
case I2S_INTCR :
case I2S_XFER :
case I2S_CLR :
2018-01-08 16:01:04 +00:00
case I2S_TXDR :
2014-07-05 19:13:03 +08:00
case I2S_RXDR :
2014-09-13 08:42:12 +08:00
case I2S_FIFOLR :
case I2S_INTSR :
2014-07-05 19:13:03 +08:00
return true ;
default :
return false ;
}
}
static bool rockchip_i2s_volatile_reg ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case I2S_INTSR :
2014-09-13 08:42:12 +08:00
case I2S_CLR :
2018-01-08 16:01:04 +00:00
case I2S_FIFOLR :
case I2S_TXDR :
case I2S_RXDR :
2014-07-05 19:13:03 +08:00
return true ;
default :
return false ;
}
}
static bool rockchip_i2s_precious_reg ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
2018-01-08 16:01:04 +00:00
case I2S_RXDR :
return true ;
2014-07-05 19:13:03 +08:00
default :
return false ;
}
}
2016-02-22 15:56:54 +08:00
static const struct reg_default rockchip_i2s_reg_defaults [ ] = {
{ 0x00 , 0x0000000f } ,
{ 0x04 , 0x0000000f } ,
{ 0x08 , 0x00071f1f } ,
{ 0x10 , 0x001f0000 } ,
{ 0x14 , 0x01f00000 } ,
} ;
2014-07-05 19:13:03 +08:00
static const struct regmap_config rockchip_i2s_regmap_config = {
. reg_bits = 32 ,
. reg_stride = 4 ,
. val_bits = 32 ,
. max_register = I2S_RXDR ,
2016-02-22 15:56:54 +08:00
. reg_defaults = rockchip_i2s_reg_defaults ,
. num_reg_defaults = ARRAY_SIZE ( rockchip_i2s_reg_defaults ) ,
2014-07-05 19:13:03 +08:00
. writeable_reg = rockchip_i2s_wr_reg ,
. readable_reg = rockchip_i2s_rd_reg ,
. volatile_reg = rockchip_i2s_volatile_reg ,
. precious_reg = rockchip_i2s_precious_reg ,
. cache_type = REGCACHE_FLAT ,
} ;
2016-04-11 17:26:03 +08:00
static const struct rk_i2s_pins rk3399_i2s_pins = {
. reg_offset = 0xe220 ,
. shift = 11 ,
} ;
static const struct of_device_id rockchip_i2s_match [ ] = {
{ . compatible = " rockchip,rk3066-i2s " , } ,
{ . compatible = " rockchip,rk3188-i2s " , } ,
{ . compatible = " rockchip,rk3288-i2s " , } ,
{ . compatible = " rockchip,rk3399-i2s " , . data = & rk3399_i2s_pins } ,
{ } ,
} ;
2014-07-05 19:13:03 +08:00
static int rockchip_i2s_probe ( struct platform_device * pdev )
{
2015-10-08 20:40:07 +08:00
struct device_node * node = pdev - > dev . of_node ;
2016-04-11 17:26:03 +08:00
const struct of_device_id * of_id ;
2014-07-05 19:13:03 +08:00
struct rk_i2s_dev * i2s ;
2015-11-10 15:32:07 +08:00
struct snd_soc_dai_driver * soc_dai ;
2014-07-05 19:13:03 +08:00
struct resource * res ;
void __iomem * regs ;
int ret ;
2015-10-08 20:40:07 +08:00
int val ;
2014-07-05 19:13:03 +08:00
i2s = devm_kzalloc ( & pdev - > dev , sizeof ( * i2s ) , GFP_KERNEL ) ;
2017-08-10 18:38:09 +02:00
if ( ! i2s )
2014-07-05 19:13:03 +08:00
return - ENOMEM ;
2016-04-11 17:26:03 +08:00
i2s - > dev = & pdev - > dev ;
i2s - > grf = syscon_regmap_lookup_by_phandle ( node , " rockchip,grf " ) ;
if ( ! IS_ERR ( i2s - > grf ) ) {
of_id = of_match_device ( rockchip_i2s_match , & pdev - > dev ) ;
if ( ! of_id | | ! of_id - > data )
return - EINVAL ;
i2s - > pins = of_id - > data ;
}
2014-07-05 19:13:03 +08:00
/* try to prepare related clocks */
i2s - > hclk = devm_clk_get ( & pdev - > dev , " i2s_hclk " ) ;
if ( IS_ERR ( i2s - > hclk ) ) {
dev_err ( & pdev - > dev , " Can't retrieve i2s bus clock \n " ) ;
return PTR_ERR ( i2s - > hclk ) ;
}
2014-09-13 08:43:13 +08:00
ret = clk_prepare_enable ( i2s - > hclk ) ;
if ( ret ) {
dev_err ( i2s - > dev , " hclock enable failed %d \n " , ret ) ;
return ret ;
}
2014-07-05 19:13:03 +08:00
i2s - > mclk = devm_clk_get ( & pdev - > dev , " i2s_clk " ) ;
if ( IS_ERR ( i2s - > mclk ) ) {
dev_err ( & pdev - > dev , " Can't retrieve i2s master clock \n " ) ;
return PTR_ERR ( i2s - > mclk ) ;
}
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
regs = devm_ioremap_resource ( & pdev - > dev , res ) ;
2014-07-28 21:21:00 +08:00
if ( IS_ERR ( regs ) )
2014-07-05 19:13:03 +08:00
return PTR_ERR ( regs ) ;
i2s - > regmap = devm_regmap_init_mmio ( & pdev - > dev , regs ,
& rockchip_i2s_regmap_config ) ;
if ( IS_ERR ( i2s - > regmap ) ) {
dev_err ( & pdev - > dev ,
" Failed to initialise managed register map \n " ) ;
return PTR_ERR ( i2s - > regmap ) ;
}
i2s - > playback_dma_data . addr = res - > start + I2S_TXDR ;
i2s - > playback_dma_data . addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES ;
2014-12-24 17:37:02 +08:00
i2s - > playback_dma_data . maxburst = 4 ;
2014-07-05 19:13:03 +08:00
i2s - > capture_dma_data . addr = res - > start + I2S_RXDR ;
i2s - > capture_dma_data . addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES ;
2014-12-24 17:37:02 +08:00
i2s - > capture_dma_data . maxburst = 4 ;
2014-07-05 19:13:03 +08:00
dev_set_drvdata ( & pdev - > dev , i2s ) ;
pm_runtime_enable ( & pdev - > dev ) ;
if ( ! pm_runtime_enabled ( & pdev - > dev ) ) {
ret = i2s_runtime_resume ( & pdev - > dev ) ;
if ( ret )
goto err_pm_disable ;
}
2017-06-15 20:13:33 +01:00
soc_dai = devm_kmemdup ( & pdev - > dev , & rockchip_i2s_dai ,
2015-11-10 15:32:07 +08:00
sizeof ( * soc_dai ) , GFP_KERNEL ) ;
2017-06-15 07:53:11 +02:00
if ( ! soc_dai ) {
2017-06-15 20:13:33 +01:00
ret = - ENOMEM ;
2017-06-15 07:53:11 +02:00
goto err_pm_disable ;
}
2015-11-10 15:32:07 +08:00
if ( ! of_property_read_u32 ( node , " rockchip,playback-channels " , & val ) ) {
if ( val > = 2 & & val < = 8 )
soc_dai - > playback . channels_max = val ;
}
2015-10-08 20:40:07 +08:00
if ( ! of_property_read_u32 ( node , " rockchip,capture-channels " , & val ) ) {
2018-01-05 14:12:42 -08:00
if ( val > = 1 & & val < = 8 )
2015-11-10 15:32:07 +08:00
soc_dai - > capture . channels_max = val ;
2015-10-08 20:40:07 +08:00
}
2014-07-05 19:13:03 +08:00
ret = devm_snd_soc_register_component ( & pdev - > dev ,
& rockchip_i2s_component ,
2015-11-10 15:32:07 +08:00
soc_dai , 1 ) ;
2014-07-05 19:13:03 +08:00
if ( ret ) {
dev_err ( & pdev - > dev , " Could not register DAI \n " ) ;
goto err_suspend ;
}
2018-06-08 16:31:09 +08:00
ret = rockchip_pcm_platform_register ( & pdev - > dev ) ;
2014-07-05 19:13:03 +08:00
if ( ret ) {
dev_err ( & pdev - > dev , " Could not register PCM \n " ) ;
2015-08-15 07:21:00 +05:30
return ret ;
2014-07-05 19:13:03 +08:00
}
return 0 ;
err_suspend :
if ( ! pm_runtime_status_suspended ( & pdev - > dev ) )
i2s_runtime_suspend ( & pdev - > dev ) ;
err_pm_disable :
pm_runtime_disable ( & pdev - > dev ) ;
return ret ;
}
static int rockchip_i2s_remove ( struct platform_device * pdev )
{
struct rk_i2s_dev * i2s = dev_get_drvdata ( & pdev - > dev ) ;
pm_runtime_disable ( & pdev - > dev ) ;
if ( ! pm_runtime_status_suspended ( & pdev - > dev ) )
i2s_runtime_suspend ( & pdev - > dev ) ;
clk_disable_unprepare ( i2s - > hclk ) ;
return 0 ;
}
static const struct dev_pm_ops rockchip_i2s_pm_ops = {
SET_RUNTIME_PM_OPS ( i2s_runtime_suspend , i2s_runtime_resume ,
NULL )
} ;
static struct platform_driver rockchip_i2s_driver = {
. probe = rockchip_i2s_probe ,
. remove = rockchip_i2s_remove ,
. driver = {
. name = DRV_NAME ,
. of_match_table = of_match_ptr ( rockchip_i2s_match ) ,
. pm = & rockchip_i2s_pm_ops ,
} ,
} ;
module_platform_driver ( rockchip_i2s_driver ) ;
MODULE_DESCRIPTION ( " ROCKCHIP IIS ASoC Interface " ) ;
MODULE_AUTHOR ( " jianqun <jay.xu@rock-chips.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_ALIAS ( " platform: " DRV_NAME ) ;
MODULE_DEVICE_TABLE ( of , rockchip_i2s_match ) ;