2015-06-04 11:41:22 +08:00
/*
* Copyright ( C ) 2015 Linaro
*
* Author : Jun Nie < jun . nie @ linaro . org >
*
* License terms : GNU General Public License ( GPL ) version 2
*/
# include <linux/clk.h>
# include <linux/device.h>
# include <linux/dmaengine.h>
# include <linux/init.h>
# include <linux/io.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/of_address.h>
# include <sound/asoundef.h>
# include <sound/core.h>
# include <sound/dmaengine_pcm.h>
# include <sound/initval.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/soc.h>
# include <sound/soc-dai.h>
# define ZX_CTRL 0x04
# define ZX_FIFOCTRL 0x08
# define ZX_INT_STATUS 0x10
# define ZX_INT_MASK 0x14
# define ZX_DATA 0x18
# define ZX_VALID_BIT 0x1c
# define ZX_CH_STA_1 0x20
# define ZX_CH_STA_2 0x24
# define ZX_CH_STA_3 0x28
# define ZX_CH_STA_4 0x2c
# define ZX_CH_STA_5 0x30
# define ZX_CH_STA_6 0x34
# define ZX_CTRL_MODA_16 (0 << 6)
# define ZX_CTRL_MODA_18 BIT(6)
# define ZX_CTRL_MODA_20 (2 << 6)
# define ZX_CTRL_MODA_24 (3 << 6)
# define ZX_CTRL_MODA_MASK (3 << 6)
# define ZX_CTRL_ENB BIT(4)
# define ZX_CTRL_DNB (0 << 4)
# define ZX_CTRL_ENB_MASK BIT(4)
# define ZX_CTRL_TX_OPEN BIT(0)
# define ZX_CTRL_TX_CLOSE (0 << 0)
# define ZX_CTRL_TX_MASK BIT(0)
# define ZX_CTRL_OPEN (ZX_CTRL_TX_OPEN | ZX_CTRL_ENB)
# define ZX_CTRL_CLOSE (ZX_CTRL_TX_CLOSE | ZX_CTRL_DNB)
# define ZX_CTRL_DOUBLE_TRACK (0 << 8)
# define ZX_CTRL_LEFT_TRACK BIT(8)
# define ZX_CTRL_RIGHT_TRACK (2 << 8)
# define ZX_CTRL_TRACK_MASK (3 << 8)
# define ZX_FIFOCTRL_TXTH_MASK (0x1f << 8)
# define ZX_FIFOCTRL_TXTH(x) (x << 8)
# define ZX_FIFOCTRL_TX_DMA_EN BIT(2)
# define ZX_FIFOCTRL_TX_DMA_DIS (0 << 2)
# define ZX_FIFOCTRL_TX_DMA_EN_MASK BIT(2)
# define ZX_FIFOCTRL_TX_FIFO_RST BIT(0)
# define ZX_FIFOCTRL_TX_FIFO_RST_MASK BIT(0)
# define ZX_VALID_DOUBLE_TRACK (0 << 0)
# define ZX_VALID_LEFT_TRACK BIT(1)
# define ZX_VALID_RIGHT_TRACK (2 << 0)
# define ZX_VALID_TRACK_MASK (3 << 0)
2016-12-08 16:44:15 +08:00
# define ZX_SPDIF_CLK_RAT (2 * 32)
2015-06-04 11:41:22 +08:00
struct zx_spdif_info {
struct snd_dmaengine_dai_dma_data dma_data ;
struct clk * dai_clk ;
void __iomem * reg_base ;
resource_size_t mapbase ;
} ;
static int zx_spdif_dai_probe ( struct snd_soc_dai * dai )
{
struct zx_spdif_info * zx_spdif = dev_get_drvdata ( dai - > dev ) ;
snd_soc_dai_set_drvdata ( dai , zx_spdif ) ;
zx_spdif - > dma_data . addr = zx_spdif - > mapbase + ZX_DATA ;
zx_spdif - > dma_data . maxburst = 8 ;
snd_soc_dai_init_dma_data ( dai , & zx_spdif - > dma_data , NULL ) ;
return 0 ;
}
static int zx_spdif_chanstats ( void __iomem * base , unsigned int rate )
{
u32 cstas1 ;
switch ( rate ) {
case 22050 :
cstas1 = IEC958_AES3_CON_FS_22050 ;
break ;
case 24000 :
cstas1 = IEC958_AES3_CON_FS_24000 ;
break ;
case 32000 :
cstas1 = IEC958_AES3_CON_FS_32000 ;
break ;
case 44100 :
cstas1 = IEC958_AES3_CON_FS_44100 ;
break ;
case 48000 :
cstas1 = IEC958_AES3_CON_FS_48000 ;
break ;
case 88200 :
cstas1 = IEC958_AES3_CON_FS_88200 ;
break ;
case 96000 :
cstas1 = IEC958_AES3_CON_FS_96000 ;
break ;
case 176400 :
cstas1 = IEC958_AES3_CON_FS_176400 ;
break ;
case 192000 :
cstas1 = IEC958_AES3_CON_FS_192000 ;
break ;
default :
return - EINVAL ;
}
cstas1 = cstas1 < < 24 ;
cstas1 | = IEC958_AES0_CON_NOT_COPYRIGHT ;
writel_relaxed ( cstas1 , base + ZX_CH_STA_1 ) ;
return 0 ;
}
static int zx_spdif_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params ,
struct snd_soc_dai * socdai )
{
struct zx_spdif_info * zx_spdif = dev_get_drvdata ( socdai - > dev ) ;
struct zx_spdif_info * spdif = snd_soc_dai_get_drvdata ( socdai ) ;
struct snd_dmaengine_dai_dma_data * dma_data = & zx_spdif - > dma_data ;
u32 val , ch_num , rate ;
int ret ;
dma_data = snd_soc_dai_get_dma_data ( socdai , substream ) ;
dma_data - > addr_width = params_width ( params ) > > 3 ;
val = readl_relaxed ( zx_spdif - > reg_base + ZX_CTRL ) ;
val & = ~ ZX_CTRL_MODA_MASK ;
switch ( params_format ( params ) ) {
case SNDRV_PCM_FORMAT_S16_LE :
val | = ZX_CTRL_MODA_16 ;
break ;
case SNDRV_PCM_FORMAT_S18_3LE :
val | = ZX_CTRL_MODA_18 ;
break ;
case SNDRV_PCM_FORMAT_S20_3LE :
val | = ZX_CTRL_MODA_20 ;
break ;
case SNDRV_PCM_FORMAT_S24_LE :
val | = ZX_CTRL_MODA_24 ;
break ;
default :
dev_err ( socdai - > dev , " Format not support! \n " ) ;
return - EINVAL ;
}
ch_num = params_channels ( params ) ;
if ( ch_num = = 2 )
val | = ZX_CTRL_DOUBLE_TRACK ;
else
val | = ZX_CTRL_LEFT_TRACK ;
writel_relaxed ( val , zx_spdif - > reg_base + ZX_CTRL ) ;
val = readl_relaxed ( zx_spdif - > reg_base + ZX_VALID_BIT ) ;
val & = ~ ZX_VALID_TRACK_MASK ;
if ( ch_num = = 2 )
val | = ZX_VALID_DOUBLE_TRACK ;
else
val | = ZX_VALID_RIGHT_TRACK ;
writel_relaxed ( val , zx_spdif - > reg_base + ZX_VALID_BIT ) ;
rate = params_rate ( params ) ;
ret = zx_spdif_chanstats ( zx_spdif - > reg_base , rate ) ;
if ( ret )
return ret ;
2015-06-04 23:57:20 +08:00
return clk_set_rate ( spdif - > dai_clk , rate * ch_num * ZX_SPDIF_CLK_RAT ) ;
2015-06-04 11:41:22 +08:00
}
static void zx_spdif_cfg_tx ( void __iomem * base , int on )
{
u32 val ;
val = readl_relaxed ( base + ZX_CTRL ) ;
val & = ~ ( ZX_CTRL_ENB_MASK | ZX_CTRL_TX_MASK ) ;
val | = on ? ZX_CTRL_OPEN : ZX_CTRL_CLOSE ;
writel_relaxed ( val , base + ZX_CTRL ) ;
val = readl_relaxed ( base + ZX_FIFOCTRL ) ;
val & = ~ ZX_FIFOCTRL_TX_DMA_EN_MASK ;
if ( on )
val | = ZX_FIFOCTRL_TX_DMA_EN ;
writel_relaxed ( val , base + ZX_FIFOCTRL ) ;
}
static int zx_spdif_trigger ( struct snd_pcm_substream * substream , int cmd ,
struct snd_soc_dai * dai )
{
u32 val ;
struct zx_spdif_info * zx_spdif = dev_get_drvdata ( dai - > dev ) ;
int ret = 0 ;
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
val = readl_relaxed ( zx_spdif - > reg_base + ZX_FIFOCTRL ) ;
val | = ZX_FIFOCTRL_TX_FIFO_RST ;
writel_relaxed ( val , zx_spdif - > reg_base + ZX_FIFOCTRL ) ;
/* fall thru */
case SNDRV_PCM_TRIGGER_RESUME :
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
zx_spdif_cfg_tx ( zx_spdif - > reg_base , true ) ;
break ;
case SNDRV_PCM_TRIGGER_STOP :
case SNDRV_PCM_TRIGGER_SUSPEND :
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
zx_spdif_cfg_tx ( zx_spdif - > reg_base , false ) ;
break ;
default :
ret = - EINVAL ;
break ;
}
return ret ;
}
static int zx_spdif_startup ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
struct zx_spdif_info * zx_spdif = dev_get_drvdata ( dai - > dev ) ;
return clk_prepare_enable ( zx_spdif - > dai_clk ) ;
}
static void zx_spdif_shutdown ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
struct zx_spdif_info * zx_spdif = dev_get_drvdata ( dai - > dev ) ;
clk_disable_unprepare ( zx_spdif - > dai_clk ) ;
}
# define ZX_RATES \
( SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \
SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000 )
# define ZX_FORMAT \
( SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE \
| SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE )
static struct snd_soc_dai_ops zx_spdif_dai_ops = {
. trigger = zx_spdif_trigger ,
. startup = zx_spdif_startup ,
. shutdown = zx_spdif_shutdown ,
. hw_params = zx_spdif_hw_params ,
} ;
static struct snd_soc_dai_driver zx_spdif_dai = {
. name = " spdif " ,
. id = 0 ,
. probe = zx_spdif_dai_probe ,
. playback = {
. channels_min = 1 ,
. channels_max = 2 ,
. rates = ZX_RATES ,
. formats = ZX_FORMAT ,
} ,
. ops = & zx_spdif_dai_ops ,
} ;
static const struct snd_soc_component_driver zx_spdif_component = {
. name = " spdif " ,
} ;
static void zx_spdif_dev_init ( void __iomem * base )
{
u32 val ;
writel_relaxed ( 0 , base + ZX_CTRL ) ;
writel_relaxed ( 0 , base + ZX_INT_MASK ) ;
writel_relaxed ( 0xf , base + ZX_INT_STATUS ) ;
writel_relaxed ( 0x1 , base + ZX_FIFOCTRL ) ;
val = readl_relaxed ( base + ZX_FIFOCTRL ) ;
val & = ~ ( ZX_FIFOCTRL_TXTH_MASK | ZX_FIFOCTRL_TX_FIFO_RST_MASK ) ;
val | = ZX_FIFOCTRL_TXTH ( 8 ) ;
writel_relaxed ( val , base + ZX_FIFOCTRL ) ;
}
static int zx_spdif_probe ( struct platform_device * pdev )
{
struct resource * res ;
struct zx_spdif_info * zx_spdif ;
int ret ;
2015-06-04 17:11:18 +01:00
zx_spdif = devm_kzalloc ( & pdev - > dev , sizeof ( * zx_spdif ) , GFP_KERNEL ) ;
2015-06-04 11:41:22 +08:00
if ( ! zx_spdif )
return - ENOMEM ;
zx_spdif - > dai_clk = devm_clk_get ( & pdev - > dev , " tx " ) ;
if ( IS_ERR ( zx_spdif - > dai_clk ) ) {
dev_err ( & pdev - > dev , " Fail to get clk \n " ) ;
return PTR_ERR ( zx_spdif - > dai_clk ) ;
}
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
zx_spdif - > mapbase = res - > start ;
zx_spdif - > reg_base = devm_ioremap_resource ( & pdev - > dev , res ) ;
2015-07-09 22:19:29 +09:00
if ( IS_ERR ( zx_spdif - > reg_base ) ) {
2015-06-04 11:41:22 +08:00
dev_err ( & pdev - > dev , " ioremap failed! \n " ) ;
2015-07-09 22:19:29 +09:00
return PTR_ERR ( zx_spdif - > reg_base ) ;
2015-06-04 11:41:22 +08:00
}
zx_spdif_dev_init ( zx_spdif - > reg_base ) ;
platform_set_drvdata ( pdev , zx_spdif ) ;
ret = devm_snd_soc_register_component ( & pdev - > dev , & zx_spdif_component ,
& zx_spdif_dai , 1 ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " Register DAI failed: %d \n " , ret ) ;
return ret ;
}
ret = devm_snd_dmaengine_pcm_register ( & pdev - > dev , NULL , 0 ) ;
if ( ret )
dev_err ( & pdev - > dev , " Register platform PCM failed: %d \n " , ret ) ;
return ret ;
}
static const struct of_device_id zx_spdif_dt_ids [ ] = {
{ . compatible = " zte,zx296702-spdif " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , zx_spdif_dt_ids ) ;
static struct platform_driver spdif_driver = {
. probe = zx_spdif_probe ,
. driver = {
. name = " zx-spdif " ,
. of_match_table = zx_spdif_dt_ids ,
} ,
} ;
module_platform_driver ( spdif_driver ) ;
MODULE_AUTHOR ( " Jun Nie <jun.nie@linaro.org> " ) ;
MODULE_DESCRIPTION ( " ZTE SPDIF SoC DAI " ) ;
MODULE_LICENSE ( " GPL " ) ;