2019-04-19 12:21:51 +02:00
// SPDX-License-Identifier: GPL-2.0+
//
// ALSA Soc Audio Layer - S3C2412 I2S driver
//
// Copyright (c) 2006 Wolfson Microelectronics PLC.
// Graeme Gregory graeme.gregory@wolfsonmicro.com
// linux@wolfsonmicro.com
//
// Copyright (c) 2007, 2004-2005 Simtec Electronics
// http://armlinux.simtec.co.uk/
// Ben Dooks <ben@simtec.co.uk>
2008-01-10 14:47:21 +01:00
# include <linux/delay.h>
2009-05-13 22:52:24 +01:00
# include <linux/gpio.h>
2008-01-10 14:47:21 +01:00
# include <linux/clk.h>
2009-03-04 00:49:26 +00:00
# include <linux/io.h>
2011-07-15 12:38:28 -04:00
# include <linux/module.h>
2008-01-10 14:47:21 +01:00
# include <sound/soc.h>
2011-01-11 07:26:06 +09:00
# include <sound/pcm_params.h>
2008-01-10 14:47:21 +01:00
2010-11-22 15:35:57 +09:00
# include "dma.h"
2010-04-27 15:55:21 +09:00
# include "regs-i2s-v2.h"
2008-01-10 14:47:21 +01:00
# include "s3c2412-i2s.h"
2015-11-18 15:26:00 +01:00
# include <linux/platform_data/asoc-s3c.h>
2016-08-04 11:30:28 +02:00
static struct snd_dmaengine_dai_dma_data s3c2412_i2s_pcm_stereo_out = {
2017-01-17 14:16:42 +01:00
. chan_name = " tx " ,
2016-08-04 11:30:28 +02:00
. addr_width = 4 ,
2008-01-10 14:47:21 +01:00
} ;
2016-08-04 11:30:28 +02:00
static struct snd_dmaengine_dai_dma_data s3c2412_i2s_pcm_stereo_in = {
2017-01-17 14:16:42 +01:00
. chan_name = " rx " ,
2016-08-04 11:30:28 +02:00
. addr_width = 4 ,
2008-01-10 14:47:21 +01:00
} ;
2009-03-04 00:49:30 +00:00
static struct s3c_i2sv2_info s3c2412_i2s ;
2008-01-10 14:47:21 +01:00
2010-03-17 20:15:21 +00:00
static int s3c2412_i2s_probe ( struct snd_soc_dai * dai )
2008-01-10 14:47:21 +01:00
{
2009-03-04 00:49:30 +00:00
int ret ;
2008-01-10 14:47:21 +01:00
2009-03-06 18:04:34 +00:00
pr_debug ( " Entered %s \n " , __func__ ) ;
2008-01-10 14:47:21 +01:00
2016-08-04 11:30:28 +02:00
snd_soc_dai_init_dma_data ( dai , & s3c2412_i2s_pcm_stereo_out ,
& s3c2412_i2s_pcm_stereo_in ) ;
2014-06-23 23:24:04 +03:00
2020-08-06 20:20:44 +02:00
ret = s3c_i2sv2_probe ( dai , & s3c2412_i2s ) ;
2009-03-04 00:49:30 +00:00
if ( ret )
return ret ;
2008-01-10 14:47:21 +01:00
2009-03-04 00:49:30 +00:00
s3c2412_i2s . dma_capture = & s3c2412_i2s_pcm_stereo_in ;
s3c2412_i2s . dma_playback = & s3c2412_i2s_pcm_stereo_out ;
2008-01-10 14:47:21 +01:00
2014-06-23 23:24:04 +03:00
s3c2412_i2s . iis_cclk = devm_clk_get ( dai - > dev , " i2sclk " ) ;
2011-09-15 10:36:54 +08:00
if ( IS_ERR ( s3c2412_i2s . iis_cclk ) ) {
2009-04-24 16:27:09 +01:00
pr_err ( " failed to get i2sclk clock \n " ) ;
2017-07-27 19:13:38 +02:00
ret = PTR_ERR ( s3c2412_i2s . iis_cclk ) ;
goto err ;
2008-01-10 14:47:21 +01:00
}
2009-03-04 00:49:30 +00:00
/* Set MPLL as the source for IIS CLK */
2008-01-10 14:47:21 +01:00
2009-03-04 00:49:30 +00:00
clk_set_parent ( s3c2412_i2s . iis_cclk , clk_get ( NULL , " mpll " ) ) ;
2017-07-25 15:44:28 +05:30
ret = clk_prepare_enable ( s3c2412_i2s . iis_cclk ) ;
if ( ret )
2017-07-27 19:13:38 +02:00
goto err ;
2008-01-10 14:47:21 +01:00
2008-01-10 14:48:37 +01:00
return 0 ;
2017-07-27 19:13:38 +02:00
err :
s3c_i2sv2_cleanup ( dai , & s3c2412_i2s ) ;
return ret ;
2008-01-10 14:48:37 +01:00
}
2010-03-17 20:15:21 +00:00
static int s3c2412_i2s_remove ( struct snd_soc_dai * dai )
{
2014-06-23 23:24:06 +03:00
clk_disable_unprepare ( s3c2412_i2s . iis_cclk ) ;
2017-07-27 19:13:38 +02:00
s3c_i2sv2_cleanup ( dai , & s3c2412_i2s ) ;
2010-03-17 20:15:21 +00:00
return 0 ;
}
2010-03-10 16:48:58 +09:00
static int s3c2412_i2s_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params ,
struct snd_soc_dai * cpu_dai )
{
2010-03-17 20:15:21 +00:00
struct s3c_i2sv2_info * i2s = snd_soc_dai_get_drvdata ( cpu_dai ) ;
2010-03-10 16:48:58 +09:00
u32 iismod ;
pr_debug ( " Entered %s \n " , __func__ ) ;
iismod = readl ( i2s - > regs + S3C2412_IISMOD ) ;
pr_debug ( " %s: r: IISMOD: %x \n " , __func__ , iismod ) ;
2014-05-23 17:35:39 +05:30
switch ( params_width ( params ) ) {
case 8 :
2010-03-10 16:48:58 +09:00
iismod | = S3C2412_IISMOD_8BIT ;
break ;
2014-05-23 17:35:39 +05:30
case 16 :
2010-03-10 16:48:58 +09:00
iismod & = ~ S3C2412_IISMOD_8BIT ;
break ;
}
writel ( iismod , i2s - > regs + S3C2412_IISMOD ) ;
pr_debug ( " %s: w: IISMOD: %x \n " , __func__ , iismod ) ;
return 0 ;
}
2020-04-13 14:45:48 +02:00
# ifdef CONFIG_PM
static int s3c2412_i2s_suspend ( struct snd_soc_component * component )
{
struct s3c_i2sv2_info * i2s = snd_soc_component_get_drvdata ( component ) ;
u32 iismod ;
if ( component - > active ) {
i2s - > suspend_iismod = readl ( i2s - > regs + S3C2412_IISMOD ) ;
i2s - > suspend_iiscon = readl ( i2s - > regs + S3C2412_IISCON ) ;
i2s - > suspend_iispsr = readl ( i2s - > regs + S3C2412_IISPSR ) ;
/* some basic suspend checks */
iismod = readl ( i2s - > regs + S3C2412_IISMOD ) ;
if ( iismod & S3C2412_IISCON_RXDMA_ACTIVE )
pr_warn ( " %s: RXDMA active? \n " , __func__ ) ;
if ( iismod & S3C2412_IISCON_TXDMA_ACTIVE )
pr_warn ( " %s: TXDMA active? \n " , __func__ ) ;
if ( iismod & S3C2412_IISCON_IIS_ACTIVE )
pr_warn ( " %s: IIS active \n " , __func__ ) ;
}
return 0 ;
}
static int s3c2412_i2s_resume ( struct snd_soc_component * component )
{
struct s3c_i2sv2_info * i2s = snd_soc_component_get_drvdata ( component ) ;
pr_info ( " component_active %d, IISMOD %08x, IISCON %08x \n " ,
component - > active , i2s - > suspend_iismod , i2s - > suspend_iiscon ) ;
if ( component - > active ) {
writel ( i2s - > suspend_iiscon , i2s - > regs + S3C2412_IISCON ) ;
writel ( i2s - > suspend_iismod , i2s - > regs + S3C2412_IISMOD ) ;
writel ( i2s - > suspend_iispsr , i2s - > regs + S3C2412_IISPSR ) ;
writel ( S3C2412_IISFIC_RXFLUSH | S3C2412_IISFIC_TXFLUSH ,
i2s - > regs + S3C2412_IISFIC ) ;
ndelay ( 250 ) ;
writel ( 0x0 , i2s - > regs + S3C2412_IISFIC ) ;
}
return 0 ;
}
# else
# define s3c2412_i2s_suspend NULL
# define s3c2412_i2s_resume NULL
# endif
2008-01-10 14:47:21 +01:00
# define S3C2412_I2S_RATES \
( SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 )
2011-11-23 11:40:40 +01:00
static const struct snd_soc_dai_ops s3c2412_i2s_dai_ops = {
2010-03-10 16:48:58 +09:00
. hw_params = s3c2412_i2s_hw_params ,
2009-03-03 09:41:00 +08:00
} ;
2010-03-17 20:15:21 +00:00
static struct snd_soc_dai_driver s3c2412_i2s_dai = {
2009-03-04 00:49:30 +00:00
. probe = s3c2412_i2s_probe ,
2010-03-17 20:15:21 +00:00
. remove = s3c2412_i2s_remove ,
2008-01-10 14:47:21 +01:00
. playback = {
. channels_min = 2 ,
. channels_max = 2 ,
. rates = S3C2412_I2S_RATES ,
. formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE ,
} ,
. capture = {
. channels_min = 2 ,
. channels_max = 2 ,
. rates = S3C2412_I2S_RATES ,
. formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE ,
} ,
2009-03-03 09:41:00 +08:00
. ops = & s3c2412_i2s_dai_ops ,
2008-01-10 14:47:21 +01:00
} ;
2010-03-17 20:15:21 +00:00
2013-03-21 03:35:22 -07:00
static const struct snd_soc_component_driver s3c2412_i2s_component = {
. name = " s3c2412-i2s " ,
2020-04-13 14:45:48 +02:00
. suspend = s3c2412_i2s_suspend ,
. resume = s3c2412_i2s_resume ,
2013-03-21 03:35:22 -07:00
} ;
2012-12-07 09:26:15 -05:00
static int s3c2412_iis_dev_probe ( struct platform_device * pdev )
2010-03-17 20:15:21 +00:00
{
2012-12-07 13:59:21 +05:30
int ret = 0 ;
2014-06-23 23:24:04 +03:00
struct resource * res ;
2015-11-18 15:26:00 +01:00
struct s3c_audio_pdata * pdata = dev_get_platdata ( & pdev - > dev ) ;
if ( ! pdata ) {
dev_err ( & pdev - > dev , " missing platform data " ) ;
return - ENXIO ;
}
2014-06-23 23:24:04 +03:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
s3c2412_i2s . regs = devm_ioremap_resource ( & pdev - > dev , res ) ;
2014-07-20 11:43:07 +08:00
if ( IS_ERR ( s3c2412_i2s . regs ) )
return PTR_ERR ( s3c2412_i2s . regs ) ;
2014-06-23 23:24:04 +03:00
2016-08-04 11:30:28 +02:00
s3c2412_i2s_pcm_stereo_out . addr = res - > start + S3C2412_IISTXD ;
s3c2412_i2s_pcm_stereo_out . filter_data = pdata - > dma_playback ;
s3c2412_i2s_pcm_stereo_in . addr = res - > start + S3C2412_IISRXD ;
s3c2412_i2s_pcm_stereo_in . filter_data = pdata - > dma_capture ;
2012-12-07 13:59:21 +05:30
2016-10-27 12:34:02 +02:00
ret = samsung_asoc_dma_platform_register ( & pdev - > dev ,
pdata - > dma_filter ,
2019-02-14 10:37:41 +01:00
" tx " , " rx " , NULL ) ;
2012-12-07 13:59:21 +05:30
if ( ret ) {
2016-10-27 12:34:02 +02:00
pr_err ( " failed to register the DMA: %d \n " , ret ) ;
2012-12-07 13:59:21 +05:30
return ret ;
}
2016-10-27 12:34:02 +02:00
ret = s3c_i2sv2_register_component ( & pdev - > dev , - 1 ,
& s3c2412_i2s_component ,
& s3c2412_i2s_dai ) ;
2014-05-21 08:52:19 +05:30
if ( ret )
2016-10-27 12:34:02 +02:00
pr_err ( " failed to register the dai \n " ) ;
2012-12-07 13:59:21 +05:30
return ret ;
2010-03-17 20:15:21 +00:00
}
static struct platform_driver s3c2412_iis_driver = {
. probe = s3c2412_iis_dev_probe ,
. driver = {
. name = " s3c2412-iis " ,
} ,
} ;
2008-01-10 14:47:21 +01:00
2011-11-23 15:20:13 +00:00
module_platform_driver ( s3c2412_iis_driver ) ;
2008-12-03 19:26:35 +00:00
2008-01-10 14:47:21 +01:00
/* Module information */
MODULE_AUTHOR ( " Ben Dooks, <ben@simtec.co.uk> " ) ;
MODULE_DESCRIPTION ( " S3C2412 I2S SoC Interface " ) ;
MODULE_LICENSE ( " GPL " ) ;
2010-08-12 11:02:19 +01:00
MODULE_ALIAS ( " platform:s3c2412-iis " ) ;