2008-02-18 11:40:22 +01:00
/*
* ALSA SoC I2S ( McBSP ) Audio Layer for TI DAVINCI processor
*
2008-09-29 23:14:11 +04:00
* Author : Vladimir Barinov , < vbarinov @ embeddedalley . com >
2008-02-18 11:40:22 +01:00
* Copyright : ( C ) 2007 MontaVista Software , Inc . , < source @ mvista . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/init.h>
# include <linux/module.h>
# include <linux/device.h>
# include <linux/delay.h>
# include <linux/io.h>
# include <linux/clk.h>
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/initval.h>
# include <sound/soc.h>
2009-07-08 16:54:51 +01:00
# include <mach/asp.h>
2008-02-18 11:40:22 +01:00
# include "davinci-pcm.h"
2009-05-14 12:47:42 -07:00
/*
* NOTE : terminology here is confusing .
*
* - This driver supports the " Audio Serial Port " ( ASP ) ,
* found on dm6446 , dm355 , and other DaVinci chips .
*
* - But it labels it a " Multi-channel Buffered Serial Port "
* ( McBSP ) as on older chips like the dm642 . . . which was
* backward - compatible , possibly explaining that confusion .
*
* - OMAP chips have a controller called McBSP , which is
* incompatible with the DaVinci flavor of McBSP .
*
* - Newer DaVinci chips have a controller called McASP ,
* incompatible with ASP and with either McBSP .
*
* In short : this uses ASP to implement I2S , not McBSP .
* And it won ' t be the only DaVinci implemention of I2S .
*/
2008-02-18 11:40:22 +01:00
# define DAVINCI_MCBSP_DRR_REG 0x00
# define DAVINCI_MCBSP_DXR_REG 0x04
# define DAVINCI_MCBSP_SPCR_REG 0x08
# define DAVINCI_MCBSP_RCR_REG 0x0c
# define DAVINCI_MCBSP_XCR_REG 0x10
# define DAVINCI_MCBSP_SRGR_REG 0x14
# define DAVINCI_MCBSP_PCR_REG 0x24
# define DAVINCI_MCBSP_SPCR_RRST (1 << 0)
# define DAVINCI_MCBSP_SPCR_RINTM(v) ((v) << 4)
# define DAVINCI_MCBSP_SPCR_XRST (1 << 16)
# define DAVINCI_MCBSP_SPCR_XINTM(v) ((v) << 20)
# define DAVINCI_MCBSP_SPCR_GRST (1 << 22)
# define DAVINCI_MCBSP_SPCR_FRST (1 << 23)
# define DAVINCI_MCBSP_SPCR_FREE (1 << 25)
# define DAVINCI_MCBSP_RCR_RWDLEN1(v) ((v) << 5)
# define DAVINCI_MCBSP_RCR_RFRLEN1(v) ((v) << 8)
# define DAVINCI_MCBSP_RCR_RDATDLY(v) ((v) << 16)
2009-07-04 19:29:57 -07:00
# define DAVINCI_MCBSP_RCR_RFIG (1 << 18)
2008-02-18 11:40:22 +01:00
# define DAVINCI_MCBSP_RCR_RWDLEN2(v) ((v) << 21)
# define DAVINCI_MCBSP_XCR_XWDLEN1(v) ((v) << 5)
# define DAVINCI_MCBSP_XCR_XFRLEN1(v) ((v) << 8)
# define DAVINCI_MCBSP_XCR_XDATDLY(v) ((v) << 16)
# define DAVINCI_MCBSP_XCR_XFIG (1 << 18)
# define DAVINCI_MCBSP_XCR_XWDLEN2(v) ((v) << 21)
# define DAVINCI_MCBSP_SRGR_FWID(v) ((v) << 8)
# define DAVINCI_MCBSP_SRGR_FPER(v) ((v) << 16)
# define DAVINCI_MCBSP_SRGR_FSGM (1 << 28)
# define DAVINCI_MCBSP_PCR_CLKRP (1 << 0)
# define DAVINCI_MCBSP_PCR_CLKXP (1 << 1)
# define DAVINCI_MCBSP_PCR_FSRP (1 << 2)
# define DAVINCI_MCBSP_PCR_FSXP (1 << 3)
2008-11-08 13:26:09 -05:00
# define DAVINCI_MCBSP_PCR_SCLKME (1 << 7)
2008-02-18 11:40:22 +01:00
# define DAVINCI_MCBSP_PCR_CLKRM (1 << 8)
# define DAVINCI_MCBSP_PCR_CLKXM (1 << 9)
# define DAVINCI_MCBSP_PCR_FSRM (1 << 10)
# define DAVINCI_MCBSP_PCR_FSXM (1 << 11)
enum {
DAVINCI_MCBSP_WORD_8 = 0 ,
DAVINCI_MCBSP_WORD_12 ,
DAVINCI_MCBSP_WORD_16 ,
DAVINCI_MCBSP_WORD_20 ,
DAVINCI_MCBSP_WORD_24 ,
DAVINCI_MCBSP_WORD_32 ,
} ;
2009-11-18 17:49:51 -07:00
static const unsigned char data_type [ SNDRV_PCM_FORMAT_S32_LE + 1 ] = {
[ SNDRV_PCM_FORMAT_S8 ] = 1 ,
[ SNDRV_PCM_FORMAT_S16_LE ] = 2 ,
[ SNDRV_PCM_FORMAT_S32_LE ] = 4 ,
} ;
static const unsigned char asp_word_length [ SNDRV_PCM_FORMAT_S32_LE + 1 ] = {
[ SNDRV_PCM_FORMAT_S8 ] = DAVINCI_MCBSP_WORD_8 ,
[ SNDRV_PCM_FORMAT_S16_LE ] = DAVINCI_MCBSP_WORD_16 ,
[ SNDRV_PCM_FORMAT_S32_LE ] = DAVINCI_MCBSP_WORD_32 ,
} ;
static const unsigned char double_fmt [ SNDRV_PCM_FORMAT_S32_LE + 1 ] = {
[ SNDRV_PCM_FORMAT_S8 ] = SNDRV_PCM_FORMAT_S16_LE ,
[ SNDRV_PCM_FORMAT_S16_LE ] = SNDRV_PCM_FORMAT_S32_LE ,
} ;
2008-02-18 11:40:22 +01:00
struct davinci_mcbsp_dev {
2009-09-11 14:29:03 -07:00
struct davinci_pcm_dma_params dma_params [ 2 ] ;
2008-02-18 11:40:22 +01:00
void __iomem * base ;
2009-07-04 19:29:57 -07:00
# define MOD_DSP_A 0
# define MOD_DSP_B 1
int mode ;
2009-07-04 19:29:52 -07:00
u32 pcr ;
2008-02-18 11:40:22 +01:00
struct clk * clk ;
2009-11-18 17:49:51 -07:00
/*
* Combining both channels into 1 element will at least double the
* amount of time between servicing the dma channel , increase
* effiency , and reduce the chance of overrun / underrun . But ,
* it will result in the left & right channels being swapped .
*
* If relabeling the left and right channels is not possible ,
* you may want to let the codec know to swap them back .
*
* It may allow x10 the amount of time to service dma requests ,
* if the codec is master and is using an unnecessarily fast bit clock
* ( ie . tlvaic23b ) , independent of the sample rate . So , having an
* entire frame at once means it can be serviced at the sample rate
* instead of the bit clock rate .
*
* In the now unlikely case that an underrun still
* occurs , both the left and right samples will be repeated
* so that no pops are heard , and the left and right channels
* won ' t end up being swapped because of the underrun .
*/
unsigned enable_channel_combine : 1 ;
2008-02-18 11:40:22 +01:00
} ;
static inline void davinci_mcbsp_write_reg ( struct davinci_mcbsp_dev * dev ,
int reg , u32 val )
{
__raw_writel ( val , dev - > base + reg ) ;
}
static inline u32 davinci_mcbsp_read_reg ( struct davinci_mcbsp_dev * dev , int reg )
{
return __raw_readl ( dev - > base + reg ) ;
}
2009-07-04 19:29:52 -07:00
static void toggle_clock ( struct davinci_mcbsp_dev * dev , int playback )
{
u32 m = playback ? DAVINCI_MCBSP_PCR_CLKXP : DAVINCI_MCBSP_PCR_CLKRP ;
/* The clock needs to toggle to complete reset.
* So , fake it by toggling the clk polarity .
*/
davinci_mcbsp_write_reg ( dev , DAVINCI_MCBSP_PCR_REG , dev - > pcr ^ m ) ;
davinci_mcbsp_write_reg ( dev , DAVINCI_MCBSP_PCR_REG , dev - > pcr ) ;
}
2009-07-04 19:29:53 -07:00
static void davinci_mcbsp_start ( struct davinci_mcbsp_dev * dev ,
struct snd_pcm_substream * substream )
2008-02-18 11:40:22 +01:00
{
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
2008-11-12 10:26:31 +05:30
struct snd_soc_device * socdev = rtd - > socdev ;
2008-12-02 16:01:14 +00:00
struct snd_soc_platform * platform = socdev - > card - > platform ;
2009-07-04 19:29:52 -07:00
int playback = ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) ;
2009-07-04 19:29:51 -07:00
u32 spcr ;
2009-07-04 19:29:52 -07:00
u32 mask = playback ? DAVINCI_MCBSP_SPCR_XRST : DAVINCI_MCBSP_SPCR_RRST ;
2009-07-04 19:29:51 -07:00
spcr = davinci_mcbsp_read_reg ( dev , DAVINCI_MCBSP_SPCR_REG ) ;
2009-07-04 19:29:52 -07:00
if ( spcr & mask ) {
/* start off disabled */
davinci_mcbsp_write_reg ( dev , DAVINCI_MCBSP_SPCR_REG ,
spcr & ~ mask ) ;
toggle_clock ( dev , playback ) ;
}
2009-07-04 19:29:55 -07:00
if ( dev - > pcr & ( DAVINCI_MCBSP_PCR_FSXM | DAVINCI_MCBSP_PCR_FSRM |
DAVINCI_MCBSP_PCR_CLKXM | DAVINCI_MCBSP_PCR_CLKRM ) ) {
/* Start the sample generator */
spcr | = DAVINCI_MCBSP_SPCR_GRST ;
davinci_mcbsp_write_reg ( dev , DAVINCI_MCBSP_SPCR_REG , spcr ) ;
}
2008-11-12 10:26:31 +05:30
2009-07-04 19:29:55 -07:00
if ( playback ) {
2008-11-12 10:26:31 +05:30
/* Stop the DMA to avoid data loss */
/* while the transmitter is out of reset to handle XSYNCERR */
if ( platform - > pcm_ops - > trigger ) {
2009-07-04 19:29:54 -07:00
int ret = platform - > pcm_ops - > trigger ( substream ,
2008-11-12 10:26:31 +05:30
SNDRV_PCM_TRIGGER_STOP ) ;
if ( ret < 0 )
printk ( KERN_DEBUG " Playback DMA stop failed \n " ) ;
}
/* Enable the transmitter */
2009-07-04 19:29:51 -07:00
spcr = davinci_mcbsp_read_reg ( dev , DAVINCI_MCBSP_SPCR_REG ) ;
spcr | = DAVINCI_MCBSP_SPCR_XRST ;
davinci_mcbsp_write_reg ( dev , DAVINCI_MCBSP_SPCR_REG , spcr ) ;
2008-11-12 10:26:31 +05:30
/* wait for any unexpected frame sync error to occur */
udelay ( 100 ) ;
/* Disable the transmitter to clear any outstanding XSYNCERR */
2009-07-04 19:29:51 -07:00
spcr = davinci_mcbsp_read_reg ( dev , DAVINCI_MCBSP_SPCR_REG ) ;
spcr & = ~ DAVINCI_MCBSP_SPCR_XRST ;
davinci_mcbsp_write_reg ( dev , DAVINCI_MCBSP_SPCR_REG , spcr ) ;
2009-07-04 19:29:52 -07:00
toggle_clock ( dev , playback ) ;
2008-11-12 10:26:31 +05:30
/* Restart the DMA */
if ( platform - > pcm_ops - > trigger ) {
2009-07-04 19:29:54 -07:00
int ret = platform - > pcm_ops - > trigger ( substream ,
2008-11-12 10:26:31 +05:30
SNDRV_PCM_TRIGGER_START ) ;
if ( ret < 0 )
printk ( KERN_DEBUG " Playback DMA start failed \n " ) ;
}
}
2009-07-04 19:29:55 -07:00
/* Enable transmitter or receiver */
2009-07-04 19:29:51 -07:00
spcr = davinci_mcbsp_read_reg ( dev , DAVINCI_MCBSP_SPCR_REG ) ;
2009-07-04 19:29:55 -07:00
spcr | = mask ;
if ( dev - > pcr & ( DAVINCI_MCBSP_PCR_FSXM | DAVINCI_MCBSP_PCR_FSRM ) ) {
/* Start frame sync */
spcr | = DAVINCI_MCBSP_SPCR_FRST ;
}
2009-07-04 19:29:51 -07:00
davinci_mcbsp_write_reg ( dev , DAVINCI_MCBSP_SPCR_REG , spcr ) ;
2008-02-18 11:40:22 +01:00
}
2009-07-04 19:29:53 -07:00
static void davinci_mcbsp_stop ( struct davinci_mcbsp_dev * dev , int playback )
2008-02-18 11:40:22 +01:00
{
2009-07-04 19:29:51 -07:00
u32 spcr ;
2008-02-18 11:40:22 +01:00
/* Reset transmitter/receiver and sample rate/frame sync generators */
2009-07-04 19:29:51 -07:00
spcr = davinci_mcbsp_read_reg ( dev , DAVINCI_MCBSP_SPCR_REG ) ;
spcr & = ~ ( DAVINCI_MCBSP_SPCR_GRST | DAVINCI_MCBSP_SPCR_FRST ) ;
2009-07-04 19:29:52 -07:00
spcr & = playback ? ~ DAVINCI_MCBSP_SPCR_XRST : ~ DAVINCI_MCBSP_SPCR_RRST ;
2009-07-04 19:29:51 -07:00
davinci_mcbsp_write_reg ( dev , DAVINCI_MCBSP_SPCR_REG , spcr ) ;
2009-07-04 19:29:52 -07:00
toggle_clock ( dev , playback ) ;
2008-02-18 11:40:22 +01:00
}
2008-12-18 12:36:43 -07:00
# define DEFAULT_BITPERSAMPLE 16
2008-07-07 16:07:42 +01:00
static int davinci_i2s_set_dai_fmt ( struct snd_soc_dai * cpu_dai ,
2008-02-18 11:40:22 +01:00
unsigned int fmt )
{
struct davinci_mcbsp_dev * dev = cpu_dai - > private_data ;
2008-12-18 12:36:43 -07:00
unsigned int pcr ;
unsigned int srgr ;
srgr = DAVINCI_MCBSP_SRGR_FSGM |
DAVINCI_MCBSP_SRGR_FPER ( DEFAULT_BITPERSAMPLE * 2 - 1 ) |
DAVINCI_MCBSP_SRGR_FWID ( DEFAULT_BITPERSAMPLE - 1 ) ;
2008-02-18 11:40:22 +01:00
2009-07-04 19:29:57 -07:00
/* set master/slave audio interface */
2008-02-18 11:40:22 +01:00
switch ( fmt & SND_SOC_DAIFMT_MASTER_MASK ) {
case SND_SOC_DAIFMT_CBS_CFS :
2008-12-18 12:36:43 -07:00
/* cpu is master */
pcr = DAVINCI_MCBSP_PCR_FSXM |
DAVINCI_MCBSP_PCR_FSRM |
DAVINCI_MCBSP_PCR_CLKXM |
DAVINCI_MCBSP_PCR_CLKRM ;
2008-02-18 11:40:22 +01:00
break ;
2008-11-08 13:26:09 -05:00
case SND_SOC_DAIFMT_CBM_CFS :
/* McBSP CLKR pin is the input for the Sample Rate Generator.
* McBSP FSR and FSX are driven by the Sample Rate Generator . */
2008-12-18 12:36:43 -07:00
pcr = DAVINCI_MCBSP_PCR_SCLKME |
DAVINCI_MCBSP_PCR_FSXM |
DAVINCI_MCBSP_PCR_FSRM ;
2008-11-08 13:26:09 -05:00
break ;
2008-02-18 11:40:22 +01:00
case SND_SOC_DAIFMT_CBM_CFM :
2008-12-18 12:36:43 -07:00
/* codec is master */
pcr = 0 ;
2008-02-18 11:40:22 +01:00
break ;
default :
2008-12-18 12:36:43 -07:00
printk ( KERN_ERR " %s:bad master \n " , __func__ ) ;
2008-02-18 11:40:22 +01:00
return - EINVAL ;
}
2009-07-04 19:29:57 -07:00
/* interface format */
2008-12-18 12:36:44 -07:00
switch ( fmt & SND_SOC_DAIFMT_FORMAT_MASK ) {
case SND_SOC_DAIFMT_I2S :
2008-12-19 13:05:24 -07:00
/* Davinci doesn't support TRUE I2S, but some codecs will have
* the left and right channels contiguous . This allows
* dsp_a mode to be used with an inverted normal frame clk .
* If your codec is master and does not have contiguous
* channels , then you will have sound on only one channel .
* Try using a different mode , or codec as slave .
*
* The TLV320AIC33 is an example of a codec where this works .
* It has a variable bit clock frequency allowing it to have
* valid data on every bit clock .
*
* The TLV320AIC23 is an example of a codec where this does not
* work . It has a fixed bit clock frequency with progressively
* more empty bit clock slots between channels as the sample
* rate is lowered .
*/
fmt ^ = SND_SOC_DAIFMT_NB_IF ;
case SND_SOC_DAIFMT_DSP_A :
2009-07-04 19:29:57 -07:00
dev - > mode = MOD_DSP_A ;
break ;
case SND_SOC_DAIFMT_DSP_B :
dev - > mode = MOD_DSP_B ;
2008-12-18 12:36:44 -07:00
break ;
default :
printk ( KERN_ERR " %s:bad format \n " , __func__ ) ;
return - EINVAL ;
}
2008-02-18 11:40:22 +01:00
switch ( fmt & SND_SOC_DAIFMT_INV_MASK ) {
2008-12-19 13:05:23 -07:00
case SND_SOC_DAIFMT_NB_NF :
2008-12-18 12:36:41 -07:00
/* CLKRP Receive clock polarity,
* 1 - sampled on rising edge of CLKR
* valid on rising edge
* CLKXP Transmit clock polarity ,
* 1 - clocked on falling edge of CLKX
* valid on rising edge
* FSRP Receive frame sync pol , 0 - active high
* FSXP Transmit frame sync pol , 0 - active high
*/
2008-12-18 12:36:43 -07:00
pcr | = ( DAVINCI_MCBSP_PCR_CLKXP | DAVINCI_MCBSP_PCR_CLKRP ) ;
2008-02-18 11:40:22 +01:00
break ;
2008-12-19 13:05:23 -07:00
case SND_SOC_DAIFMT_IB_IF :
2008-12-18 12:36:41 -07:00
/* CLKRP Receive clock polarity,
* 0 - sampled on falling edge of CLKR
* valid on falling edge
* CLKXP Transmit clock polarity ,
* 0 - clocked on rising edge of CLKX
* valid on falling edge
* FSRP Receive frame sync pol , 1 - active low
* FSXP Transmit frame sync pol , 1 - active low
*/
2008-12-18 12:36:43 -07:00
pcr | = ( DAVINCI_MCBSP_PCR_FSXP | DAVINCI_MCBSP_PCR_FSRP ) ;
2008-02-18 11:40:22 +01:00
break ;
2008-12-19 13:05:23 -07:00
case SND_SOC_DAIFMT_NB_IF :
2008-12-18 12:36:41 -07:00
/* CLKRP Receive clock polarity,
* 1 - sampled on rising edge of CLKR
* valid on rising edge
* CLKXP Transmit clock polarity ,
* 1 - clocked on falling edge of CLKX
* valid on rising edge
* FSRP Receive frame sync pol , 1 - active low
* FSXP Transmit frame sync pol , 1 - active low
*/
2008-12-18 12:36:43 -07:00
pcr | = ( DAVINCI_MCBSP_PCR_CLKXP | DAVINCI_MCBSP_PCR_CLKRP |
DAVINCI_MCBSP_PCR_FSXP | DAVINCI_MCBSP_PCR_FSRP ) ;
2008-02-18 11:40:22 +01:00
break ;
2008-12-19 13:05:23 -07:00
case SND_SOC_DAIFMT_IB_NF :
2008-12-18 12:36:41 -07:00
/* CLKRP Receive clock polarity,
* 0 - sampled on falling edge of CLKR
* valid on falling edge
* CLKXP Transmit clock polarity ,
* 0 - clocked on rising edge of CLKX
* valid on falling edge
* FSRP Receive frame sync pol , 0 - active high
* FSXP Transmit frame sync pol , 0 - active high
*/
2008-02-18 11:40:22 +01:00
break ;
default :
return - EINVAL ;
}
2008-12-18 12:36:43 -07:00
davinci_mcbsp_write_reg ( dev , DAVINCI_MCBSP_SRGR_REG , srgr ) ;
2009-07-04 19:29:52 -07:00
dev - > pcr = pcr ;
2008-12-18 12:36:43 -07:00
davinci_mcbsp_write_reg ( dev , DAVINCI_MCBSP_PCR_REG , pcr ) ;
2008-02-18 11:40:22 +01:00
return 0 ;
}
static int davinci_i2s_hw_params ( struct snd_pcm_substream * substream ,
2008-11-18 22:11:38 +00:00
struct snd_pcm_hw_params * params ,
struct snd_soc_dai * dai )
2008-02-18 11:40:22 +01:00
{
2009-08-06 16:55:31 -07:00
struct davinci_mcbsp_dev * dev = dai - > private_data ;
2009-09-11 14:29:02 -07:00
struct davinci_pcm_dma_params * dma_params =
2009-09-11 14:29:03 -07:00
& dev - > dma_params [ substream - > stream ] ;
2008-02-18 11:40:22 +01:00
struct snd_interval * i = NULL ;
int mcbsp_word_length ;
2009-07-04 19:29:51 -07:00
unsigned int rcr , xcr , srgr ;
u32 spcr ;
2009-11-18 17:49:51 -07:00
snd_pcm_format_t fmt ;
unsigned element_cnt = 1 ;
2008-02-18 11:40:22 +01:00
/* general line settings */
2009-07-04 19:29:51 -07:00
spcr = davinci_mcbsp_read_reg ( dev , DAVINCI_MCBSP_SPCR_REG ) ;
2008-11-18 11:01:03 +05:30
if ( substream - > stream = = SNDRV_PCM_STREAM_CAPTURE ) {
2009-07-04 19:29:51 -07:00
spcr | = DAVINCI_MCBSP_SPCR_RINTM ( 3 ) | DAVINCI_MCBSP_SPCR_FREE ;
davinci_mcbsp_write_reg ( dev , DAVINCI_MCBSP_SPCR_REG , spcr ) ;
2008-11-18 11:01:03 +05:30
} else {
2009-07-04 19:29:51 -07:00
spcr | = DAVINCI_MCBSP_SPCR_XINTM ( 3 ) | DAVINCI_MCBSP_SPCR_FREE ;
davinci_mcbsp_write_reg ( dev , DAVINCI_MCBSP_SPCR_REG , spcr ) ;
2008-11-18 11:01:03 +05:30
}
2008-02-18 11:40:22 +01:00
i = hw_param_interval ( params , SNDRV_PCM_HW_PARAM_SAMPLE_BITS ) ;
2009-07-04 19:29:51 -07:00
srgr = DAVINCI_MCBSP_SRGR_FSGM ;
srgr | = DAVINCI_MCBSP_SRGR_FWID ( snd_interval_value ( i ) - 1 ) ;
2008-02-18 11:40:22 +01:00
i = hw_param_interval ( params , SNDRV_PCM_HW_PARAM_FRAME_BITS ) ;
2009-07-04 19:29:51 -07:00
srgr | = DAVINCI_MCBSP_SRGR_FPER ( snd_interval_value ( i ) - 1 ) ;
davinci_mcbsp_write_reg ( dev , DAVINCI_MCBSP_SRGR_REG , srgr ) ;
2008-02-18 11:40:22 +01:00
2009-07-04 19:29:57 -07:00
rcr = DAVINCI_MCBSP_RCR_RFIG ;
xcr = DAVINCI_MCBSP_XCR_XFIG ;
if ( dev - > mode = = MOD_DSP_B ) {
rcr | = DAVINCI_MCBSP_RCR_RDATDLY ( 0 ) ;
xcr | = DAVINCI_MCBSP_XCR_XDATDLY ( 0 ) ;
} else {
rcr | = DAVINCI_MCBSP_RCR_RDATDLY ( 1 ) ;
xcr | = DAVINCI_MCBSP_XCR_XDATDLY ( 1 ) ;
}
2008-02-18 11:40:22 +01:00
/* Determine xfer data type */
2009-11-18 17:49:51 -07:00
fmt = params_format ( params ) ;
if ( ( fmt > SNDRV_PCM_FORMAT_S32_LE ) | | ! data_type [ fmt ] ) {
2008-08-26 15:47:55 +02:00
printk ( KERN_WARNING " davinci-i2s: unsupported PCM format \n " ) ;
2008-02-18 11:40:22 +01:00
return - EINVAL ;
}
2009-11-18 17:49:51 -07:00
if ( params_channels ( params ) = = 2 ) {
element_cnt = 2 ;
if ( double_fmt [ fmt ] & & dev - > enable_channel_combine ) {
element_cnt = 1 ;
fmt = double_fmt [ fmt ] ;
}
}
dma_params - > acnt = dma_params - > data_type = data_type [ fmt ] ;
2009-09-30 17:32:27 -04:00
dma_params - > fifo_level = 0 ;
2009-11-18 17:49:51 -07:00
mcbsp_word_length = asp_word_length [ fmt ] ;
rcr | = DAVINCI_MCBSP_RCR_RFRLEN1 ( element_cnt - 1 ) ;
xcr | = DAVINCI_MCBSP_XCR_XFRLEN1 ( element_cnt - 1 ) ;
2008-02-18 11:40:22 +01:00
2009-07-04 19:29:57 -07:00
rcr | = DAVINCI_MCBSP_RCR_RWDLEN1 ( mcbsp_word_length ) |
DAVINCI_MCBSP_RCR_RWDLEN2 ( mcbsp_word_length ) ;
xcr | = DAVINCI_MCBSP_XCR_XWDLEN1 ( mcbsp_word_length ) |
DAVINCI_MCBSP_XCR_XWDLEN2 ( mcbsp_word_length ) ;
2008-02-18 11:40:22 +01:00
2009-07-04 19:29:57 -07:00
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
davinci_mcbsp_write_reg ( dev , DAVINCI_MCBSP_XCR_REG , xcr ) ;
else
davinci_mcbsp_write_reg ( dev , DAVINCI_MCBSP_RCR_REG , rcr ) ;
2008-02-18 11:40:22 +01:00
return 0 ;
}
2009-07-04 19:29:59 -07:00
static int davinci_i2s_prepare ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
2009-08-06 16:55:31 -07:00
struct davinci_mcbsp_dev * dev = dai - > private_data ;
2009-07-04 19:29:59 -07:00
int playback = ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) ;
davinci_mcbsp_stop ( dev , playback ) ;
if ( ( dev - > pcr & DAVINCI_MCBSP_PCR_FSXM ) = = 0 ) {
/* codec is master */
davinci_mcbsp_start ( dev , substream ) ;
}
return 0 ;
}
2008-11-18 22:11:38 +00:00
static int davinci_i2s_trigger ( struct snd_pcm_substream * substream , int cmd ,
struct snd_soc_dai * dai )
2008-02-18 11:40:22 +01:00
{
2009-08-06 16:55:31 -07:00
struct davinci_mcbsp_dev * dev = dai - > private_data ;
2008-02-18 11:40:22 +01:00
int ret = 0 ;
2009-07-04 19:29:53 -07:00
int playback = ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) ;
2009-07-04 19:29:59 -07:00
if ( ( dev - > pcr & DAVINCI_MCBSP_PCR_FSXM ) = = 0 )
return 0 ; /* return if codec is master */
2008-02-18 11:40:22 +01:00
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
case SNDRV_PCM_TRIGGER_RESUME :
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
2009-07-04 19:29:53 -07:00
davinci_mcbsp_start ( dev , substream ) ;
2008-02-18 11:40:22 +01:00
break ;
case SNDRV_PCM_TRIGGER_STOP :
case SNDRV_PCM_TRIGGER_SUSPEND :
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
2009-07-04 19:29:53 -07:00
davinci_mcbsp_stop ( dev , playback ) ;
2008-02-18 11:40:22 +01:00
break ;
default :
ret = - EINVAL ;
}
return ret ;
}
2009-07-04 19:29:59 -07:00
static void davinci_i2s_shutdown ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
2009-08-06 16:55:31 -07:00
struct davinci_mcbsp_dev * dev = dai - > private_data ;
2009-07-04 19:29:59 -07:00
int playback = ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) ;
davinci_mcbsp_stop ( dev , playback ) ;
}
2009-06-05 06:28:23 -04:00
# define DAVINCI_I2S_RATES SNDRV_PCM_RATE_8000_96000
static struct snd_soc_dai_ops davinci_i2s_dai_ops = {
2009-07-07 19:18:46 +01:00
. shutdown = davinci_i2s_shutdown ,
. prepare = davinci_i2s_prepare ,
2009-06-05 06:28:23 -04:00
. trigger = davinci_i2s_trigger ,
. hw_params = davinci_i2s_hw_params ,
. set_fmt = davinci_i2s_set_dai_fmt ,
} ;
struct snd_soc_dai davinci_i2s_dai = {
. name = " davinci-i2s " ,
. id = 0 ,
. playback = {
. channels_min = 2 ,
. channels_max = 2 ,
. rates = DAVINCI_I2S_RATES ,
. formats = SNDRV_PCM_FMTBIT_S16_LE , } ,
. capture = {
. channels_min = 2 ,
. channels_max = 2 ,
. rates = DAVINCI_I2S_RATES ,
. formats = SNDRV_PCM_FMTBIT_S16_LE , } ,
. ops = & davinci_i2s_dai_ops ,
} ;
EXPORT_SYMBOL_GPL ( davinci_i2s_dai ) ;
static int davinci_i2s_probe ( struct platform_device * pdev )
2008-02-18 11:40:22 +01:00
{
2009-06-05 06:28:23 -04:00
struct snd_platform_data * pdata = pdev - > dev . platform_data ;
2008-02-18 11:40:22 +01:00
struct davinci_mcbsp_dev * dev ;
2009-06-05 06:28:23 -04:00
struct resource * mem , * ioarea , * res ;
2008-02-18 11:40:22 +01:00
int ret ;
mem = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! mem ) {
dev_err ( & pdev - > dev , " no mem resource? \n " ) ;
return - ENODEV ;
}
ioarea = request_mem_region ( mem - > start , ( mem - > end - mem - > start ) + 1 ,
pdev - > name ) ;
if ( ! ioarea ) {
dev_err ( & pdev - > dev , " McBSP region already claimed \n " ) ;
return - EBUSY ;
}
dev = kzalloc ( sizeof ( struct davinci_mcbsp_dev ) , GFP_KERNEL ) ;
if ( ! dev ) {
ret = - ENOMEM ;
goto err_release_region ;
}
2009-11-18 17:49:53 -07:00
if ( pdata ) {
2009-11-18 17:49:51 -07:00
dev - > enable_channel_combine = pdata - > enable_channel_combine ;
2009-11-18 17:49:53 -07:00
dev - > dma_params [ SNDRV_PCM_STREAM_PLAYBACK ] . sram_size =
pdata - > sram_size_playback ;
dev - > dma_params [ SNDRV_PCM_STREAM_CAPTURE ] . sram_size =
pdata - > sram_size_capture ;
}
2009-07-15 10:42:09 -07:00
dev - > clk = clk_get ( & pdev - > dev , NULL ) ;
2008-02-18 11:40:22 +01:00
if ( IS_ERR ( dev - > clk ) ) {
ret = - ENODEV ;
goto err_free_mem ;
}
clk_enable ( dev - > clk ) ;
dev - > base = ( void __iomem * ) IO_ADDRESS ( mem - > start ) ;
2009-09-11 14:29:03 -07:00
dev - > dma_params [ SNDRV_PCM_STREAM_PLAYBACK ] . dma_addr =
2008-02-18 11:40:22 +01:00
( dma_addr_t ) ( io_v2p ( dev - > base ) + DAVINCI_MCBSP_DXR_REG ) ;
2009-09-11 14:29:03 -07:00
dev - > dma_params [ SNDRV_PCM_STREAM_CAPTURE ] . dma_addr =
2008-02-18 11:40:22 +01:00
( dma_addr_t ) ( io_v2p ( dev - > base ) + DAVINCI_MCBSP_DRR_REG ) ;
2009-06-05 06:28:23 -04:00
/* first TX, then RX */
res = platform_get_resource ( pdev , IORESOURCE_DMA , 0 ) ;
if ( ! res ) {
dev_err ( & pdev - > dev , " no DMA resource \n " ) ;
2009-06-08 06:49:41 -04:00
ret = - ENXIO ;
2009-06-05 06:28:23 -04:00
goto err_free_mem ;
}
2009-09-11 14:29:03 -07:00
dev - > dma_params [ SNDRV_PCM_STREAM_PLAYBACK ] . channel = res - > start ;
2009-06-05 06:28:23 -04:00
res = platform_get_resource ( pdev , IORESOURCE_DMA , 1 ) ;
if ( ! res ) {
dev_err ( & pdev - > dev , " no DMA resource \n " ) ;
2009-06-08 06:49:41 -04:00
ret = - ENXIO ;
2009-06-05 06:28:23 -04:00
goto err_free_mem ;
}
2009-09-11 14:29:03 -07:00
dev - > dma_params [ SNDRV_PCM_STREAM_CAPTURE ] . channel = res - > start ;
2009-06-05 06:28:23 -04:00
davinci_i2s_dai . private_data = dev ;
2009-11-16 16:52:31 -07:00
davinci_i2s_dai . dma_data = dev - > dma_params ;
2009-06-05 06:28:23 -04:00
ret = snd_soc_register_dai ( & davinci_i2s_dai ) ;
if ( ret ! = 0 )
goto err_free_mem ;
2008-02-18 11:40:22 +01:00
return 0 ;
err_free_mem :
kfree ( dev ) ;
err_release_region :
release_mem_region ( mem - > start , ( mem - > end - mem - > start ) + 1 ) ;
return ret ;
}
2009-06-05 06:28:23 -04:00
static int davinci_i2s_remove ( struct platform_device * pdev )
2008-02-18 11:40:22 +01:00
{
2009-06-05 06:28:23 -04:00
struct davinci_mcbsp_dev * dev = davinci_i2s_dai . private_data ;
2008-02-18 11:40:22 +01:00
struct resource * mem ;
2009-06-05 06:28:23 -04:00
snd_soc_unregister_dai ( & davinci_i2s_dai ) ;
2008-02-18 11:40:22 +01:00
clk_disable ( dev - > clk ) ;
clk_put ( dev - > clk ) ;
dev - > clk = NULL ;
kfree ( dev ) ;
mem = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
release_mem_region ( mem - > start , ( mem - > end - mem - > start ) + 1 ) ;
2009-06-05 06:28:23 -04:00
return 0 ;
}
2009-03-03 09:41:00 +08:00
2009-06-05 06:28:23 -04:00
static struct platform_driver davinci_mcbsp_driver = {
. probe = davinci_i2s_probe ,
. remove = davinci_i2s_remove ,
. driver = {
. name = " davinci-asp " ,
. owner = THIS_MODULE ,
} ,
2008-02-18 11:40:22 +01:00
} ;
2008-12-10 07:47:22 +01:00
static int __init davinci_i2s_init ( void )
2008-12-03 19:26:35 +00:00
{
2009-06-05 06:28:23 -04:00
return platform_driver_register ( & davinci_mcbsp_driver ) ;
2008-12-03 19:26:35 +00:00
}
module_init ( davinci_i2s_init ) ;
static void __exit davinci_i2s_exit ( void )
{
2009-06-05 06:28:23 -04:00
platform_driver_unregister ( & davinci_mcbsp_driver ) ;
2008-12-03 19:26:35 +00:00
}
module_exit ( davinci_i2s_exit ) ;
2008-02-18 11:40:22 +01:00
MODULE_AUTHOR ( " Vladimir Barinov " ) ;
MODULE_DESCRIPTION ( " TI DAVINCI I2S (McBSP) SoC Interface " ) ;
MODULE_LICENSE ( " GPL " ) ;