2010-11-22 09:37:25 +03:00
/* sound/soc/samsung/s3c-i2c-v2.c
2009-03-04 03:49:30 +03:00
*
* ALSA Soc Audio Layer - I2S core for newer Samsung SoCs .
*
* Copyright ( c ) 2006 Wolfson Microelectronics PLC .
* Graeme Gregory graeme . gregory @ wolfsonmicro . com
* linux @ wolfsonmicro . com
*
* Copyright ( c ) 2008 , 2007 , 2004 - 2005 Simtec Electronics
* http : //armlinux.simtec.co.uk/
* Ben Dooks < ben @ simtec . co . uk >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*/
# include <linux/delay.h>
# include <linux/clk.h>
# include <linux/io.h>
# include <sound/soc.h>
2011-01-11 01:26:06 +03:00
# include <sound/pcm_params.h>
2009-03-04 03:49:30 +03:00
# include <mach/dma.h>
2010-04-27 10:55:21 +04:00
# include "regs-i2s-v2.h"
2009-03-04 03:49:30 +03:00
# include "s3c-i2s-v2.h"
2010-11-22 09:35:57 +03:00
# include "dma.h"
2009-03-04 03:49:30 +03:00
2009-04-29 23:28:47 +04:00
# undef S3C_IIS_V2_SUPPORTED
2010-07-20 09:28:30 +04:00
# if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413) \
| | defined ( CONFIG_CPU_S5PV210 )
2009-04-29 23:28:47 +04:00
# define S3C_IIS_V2_SUPPORTED
# endif
# ifdef CONFIG_PLAT_S3C64XX
# define S3C_IIS_V2_SUPPORTED
# endif
# ifndef S3C_IIS_V2_SUPPORTED
# error Unsupported CPU model
# endif
2009-03-04 03:49:30 +03:00
# define S3C2412_I2S_DEBUG_CON 0
static inline struct s3c_i2sv2_info * to_info ( struct snd_soc_dai * cpu_dai )
{
2010-03-17 23:15:21 +03:00
return snd_soc_dai_get_drvdata ( cpu_dai ) ;
2009-03-04 03:49:30 +03:00
}
# define bit_set(v, b) (((v) & (b)) ? 1 : 0)
# if S3C2412_I2S_DEBUG_CON
static void dbg_showcon ( const char * fn , u32 con )
{
printk ( KERN_DEBUG " %s: LRI=%d, TXFEMPT=%d, RXFEMPT=%d, TXFFULL=%d, RXFFULL=%d \n " , fn ,
bit_set ( con , S3C2412_IISCON_LRINDEX ) ,
bit_set ( con , S3C2412_IISCON_TXFIFO_EMPTY ) ,
bit_set ( con , S3C2412_IISCON_RXFIFO_EMPTY ) ,
bit_set ( con , S3C2412_IISCON_TXFIFO_FULL ) ,
bit_set ( con , S3C2412_IISCON_RXFIFO_FULL ) ) ;
printk ( KERN_DEBUG " %s: PAUSE: TXDMA=%d, RXDMA=%d, TXCH=%d, RXCH=%d \n " ,
fn ,
bit_set ( con , S3C2412_IISCON_TXDMA_PAUSE ) ,
bit_set ( con , S3C2412_IISCON_RXDMA_PAUSE ) ,
bit_set ( con , S3C2412_IISCON_TXCH_PAUSE ) ,
bit_set ( con , S3C2412_IISCON_RXCH_PAUSE ) ) ;
printk ( KERN_DEBUG " %s: ACTIVE: TXDMA=%d, RXDMA=%d, IIS=%d \n " , fn ,
bit_set ( con , S3C2412_IISCON_TXDMA_ACTIVE ) ,
bit_set ( con , S3C2412_IISCON_RXDMA_ACTIVE ) ,
bit_set ( con , S3C2412_IISCON_IIS_ACTIVE ) ) ;
}
# else
static inline void dbg_showcon ( const char * fn , u32 con )
{
}
# endif
/* Turn on or off the transmission path. */
2009-04-30 16:21:52 +04:00
static void s3c2412_snd_txctrl ( struct s3c_i2sv2_info * i2s , int on )
2009-03-04 03:49:30 +03:00
{
void __iomem * regs = i2s - > regs ;
u32 fic , con , mod ;
2009-03-06 21:04:34 +03:00
pr_debug ( " %s(%d) \n " , __func__ , on ) ;
2009-03-04 03:49:30 +03:00
fic = readl ( regs + S3C2412_IISFIC ) ;
con = readl ( regs + S3C2412_IISCON ) ;
mod = readl ( regs + S3C2412_IISMOD ) ;
2009-03-06 21:04:34 +03:00
pr_debug ( " %s: IIS: CON=%x MOD=%x FIC=%x \n " , __func__ , con , mod , fic ) ;
2009-03-04 03:49:30 +03:00
if ( on ) {
con | = S3C2412_IISCON_TXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE ;
con & = ~ S3C2412_IISCON_TXDMA_PAUSE ;
con & = ~ S3C2412_IISCON_TXCH_PAUSE ;
switch ( mod & S3C2412_IISMOD_MODE_MASK ) {
case S3C2412_IISMOD_MODE_TXONLY :
case S3C2412_IISMOD_MODE_TXRX :
/* do nothing, we are in the right mode */
break ;
case S3C2412_IISMOD_MODE_RXONLY :
mod & = ~ S3C2412_IISMOD_MODE_MASK ;
mod | = S3C2412_IISMOD_MODE_TXRX ;
break ;
default :
2009-04-30 16:21:52 +04:00
dev_err ( i2s - > dev , " TXEN: Invalid MODE %x in IISMOD \n " ,
mod & S3C2412_IISMOD_MODE_MASK ) ;
break ;
2009-03-04 03:49:30 +03:00
}
writel ( con , regs + S3C2412_IISCON ) ;
writel ( mod , regs + S3C2412_IISMOD ) ;
} else {
/* Note, we do not have any indication that the FIFO problems
* tha the S3C2410 / 2440 had apply here , so we should be able
* to disable the DMA and TX without resetting the FIFOS .
*/
con | = S3C2412_IISCON_TXDMA_PAUSE ;
con | = S3C2412_IISCON_TXCH_PAUSE ;
con & = ~ S3C2412_IISCON_TXDMA_ACTIVE ;
switch ( mod & S3C2412_IISMOD_MODE_MASK ) {
case S3C2412_IISMOD_MODE_TXRX :
mod & = ~ S3C2412_IISMOD_MODE_MASK ;
mod | = S3C2412_IISMOD_MODE_RXONLY ;
break ;
case S3C2412_IISMOD_MODE_TXONLY :
mod & = ~ S3C2412_IISMOD_MODE_MASK ;
con & = ~ S3C2412_IISCON_IIS_ACTIVE ;
break ;
default :
2009-04-30 16:21:52 +04:00
dev_err ( i2s - > dev , " TXDIS: Invalid MODE %x in IISMOD \n " ,
mod & S3C2412_IISMOD_MODE_MASK ) ;
break ;
2009-03-04 03:49:30 +03:00
}
writel ( mod , regs + S3C2412_IISMOD ) ;
writel ( con , regs + S3C2412_IISCON ) ;
}
fic = readl ( regs + S3C2412_IISFIC ) ;
dbg_showcon ( __func__ , con ) ;
2009-03-06 21:04:34 +03:00
pr_debug ( " %s: IIS: CON=%x MOD=%x FIC=%x \n " , __func__ , con , mod , fic ) ;
2009-03-04 03:49:30 +03:00
}
2009-04-30 16:21:52 +04:00
static void s3c2412_snd_rxctrl ( struct s3c_i2sv2_info * i2s , int on )
2009-03-04 03:49:30 +03:00
{
void __iomem * regs = i2s - > regs ;
u32 fic , con , mod ;
2009-03-06 21:04:34 +03:00
pr_debug ( " %s(%d) \n " , __func__ , on ) ;
2009-03-04 03:49:30 +03:00
fic = readl ( regs + S3C2412_IISFIC ) ;
con = readl ( regs + S3C2412_IISCON ) ;
mod = readl ( regs + S3C2412_IISMOD ) ;
2009-03-06 21:04:34 +03:00
pr_debug ( " %s: IIS: CON=%x MOD=%x FIC=%x \n " , __func__ , con , mod , fic ) ;
2009-03-04 03:49:30 +03:00
if ( on ) {
con | = S3C2412_IISCON_RXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE ;
con & = ~ S3C2412_IISCON_RXDMA_PAUSE ;
con & = ~ S3C2412_IISCON_RXCH_PAUSE ;
switch ( mod & S3C2412_IISMOD_MODE_MASK ) {
case S3C2412_IISMOD_MODE_TXRX :
case S3C2412_IISMOD_MODE_RXONLY :
/* do nothing, we are in the right mode */
break ;
case S3C2412_IISMOD_MODE_TXONLY :
mod & = ~ S3C2412_IISMOD_MODE_MASK ;
mod | = S3C2412_IISMOD_MODE_TXRX ;
break ;
default :
2009-04-30 16:21:52 +04:00
dev_err ( i2s - > dev , " RXEN: Invalid MODE %x in IISMOD \n " ,
mod & S3C2412_IISMOD_MODE_MASK ) ;
2009-03-04 03:49:30 +03:00
}
writel ( mod , regs + S3C2412_IISMOD ) ;
writel ( con , regs + S3C2412_IISCON ) ;
} else {
/* See txctrl notes on FIFOs. */
con & = ~ S3C2412_IISCON_RXDMA_ACTIVE ;
con | = S3C2412_IISCON_RXDMA_PAUSE ;
con | = S3C2412_IISCON_RXCH_PAUSE ;
switch ( mod & S3C2412_IISMOD_MODE_MASK ) {
case S3C2412_IISMOD_MODE_RXONLY :
con & = ~ S3C2412_IISCON_IIS_ACTIVE ;
mod & = ~ S3C2412_IISMOD_MODE_MASK ;
break ;
case S3C2412_IISMOD_MODE_TXRX :
mod & = ~ S3C2412_IISMOD_MODE_MASK ;
mod | = S3C2412_IISMOD_MODE_TXONLY ;
break ;
default :
2009-04-30 16:21:52 +04:00
dev_err ( i2s - > dev , " RXDIS: Invalid MODE %x in IISMOD \n " ,
mod & S3C2412_IISMOD_MODE_MASK ) ;
2009-03-04 03:49:30 +03:00
}
writel ( con , regs + S3C2412_IISCON ) ;
writel ( mod , regs + S3C2412_IISMOD ) ;
}
fic = readl ( regs + S3C2412_IISFIC ) ;
2009-03-06 21:04:34 +03:00
pr_debug ( " %s: IIS: CON=%x MOD=%x FIC=%x \n " , __func__ , con , mod , fic ) ;
2009-03-04 03:49:30 +03:00
}
2009-09-15 14:02:37 +04:00
# define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
2009-03-04 03:49:30 +03:00
/*
* Wait for the LR signal to allow synchronisation to the L / R clock
* from the codec . May only be needed for slave mode .
*/
static int s3c2412_snd_lrsync ( struct s3c_i2sv2_info * i2s )
{
u32 iiscon ;
2009-09-15 14:02:37 +04:00
unsigned long loops = msecs_to_loops ( 5 ) ;
2009-03-04 03:49:30 +03:00
2009-03-06 21:04:34 +03:00
pr_debug ( " Entered %s \n " , __func__ ) ;
2009-03-04 03:49:30 +03:00
2009-09-15 14:02:37 +04:00
while ( - - loops ) {
2009-03-04 03:49:30 +03:00
iiscon = readl ( i2s - > regs + S3C2412_IISCON ) ;
if ( iiscon & S3C2412_IISCON_LRINDEX )
break ;
2009-09-15 14:02:37 +04:00
cpu_relax ( ) ;
}
if ( ! loops ) {
printk ( KERN_ERR " %s: timeout \n " , __func__ ) ;
return - ETIMEDOUT ;
2009-03-04 03:49:30 +03:00
}
return 0 ;
}
/*
* Set S3C2412 I2S DAI format
*/
static int s3c2412_i2s_set_fmt ( struct snd_soc_dai * cpu_dai ,
unsigned int fmt )
{
struct s3c_i2sv2_info * i2s = to_info ( cpu_dai ) ;
u32 iismod ;
2009-03-06 21:04:34 +03:00
pr_debug ( " Entered %s \n " , __func__ ) ;
2009-03-04 03:49:30 +03:00
iismod = readl ( i2s - > regs + S3C2412_IISMOD ) ;
2009-03-06 21:04:34 +03:00
pr_debug ( " hw_params r: IISMOD: %x \n " , iismod ) ;
2009-03-04 03:49:30 +03:00
switch ( fmt & SND_SOC_DAIFMT_MASTER_MASK ) {
case SND_SOC_DAIFMT_CBM_CFM :
i2s - > master = 0 ;
2010-04-27 10:56:27 +04:00
iismod | = S3C2412_IISMOD_SLAVE ;
2009-03-04 03:49:30 +03:00
break ;
case SND_SOC_DAIFMT_CBS_CFS :
i2s - > master = 1 ;
2010-04-27 10:56:27 +04:00
iismod & = ~ S3C2412_IISMOD_SLAVE ;
2009-03-04 03:49:30 +03:00
break ;
default :
2009-04-30 16:14:38 +04:00
pr_err ( " unknwon master/slave format \n " ) ;
2009-03-04 03:49:30 +03:00
return - EINVAL ;
}
iismod & = ~ S3C2412_IISMOD_SDF_MASK ;
switch ( fmt & SND_SOC_DAIFMT_FORMAT_MASK ) {
case SND_SOC_DAIFMT_RIGHT_J :
2009-09-15 14:02:38 +04:00
iismod | = S3C2412_IISMOD_LR_RLOW ;
2009-03-04 03:49:30 +03:00
iismod | = S3C2412_IISMOD_SDF_MSB ;
break ;
case SND_SOC_DAIFMT_LEFT_J :
2009-09-15 14:02:38 +04:00
iismod | = S3C2412_IISMOD_LR_RLOW ;
2009-03-04 03:49:30 +03:00
iismod | = S3C2412_IISMOD_SDF_LSB ;
break ;
case SND_SOC_DAIFMT_I2S :
2009-09-15 14:02:38 +04:00
iismod & = ~ S3C2412_IISMOD_LR_RLOW ;
2009-03-04 03:49:30 +03:00
iismod | = S3C2412_IISMOD_SDF_IIS ;
break ;
default :
2009-04-30 16:14:38 +04:00
pr_err ( " Unknown data format \n " ) ;
2009-03-04 03:49:30 +03:00
return - EINVAL ;
}
writel ( iismod , i2s - > regs + S3C2412_IISMOD ) ;
2009-03-06 21:04:34 +03:00
pr_debug ( " hw_params w: IISMOD: %x \n " , iismod ) ;
2009-03-04 03:49:30 +03:00
return 0 ;
}
2010-03-10 10:48:58 +03:00
static int s3c_i2sv2_hw_params ( struct snd_pcm_substream * substream ,
2009-03-04 03:49:30 +03:00
struct snd_pcm_hw_params * params ,
2010-03-17 23:15:21 +03:00
struct snd_soc_dai * dai )
2009-03-04 03:49:30 +03:00
{
2010-03-17 23:15:21 +03:00
struct s3c_i2sv2_info * i2s = to_info ( dai ) ;
2010-03-19 17:52:55 +03:00
struct s3c_dma_params * dma_data ;
2009-03-04 03:49:30 +03:00
u32 iismod ;
2009-03-06 21:04:34 +03:00
pr_debug ( " Entered %s \n " , __func__ ) ;
2009-03-04 03:49:30 +03:00
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
2010-03-19 17:52:55 +03:00
dma_data = i2s - > dma_playback ;
2009-03-04 03:49:30 +03:00
else
2010-03-19 17:52:55 +03:00
dma_data = i2s - > dma_capture ;
2010-03-17 23:15:21 +03:00
snd_soc_dai_set_dma_data ( dai , substream , dma_data ) ;
2009-03-04 03:49:30 +03:00
/* Working copies of register */
iismod = readl ( i2s - > regs + S3C2412_IISMOD ) ;
2009-03-06 21:04:34 +03:00
pr_debug ( " %s: r: IISMOD: %x \n " , __func__ , iismod ) ;
2009-03-04 03:49:30 +03:00
2010-03-10 10:48:52 +03:00
iismod & = ~ S3C64XX_IISMOD_BLC_MASK ;
2009-04-29 23:29:25 +04:00
/* Sample size */
switch ( params_format ( params ) ) {
case SNDRV_PCM_FORMAT_S8 :
2010-03-10 10:48:52 +03:00
iismod | = S3C64XX_IISMOD_BLC_8BIT ;
2009-04-29 23:29:25 +04:00
break ;
case SNDRV_PCM_FORMAT_S16_LE :
break ;
case SNDRV_PCM_FORMAT_S24_LE :
2010-03-10 10:48:52 +03:00
iismod | = S3C64XX_IISMOD_BLC_24BIT ;
2009-04-29 23:29:25 +04:00
break ;
}
2009-03-04 03:49:30 +03:00
writel ( iismod , i2s - > regs + S3C2412_IISMOD ) ;
2009-03-06 21:04:34 +03:00
pr_debug ( " %s: w: IISMOD: %x \n " , __func__ , iismod ) ;
2010-04-27 10:57:05 +04:00
return 0 ;
}
static int s3c_i2sv2_set_sysclk ( struct snd_soc_dai * cpu_dai ,
int clk_id , unsigned int freq , int dir )
{
struct s3c_i2sv2_info * i2s = to_info ( cpu_dai ) ;
u32 iismod = readl ( i2s - > regs + S3C2412_IISMOD ) ;
pr_debug ( " Entered %s \n " , __func__ ) ;
pr_debug ( " %s r: IISMOD: %x \n " , __func__ , iismod ) ;
switch ( clk_id ) {
case S3C_I2SV2_CLKSRC_PCLK :
iismod & = ~ S3C2412_IISMOD_IMS_SYSMUX ;
break ;
case S3C_I2SV2_CLKSRC_AUDIOBUS :
iismod | = S3C2412_IISMOD_IMS_SYSMUX ;
break ;
case S3C_I2SV2_CLKSRC_CDCLK :
/* Error if controller doesn't have the CDCLKCON bit */
if ( ! ( i2s - > feature & S3C_FEATURE_CDCLKCON ) )
return - EINVAL ;
switch ( dir ) {
case SND_SOC_CLOCK_IN :
iismod | = S3C64XX_IISMOD_CDCLKCON ;
break ;
case SND_SOC_CLOCK_OUT :
iismod & = ~ S3C64XX_IISMOD_CDCLKCON ;
break ;
default :
return - EINVAL ;
}
break ;
default :
return - EINVAL ;
}
writel ( iismod , i2s - > regs + S3C2412_IISMOD ) ;
pr_debug ( " %s w: IISMOD: %x \n " , __func__ , iismod ) ;
2009-03-04 03:49:30 +03:00
return 0 ;
}
static int s3c2412_i2s_trigger ( struct snd_pcm_substream * substream , int cmd ,
struct snd_soc_dai * dai )
{
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
2010-03-17 23:15:21 +03:00
struct s3c_i2sv2_info * i2s = to_info ( rtd - > cpu_dai ) ;
2009-03-04 03:49:30 +03:00
int capture = ( substream - > stream = = SNDRV_PCM_STREAM_CAPTURE ) ;
unsigned long irqs ;
int ret = 0 ;
2010-03-19 17:52:55 +03:00
struct s3c_dma_params * dma_data =
2010-03-17 23:15:21 +03:00
snd_soc_dai_get_dma_data ( rtd - > cpu_dai , substream ) ;
2009-03-04 03:49:30 +03:00
2009-03-06 21:04:34 +03:00
pr_debug ( " Entered %s \n " , __func__ ) ;
2009-03-04 03:49:30 +03:00
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
/* On start, ensure that the FIFOs are cleared and reset. */
writel ( capture ? S3C2412_IISFIC_RXFLUSH : S3C2412_IISFIC_TXFLUSH ,
i2s - > regs + S3C2412_IISFIC ) ;
/* clear again, just in case */
writel ( 0x0 , i2s - > regs + S3C2412_IISFIC ) ;
case SNDRV_PCM_TRIGGER_RESUME :
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
if ( ! i2s - > master ) {
ret = s3c2412_snd_lrsync ( i2s ) ;
if ( ret )
goto exit_err ;
}
local_irq_save ( irqs ) ;
if ( capture )
s3c2412_snd_rxctrl ( i2s , 1 ) ;
else
s3c2412_snd_txctrl ( i2s , 1 ) ;
local_irq_restore ( irqs ) ;
ASoC: S3C platform: Fix s3c2410_dma_started() called at improper time
s3c24xx dma has the auto reload feature, when the the trnasfer is done,
CURR_TC(DSTAT[19:0], current value of transfer count) reaches 0, and DMA
ACK becomes 1, and then, TC(DCON[19:0]) will be loaded into CURR_TC. So
the transmission is repeated.
IRQ is issued while auto reload occurs. We change the DISRC and
DCON[19:0] in the ISR, but at this time, the auto reload has been
performed already. The first block is being re-transmitted by the DMA.
So we need rewrite the DISRC and DCON[19:0] for the next block
immediatly after the this block has been started to be transported.
The function s3c2410_dma_started() is for this perpose, which is called
in the form of "s3c2410_dma_ctrl(prtd->params->channel,
S3C2410_DMAOP_STARTED);" in s3c24xx_pcm_trigger().
But it is not correct. DMA transmission won't start until DMA REQ signal
arrived, it is the time s3c24xx_snd_txctrl(1) or s3c24xx_snd_rxctrl(1)
is called in s3c24xx_i2s_trigger().
In the current framework, s3c24xx_pcm_trigger() is always called before
s3c24xx_pcm_trigger(). So the s3c2410_dma_started() should be called in
s3c24xx_pcm_trigger() after s3c24xx_snd_txctrl(1) or
s3c24xx_snd_rxctrl(1) is called in this function.
However, s3c2410_dma_started() is dma related, to call this function we
should provide the channel number, which is given by
substream->runtime->private_data->params->channel. The private_data
points to a struct s3c24xx_runtime_data object, which is define in
s3c24xx_pcm.c, so s3c2410_dma_started() can't be called in s3c24xx_i2s.c
Fix this by moving the call to signal the DMA started to the DAI
drivers.
Signed-off-by: Shine Liu <liuxian@redflag-linux.com>
Signed-off-by: Shine Liu <shinel@foxmail.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
2009-08-25 16:05:50 +04:00
/*
* Load the next buffer to DMA to meet the reqirement
* of the auto reload mechanism of S3C24XX .
* This call won ' t bother S3C64XX .
*/
2010-03-19 17:52:55 +03:00
s3c2410_dma_ctrl ( dma_data - > channel , S3C2410_DMAOP_STARTED ) ;
ASoC: S3C platform: Fix s3c2410_dma_started() called at improper time
s3c24xx dma has the auto reload feature, when the the trnasfer is done,
CURR_TC(DSTAT[19:0], current value of transfer count) reaches 0, and DMA
ACK becomes 1, and then, TC(DCON[19:0]) will be loaded into CURR_TC. So
the transmission is repeated.
IRQ is issued while auto reload occurs. We change the DISRC and
DCON[19:0] in the ISR, but at this time, the auto reload has been
performed already. The first block is being re-transmitted by the DMA.
So we need rewrite the DISRC and DCON[19:0] for the next block
immediatly after the this block has been started to be transported.
The function s3c2410_dma_started() is for this perpose, which is called
in the form of "s3c2410_dma_ctrl(prtd->params->channel,
S3C2410_DMAOP_STARTED);" in s3c24xx_pcm_trigger().
But it is not correct. DMA transmission won't start until DMA REQ signal
arrived, it is the time s3c24xx_snd_txctrl(1) or s3c24xx_snd_rxctrl(1)
is called in s3c24xx_i2s_trigger().
In the current framework, s3c24xx_pcm_trigger() is always called before
s3c24xx_pcm_trigger(). So the s3c2410_dma_started() should be called in
s3c24xx_pcm_trigger() after s3c24xx_snd_txctrl(1) or
s3c24xx_snd_rxctrl(1) is called in this function.
However, s3c2410_dma_started() is dma related, to call this function we
should provide the channel number, which is given by
substream->runtime->private_data->params->channel. The private_data
points to a struct s3c24xx_runtime_data object, which is define in
s3c24xx_pcm.c, so s3c2410_dma_started() can't be called in s3c24xx_i2s.c
Fix this by moving the call to signal the DMA started to the DAI
drivers.
Signed-off-by: Shine Liu <liuxian@redflag-linux.com>
Signed-off-by: Shine Liu <shinel@foxmail.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
2009-08-25 16:05:50 +04:00
2009-03-04 03:49:30 +03:00
break ;
case SNDRV_PCM_TRIGGER_STOP :
case SNDRV_PCM_TRIGGER_SUSPEND :
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
local_irq_save ( irqs ) ;
if ( capture )
s3c2412_snd_rxctrl ( i2s , 0 ) ;
else
s3c2412_snd_txctrl ( i2s , 0 ) ;
local_irq_restore ( irqs ) ;
break ;
default :
ret = - EINVAL ;
break ;
}
exit_err :
return ret ;
}
/*
* Set S3C2412 Clock dividers
*/
static int s3c2412_i2s_set_clkdiv ( struct snd_soc_dai * cpu_dai ,
int div_id , int div )
{
struct s3c_i2sv2_info * i2s = to_info ( cpu_dai ) ;
u32 reg ;
2009-03-06 21:04:34 +03:00
pr_debug ( " %s(%p, %d, %d) \n " , __func__ , cpu_dai , div_id , div ) ;
2009-03-04 03:49:30 +03:00
switch ( div_id ) {
case S3C_I2SV2_DIV_BCLK :
2010-03-10 10:48:51 +03:00
switch ( div ) {
case 16 :
div = S3C2412_IISMOD_BCLK_16FS ;
break ;
2009-09-15 14:02:38 +04:00
2010-03-10 10:48:51 +03:00
case 32 :
div = S3C2412_IISMOD_BCLK_32FS ;
break ;
2009-09-15 14:02:38 +04:00
2010-03-10 10:48:51 +03:00
case 24 :
div = S3C2412_IISMOD_BCLK_24FS ;
break ;
2009-09-15 14:02:38 +04:00
2010-03-10 10:48:51 +03:00
case 48 :
div = S3C2412_IISMOD_BCLK_48FS ;
break ;
2009-09-15 14:02:38 +04:00
2010-03-10 10:48:51 +03:00
default :
return - EINVAL ;
2009-09-15 14:02:38 +04:00
}
2009-03-04 03:49:30 +03:00
reg = readl ( i2s - > regs + S3C2412_IISMOD ) ;
reg & = ~ S3C2412_IISMOD_BCLK_MASK ;
writel ( reg | div , i2s - > regs + S3C2412_IISMOD ) ;
2009-03-06 21:04:34 +03:00
pr_debug ( " %s: MOD=%08x \n " , __func__ , readl ( i2s - > regs + S3C2412_IISMOD ) ) ;
2009-03-04 03:49:30 +03:00
break ;
case S3C_I2SV2_DIV_RCLK :
2010-03-10 10:48:51 +03:00
switch ( div ) {
case 256 :
div = S3C2412_IISMOD_RCLK_256FS ;
break ;
2009-03-04 03:49:30 +03:00
2010-03-10 10:48:51 +03:00
case 384 :
div = S3C2412_IISMOD_RCLK_384FS ;
break ;
2009-03-04 03:49:30 +03:00
2010-03-10 10:48:51 +03:00
case 512 :
div = S3C2412_IISMOD_RCLK_512FS ;
break ;
2009-03-04 03:49:30 +03:00
2010-03-10 10:48:51 +03:00
case 768 :
div = S3C2412_IISMOD_RCLK_768FS ;
break ;
2009-03-04 03:49:30 +03:00
2010-03-10 10:48:51 +03:00
default :
return - EINVAL ;
2009-03-04 03:49:30 +03:00
}
reg = readl ( i2s - > regs + S3C2412_IISMOD ) ;
reg & = ~ S3C2412_IISMOD_RCLK_MASK ;
writel ( reg | div , i2s - > regs + S3C2412_IISMOD ) ;
2009-03-06 21:04:34 +03:00
pr_debug ( " %s: MOD=%08x \n " , __func__ , readl ( i2s - > regs + S3C2412_IISMOD ) ) ;
2009-03-04 03:49:30 +03:00
break ;
case S3C_I2SV2_DIV_PRESCALER :
if ( div > = 0 ) {
writel ( ( div < < 8 ) | S3C2412_IISPSR_PSREN ,
i2s - > regs + S3C2412_IISPSR ) ;
} else {
writel ( 0x0 , i2s - > regs + S3C2412_IISPSR ) ;
}
2009-03-06 21:04:34 +03:00
pr_debug ( " %s: PSR=%08x \n " , __func__ , readl ( i2s - > regs + S3C2412_IISPSR ) ) ;
2009-03-04 03:49:30 +03:00
break ;
default :
return - EINVAL ;
}
return 0 ;
}
2010-03-04 17:54:40 +03:00
static snd_pcm_sframes_t s3c2412_i2s_delay ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
struct s3c_i2sv2_info * i2s = to_info ( dai ) ;
u32 reg = readl ( i2s - > regs + S3C2412_IISFIC ) ;
snd_pcm_sframes_t delay ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
delay = S3C2412_IISFIC_TXCOUNT ( reg ) ;
else
delay = S3C2412_IISFIC_RXCOUNT ( reg ) ;
return delay ;
}
2010-04-27 10:56:45 +04:00
struct clk * s3c_i2sv2_get_clock ( struct snd_soc_dai * cpu_dai )
{
struct s3c_i2sv2_info * i2s = to_info ( cpu_dai ) ;
u32 iismod = readl ( i2s - > regs + S3C2412_IISMOD ) ;
if ( iismod & S3C2412_IISMOD_IMS_SYSMUX )
return i2s - > iis_cclk ;
else
return i2s - > iis_pclk ;
}
EXPORT_SYMBOL_GPL ( s3c_i2sv2_get_clock ) ;
2009-03-04 03:49:30 +03:00
/* default table of all avaialable root fs divisors */
static unsigned int iis_fs_tab [ ] = { 256 , 512 , 384 , 768 } ;
2009-04-16 13:32:23 +04:00
int s3c_i2sv2_iis_calc_rate ( struct s3c_i2sv2_rate_calc * info ,
unsigned int * fstab ,
unsigned int rate , struct clk * clk )
2009-03-04 03:49:30 +03:00
{
unsigned long clkrate = clk_get_rate ( clk ) ;
unsigned int div ;
unsigned int fsclk ;
unsigned int actual ;
unsigned int fs ;
unsigned int fsdiv ;
signed int deviation = 0 ;
unsigned int best_fs = 0 ;
unsigned int best_div = 0 ;
unsigned int best_rate = 0 ;
unsigned int best_deviation = INT_MAX ;
2009-04-30 16:13:55 +04:00
pr_debug ( " Input clock rate %ldHz \n " , clkrate ) ;
2009-03-04 03:49:30 +03:00
if ( fstab = = NULL )
fstab = iis_fs_tab ;
for ( fs = 0 ; fs < ARRAY_SIZE ( iis_fs_tab ) ; fs + + ) {
fsdiv = iis_fs_tab [ fs ] ;
fsclk = clkrate / fsdiv ;
div = fsclk / rate ;
if ( ( fsclk % rate ) > ( rate / 2 ) )
div + + ;
if ( div < = 1 )
continue ;
actual = clkrate / ( fsdiv * div ) ;
deviation = actual - rate ;
2009-05-28 04:08:39 +04:00
printk ( KERN_DEBUG " %ufs: div %u => result %u, deviation %d \n " ,
2009-03-04 03:49:30 +03:00
fsdiv , div , actual , deviation ) ;
deviation = abs ( deviation ) ;
if ( deviation < best_deviation ) {
best_fs = fsdiv ;
best_div = div ;
best_rate = actual ;
best_deviation = deviation ;
}
if ( deviation = = 0 )
break ;
}
2009-05-28 04:08:39 +04:00
printk ( KERN_DEBUG " best: fs=%u, div=%u, rate=%u \n " ,
2009-03-04 03:49:30 +03:00
best_fs , best_div , best_rate ) ;
info - > fs_div = best_fs ;
info - > clk_div = best_div ;
return 0 ;
}
2009-04-16 13:32:23 +04:00
EXPORT_SYMBOL_GPL ( s3c_i2sv2_iis_calc_rate ) ;
2009-03-04 03:49:30 +03:00
2010-03-17 23:15:21 +03:00
int s3c_i2sv2_probe ( struct snd_soc_dai * dai ,
2009-03-04 03:49:30 +03:00
struct s3c_i2sv2_info * i2s ,
unsigned long base )
{
2010-03-17 23:15:21 +03:00
struct device * dev = dai - > dev ;
2009-04-30 16:13:14 +04:00
unsigned int iismod ;
2009-03-04 03:49:30 +03:00
i2s - > dev = dev ;
/* record our i2s structure for later use in the callbacks */
2010-03-17 23:15:21 +03:00
snd_soc_dai_set_drvdata ( dai , i2s ) ;
2009-04-30 16:09:33 +04:00
2009-03-04 03:49:30 +03:00
i2s - > regs = ioremap ( base , 0x100 ) ;
if ( i2s - > regs = = NULL ) {
dev_err ( dev , " cannot ioremap registers \n " ) ;
return - ENXIO ;
}
i2s - > iis_pclk = clk_get ( dev , " iis " ) ;
2009-09-15 14:02:38 +04:00
if ( IS_ERR ( i2s - > iis_pclk ) ) {
2009-03-06 21:13:43 +03:00
dev_err ( dev , " failed to get iis_clock \n " ) ;
2009-03-04 03:49:30 +03:00
iounmap ( i2s - > regs ) ;
return - ENOENT ;
}
clk_enable ( i2s - > iis_pclk ) ;
2009-04-30 16:13:14 +04:00
/* Mark ourselves as in TXRX mode so we can run through our cleanup
* process without warnings . */
iismod = readl ( i2s - > regs + S3C2412_IISMOD ) ;
iismod | = S3C2412_IISMOD_MODE_TXRX ;
writel ( iismod , i2s - > regs + S3C2412_IISMOD ) ;
2009-03-04 03:49:30 +03:00
s3c2412_snd_txctrl ( i2s , 0 ) ;
s3c2412_snd_rxctrl ( i2s , 0 ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( s3c_i2sv2_probe ) ;
# ifdef CONFIG_PM
static int s3c2412_i2s_suspend ( struct snd_soc_dai * dai )
{
struct s3c_i2sv2_info * i2s = to_info ( dai ) ;
u32 iismod ;
if ( dai - > 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_warning ( " %s: RXDMA active? \n " , __func__ ) ;
if ( iismod & S3C2412_IISCON_TXDMA_ACTIVE )
pr_warning ( " %s: TXDMA active? \n " , __func__ ) ;
if ( iismod & S3C2412_IISCON_IIS_ACTIVE )
pr_warning ( " %s: IIS active \n " , __func__ ) ;
}
return 0 ;
}
static int s3c2412_i2s_resume ( struct snd_soc_dai * dai )
{
struct s3c_i2sv2_info * i2s = to_info ( dai ) ;
pr_info ( " dai_active %d, IISMOD %08x, IISCON %08x \n " ,
dai - > active , i2s - > suspend_iismod , i2s - > suspend_iiscon ) ;
if ( dai - > 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
2010-03-17 23:15:21 +03:00
int s3c_i2sv2_register_dai ( struct device * dev , int id ,
struct snd_soc_dai_driver * drv )
2009-03-04 03:49:30 +03:00
{
2010-03-17 23:15:21 +03:00
struct snd_soc_dai_ops * ops = drv - > ops ;
2009-04-16 13:32:22 +04:00
ops - > trigger = s3c2412_i2s_trigger ;
2010-03-10 10:48:58 +03:00
if ( ! ops - > hw_params )
ops - > hw_params = s3c_i2sv2_hw_params ;
2009-04-16 13:32:22 +04:00
ops - > set_fmt = s3c2412_i2s_set_fmt ;
ops - > set_clkdiv = s3c2412_i2s_set_clkdiv ;
2010-04-27 10:57:05 +04:00
ops - > set_sysclk = s3c_i2sv2_set_sysclk ;
2009-03-04 03:49:30 +03:00
2010-03-04 17:54:40 +03:00
/* Allow overriding by (for example) IISv4 */
if ( ! ops - > delay )
2010-03-10 10:48:50 +03:00
ops - > delay = s3c2412_i2s_delay ;
2010-03-04 17:54:40 +03:00
2010-03-17 23:15:21 +03:00
drv - > suspend = s3c2412_i2s_suspend ;
drv - > resume = s3c2412_i2s_resume ;
2009-03-04 03:49:30 +03:00
2010-07-21 03:06:56 +04:00
return snd_soc_register_dai ( dev , drv ) ;
2009-03-04 03:49:30 +03:00
}
EXPORT_SYMBOL_GPL ( s3c_i2sv2_register_dai ) ;
2009-04-23 18:43:45 +04:00
MODULE_LICENSE ( " GPL " ) ;