2008-04-25 15:55:19 +04:00
/*
* omap - mcbsp . c - - OMAP ALSA SoC DAI driver using McBSP port
*
* Copyright ( C ) 2008 Nokia Corporation
*
2011-08-11 16:44:57 +04:00
* Contact : Jarkko Nikula < jarkko . nikula @ bitmer . com >
2011-05-03 19:14:06 +04:00
* Peter Ujfalusi < peter . ujfalusi @ ti . com >
2008-04-25 15:55:19 +04:00
*
* 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 .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA
* 02110 - 1301 USA
*
*/
# include <linux/init.h>
# include <linux/module.h>
# include <linux/device.h>
2012-02-14 16:52:42 +04:00
# include <linux/pm_runtime.h>
2012-08-16 17:41:08 +04:00
# include <linux/of.h>
# include <linux/of_device.h>
2008-04-25 15:55:19 +04:00
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/initval.h>
# include <sound/soc.h>
2013-04-03 13:06:05 +04:00
# include <sound/dmaengine_pcm.h>
2014-05-26 12:51:14 +04:00
# include <sound/omap-pcm.h>
2008-04-25 15:55:19 +04:00
2012-08-24 17:21:06 +04:00
# include <linux/platform_data/asoc-ti-mcbsp.h>
2012-02-03 15:11:47 +04:00
# include "mcbsp.h"
2008-04-25 15:55:19 +04:00
# include "omap-mcbsp.h"
2008-11-12 18:05:51 +03:00
# define OMAP_MCBSP_RATES (SNDRV_PCM_RATE_8000_96000)
2008-04-25 15:55:19 +04:00
2010-02-22 15:21:12 +03:00
# define OMAP_MCBSP_SOC_SINGLE_S16_EXT(xname, xmin, xmax, \
xhandler_get , xhandler_put ) \
{ . iface = SNDRV_CTL_ELEM_IFACE_MIXER , . name = xname , \
. info = omap_mcbsp_st_info_volsw , \
. get = xhandler_get , . put = xhandler_put , \
. private_value = ( unsigned long ) & ( struct soc_mixer_control ) \
{ . min = xmin , . max = xmax } }
2012-02-03 15:11:47 +04:00
enum {
OMAP_MCBSP_WORD_8 = 0 ,
OMAP_MCBSP_WORD_12 ,
OMAP_MCBSP_WORD_16 ,
OMAP_MCBSP_WORD_20 ,
OMAP_MCBSP_WORD_24 ,
OMAP_MCBSP_WORD_32 ,
} ;
2008-04-25 15:55:19 +04:00
/*
* Stream DMA parameters . DMA request line and port address are set runtime
* since they are different between OMAP1 and later OMAPs
*/
2013-03-25 19:58:16 +04:00
static void omap_mcbsp_set_threshold ( struct snd_pcm_substream * substream ,
unsigned int packet_size )
2009-08-20 17:18:25 +04:00
{
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
2010-03-17 23:15:21 +03:00
struct snd_soc_dai * cpu_dai = rtd - > cpu_dai ;
2012-02-14 20:20:58 +04:00
struct omap_mcbsp * mcbsp = snd_soc_dai_get_drvdata ( cpu_dai ) ;
2010-06-03 08:39:35 +04:00
int words ;
2009-08-20 17:18:26 +04:00
2012-03-15 14:20:32 +04:00
/*
* Configure McBSP threshold based on either :
* packet_size , when the sDMA is in packet mode , or based on the
* period size in THRESHOLD mode , otherwise use McBSP threshold = 1
* for mono streams .
*/
2013-03-25 19:58:16 +04:00
if ( packet_size )
words = packet_size ;
2009-08-20 17:18:26 +04:00
else
2010-06-03 08:39:35 +04:00
words = 1 ;
2009-08-20 17:18:25 +04:00
/* Configure McBSP internal buffer usage */
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
2012-02-14 20:20:58 +04:00
omap_mcbsp_set_tx_threshold ( mcbsp , words ) ;
2009-08-20 17:18:25 +04:00
else
2012-02-14 20:20:58 +04:00
omap_mcbsp_set_rx_threshold ( mcbsp , words ) ;
2009-08-20 17:18:25 +04:00
}
2010-06-03 08:39:36 +04:00
static int omap_mcbsp_hwrule_min_buffersize ( struct snd_pcm_hw_params * params ,
struct snd_pcm_hw_rule * rule )
{
struct snd_interval * buffer_size = hw_param_interval ( params ,
SNDRV_PCM_HW_PARAM_BUFFER_SIZE ) ;
struct snd_interval * channels = hw_param_interval ( params ,
SNDRV_PCM_HW_PARAM_CHANNELS ) ;
2012-02-14 20:20:58 +04:00
struct omap_mcbsp * mcbsp = rule - > private ;
2010-06-03 08:39:36 +04:00
struct snd_interval frames ;
int size ;
snd_interval_any ( & frames ) ;
2012-02-13 18:26:54 +04:00
size = mcbsp - > pdata - > buffer_size ;
2010-06-03 08:39:36 +04:00
frames . min = size / channels - > min ;
frames . integer = 1 ;
return snd_interval_refine ( buffer_size , & frames ) ;
}
2008-11-19 01:11:38 +03:00
static int omap_mcbsp_dai_startup ( struct snd_pcm_substream * substream ,
2010-03-17 23:15:21 +03:00
struct snd_soc_dai * cpu_dai )
2008-04-25 15:55:19 +04:00
{
2012-02-14 20:20:58 +04:00
struct omap_mcbsp * mcbsp = snd_soc_dai_get_drvdata ( cpu_dai ) ;
2008-04-25 15:55:19 +04:00
int err = 0 ;
2009-08-20 17:18:25 +04:00
if ( ! cpu_dai - > active )
2012-02-14 20:20:58 +04:00
err = omap_mcbsp_request ( mcbsp ) ;
2009-08-20 17:18:25 +04:00
2010-06-03 08:39:36 +04:00
/*
* OMAP3 McBSP FIFO is word structured .
* McBSP2 has 1024 + 256 = 1280 word long buffer ,
* McBSP1 , 3 , 4 , 5 has 128 word long buffer
* This means that the size of the FIFO depends on the sample format .
* For example on McBSP3 :
* 16 bit samples : size is 128 * 2 = 256 bytes
* 32 bit samples : size is 128 * 4 = 512 bytes
* It is simpler to place constraint for buffer and period based on
* channels .
* McBSP3 as example again ( 16 or 32 bit samples ) :
* 1 channel ( mono ) : size is 128 frames ( 128 words )
* 2 channels ( stereo ) : size is 128 / 2 = 64 frames ( 2 * 64 words )
* 4 channels : size is 128 / 4 = 32 frames ( 4 * 32 words )
*/
2012-02-14 20:20:58 +04:00
if ( mcbsp - > pdata - > buffer_size ) {
2009-03-27 16:32:01 +03:00
/*
2010-07-29 10:51:28 +04:00
* Rule for the buffer size . We should not allow
2012-03-20 13:47:36 +04:00
* smaller buffer than the FIFO size to avoid underruns .
* This applies only for the playback stream .
2010-06-03 08:39:36 +04:00
*/
2012-03-20 13:47:36 +04:00
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
snd_pcm_hw_rule_add ( substream - > runtime , 0 ,
SNDRV_PCM_HW_PARAM_BUFFER_SIZE ,
omap_mcbsp_hwrule_min_buffersize ,
mcbsp ,
SNDRV_PCM_HW_PARAM_CHANNELS , - 1 ) ;
2009-08-20 17:18:25 +04:00
2010-07-29 10:51:28 +04:00
/* Make sure, that the period size is always even */
snd_pcm_hw_constraint_step ( substream - > runtime , 0 ,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE , 2 ) ;
2009-03-27 16:32:01 +03:00
}
2008-04-25 15:55:19 +04:00
return err ;
}
2008-11-19 01:11:38 +03:00
static void omap_mcbsp_dai_shutdown ( struct snd_pcm_substream * substream ,
2010-03-17 23:15:21 +03:00
struct snd_soc_dai * cpu_dai )
2008-04-25 15:55:19 +04:00
{
2012-02-14 20:20:58 +04:00
struct omap_mcbsp * mcbsp = snd_soc_dai_get_drvdata ( cpu_dai ) ;
2008-04-25 15:55:19 +04:00
if ( ! cpu_dai - > active ) {
2012-02-14 20:20:58 +04:00
omap_mcbsp_free ( mcbsp ) ;
2012-02-14 17:23:15 +04:00
mcbsp - > configured = 0 ;
2008-04-25 15:55:19 +04:00
}
}
2008-11-19 01:11:38 +03:00
static int omap_mcbsp_dai_trigger ( struct snd_pcm_substream * substream , int cmd ,
2010-03-17 23:15:21 +03:00
struct snd_soc_dai * cpu_dai )
2008-04-25 15:55:19 +04:00
{
2012-02-14 20:20:58 +04:00
struct omap_mcbsp * mcbsp = snd_soc_dai_get_drvdata ( cpu_dai ) ;
2009-08-07 10:59:47 +04:00
int err = 0 , play = ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) ;
2008-04-25 15:55:19 +04:00
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
case SNDRV_PCM_TRIGGER_RESUME :
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
2012-02-14 17:23:15 +04:00
mcbsp - > active + + ;
2012-02-14 20:20:58 +04:00
omap_mcbsp_start ( mcbsp , play , ! play ) ;
2008-04-25 15:55:19 +04:00
break ;
case SNDRV_PCM_TRIGGER_STOP :
case SNDRV_PCM_TRIGGER_SUSPEND :
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
2012-02-14 20:20:58 +04:00
omap_mcbsp_stop ( mcbsp , play , ! play ) ;
2012-02-14 17:23:15 +04:00
mcbsp - > active - - ;
2008-04-25 15:55:19 +04:00
break ;
default :
err = - EINVAL ;
}
return err ;
}
2010-03-03 16:08:09 +03:00
static snd_pcm_sframes_t omap_mcbsp_dai_delay (
struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
2010-03-17 23:15:21 +03:00
struct snd_soc_dai * cpu_dai = rtd - > cpu_dai ;
2012-02-14 20:20:58 +04:00
struct omap_mcbsp * mcbsp = snd_soc_dai_get_drvdata ( cpu_dai ) ;
2010-03-03 16:08:09 +03:00
u16 fifo_use ;
snd_pcm_sframes_t delay ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
2012-02-14 20:20:58 +04:00
fifo_use = omap_mcbsp_get_tx_delay ( mcbsp ) ;
2010-03-03 16:08:09 +03:00
else
2012-02-14 20:20:58 +04:00
fifo_use = omap_mcbsp_get_rx_delay ( mcbsp ) ;
2010-03-03 16:08:09 +03:00
/*
* Divide the used locations with the channel count to get the
* FIFO usage in samples ( don ' t care about partial samples in the
* buffer ) .
*/
delay = fifo_use / substream - > runtime - > channels ;
return delay ;
}
2008-04-25 15:55:19 +04:00
static int omap_mcbsp_dai_hw_params ( struct snd_pcm_substream * substream ,
2008-11-19 01:11:38 +03:00
struct snd_pcm_hw_params * params ,
2010-03-17 23:15:21 +03:00
struct snd_soc_dai * cpu_dai )
2008-04-25 15:55:19 +04:00
{
2012-02-14 20:20:58 +04:00
struct omap_mcbsp * mcbsp = snd_soc_dai_get_drvdata ( cpu_dai ) ;
2012-02-14 17:23:15 +04:00
struct omap_mcbsp_reg_cfg * regs = & mcbsp - > cfg_regs ;
2013-04-03 13:06:05 +04:00
struct snd_dmaengine_dai_dma_data * dma_data ;
2012-09-14 16:05:51 +04:00
int wlen , channels , wpf ;
2010-07-29 10:51:27 +04:00
int pkt_size = 0 ;
2009-11-09 22:02:15 +03:00
unsigned int format , div , framesize , master ;
2008-04-25 15:55:19 +04:00
2012-09-14 16:05:57 +04:00
dma_data = snd_soc_dai_get_dma_data ( cpu_dai , substream ) ;
2012-03-15 14:20:32 +04:00
channels = params_channels ( params ) ;
2011-02-24 12:46:56 +03:00
2010-05-13 19:48:16 +04:00
switch ( params_format ( params ) ) {
case SNDRV_PCM_FORMAT_S16_LE :
2010-07-29 10:51:27 +04:00
wlen = 16 ;
2010-05-13 19:48:16 +04:00
break ;
case SNDRV_PCM_FORMAT_S32_LE :
2010-07-29 10:51:27 +04:00
wlen = 32 ;
2010-05-13 19:48:16 +04:00
break ;
default :
return - EINVAL ;
}
2012-02-14 20:20:58 +04:00
if ( mcbsp - > pdata - > buffer_size ) {
2012-02-13 18:26:54 +04:00
if ( mcbsp - > dma_op_mode = = MCBSP_DMA_MODE_THRESHOLD ) {
2010-07-29 10:51:27 +04:00
int period_words , max_thrsh ;
2012-09-14 16:05:49 +04:00
int divider = 0 ;
2010-07-29 10:51:27 +04:00
period_words = params_period_bytes ( params ) / ( wlen / 8 ) ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
2012-02-13 18:26:54 +04:00
max_thrsh = mcbsp - > max_tx_thres ;
2010-07-29 10:51:27 +04:00
else
2012-02-13 18:26:54 +04:00
max_thrsh = mcbsp - > max_rx_thres ;
2010-07-29 10:51:27 +04:00
/*
2012-09-14 16:05:49 +04:00
* Use sDMA packet mode if McBSP is in threshold mode :
* If period words less than the FIFO size the packet
* size is set to the number of period words , otherwise
* Look for the biggest threshold value which divides
* the period size evenly .
2010-07-29 10:51:27 +04:00
*/
2012-09-14 16:05:49 +04:00
divider = period_words / max_thrsh ;
if ( period_words % max_thrsh )
divider + + ;
while ( period_words % divider & &
divider < period_words )
divider + + ;
if ( divider = = period_words )
return - EINVAL ;
pkt_size = period_words / divider ;
2012-03-15 14:20:32 +04:00
} else if ( channels > 1 ) {
/* Use packet mode for non mono streams */
pkt_size = channels ;
2010-07-29 10:51:27 +04:00
}
2013-03-25 19:58:16 +04:00
omap_mcbsp_set_threshold ( substream , pkt_size ) ;
2010-07-29 10:51:25 +04:00
}
2013-04-03 13:06:05 +04:00
dma_data - > maxburst = pkt_size ;
2010-03-19 17:52:55 +03:00
2012-02-14 17:23:15 +04:00
if ( mcbsp - > configured ) {
2008-04-25 15:55:19 +04:00
/* McBSP already configured by another stream */
return 0 ;
}
2011-09-30 17:07:44 +04:00
regs - > rcr2 & = ~ ( RPHASE | RFRLEN2 ( 0x7f ) | RWDLEN2 ( 7 ) ) ;
regs - > xcr2 & = ~ ( RPHASE | XFRLEN2 ( 0x7f ) | XWDLEN2 ( 7 ) ) ;
regs - > rcr1 & = ~ ( RFRLEN1 ( 0x7f ) | RWDLEN1 ( 7 ) ) ;
regs - > xcr1 & = ~ ( XFRLEN1 ( 0x7f ) | XWDLEN1 ( 7 ) ) ;
2012-02-14 17:23:15 +04:00
format = mcbsp - > fmt & SND_SOC_DAIFMT_FORMAT_MASK ;
2012-03-15 14:20:32 +04:00
wpf = channels ;
2010-03-19 13:27:31 +03:00
if ( channels = = 2 & & ( format = = SND_SOC_DAIFMT_I2S | |
format = = SND_SOC_DAIFMT_LEFT_J ) ) {
2009-11-09 22:02:15 +03:00
/* Use dual-phase frames */
regs - > rcr2 | = RPHASE ;
regs - > xcr2 | = XPHASE ;
/* Set 1 word per (McBSP) frame for phase1 and phase2 */
wpf - - ;
regs - > rcr2 | = RFRLEN2 ( wpf - 1 ) ;
regs - > xcr2 | = XFRLEN2 ( wpf - 1 ) ;
2008-04-25 15:55:19 +04:00
}
2009-11-09 22:02:15 +03:00
regs - > rcr1 | = RFRLEN1 ( wpf - 1 ) ;
regs - > xcr1 | = XFRLEN1 ( wpf - 1 ) ;
2008-04-25 15:55:19 +04:00
switch ( params_format ( params ) ) {
case SNDRV_PCM_FORMAT_S16_LE :
/* Set word lengths */
regs - > rcr2 | = RWDLEN2 ( OMAP_MCBSP_WORD_16 ) ;
regs - > rcr1 | = RWDLEN1 ( OMAP_MCBSP_WORD_16 ) ;
regs - > xcr2 | = XWDLEN2 ( OMAP_MCBSP_WORD_16 ) ;
regs - > xcr1 | = XWDLEN1 ( OMAP_MCBSP_WORD_16 ) ;
break ;
2010-05-13 19:48:16 +04:00
case SNDRV_PCM_FORMAT_S32_LE :
/* Set word lengths */
regs - > rcr2 | = RWDLEN2 ( OMAP_MCBSP_WORD_32 ) ;
regs - > rcr1 | = RWDLEN1 ( OMAP_MCBSP_WORD_32 ) ;
regs - > xcr2 | = XWDLEN2 ( OMAP_MCBSP_WORD_32 ) ;
regs - > xcr1 | = XWDLEN1 ( OMAP_MCBSP_WORD_32 ) ;
break ;
2008-04-25 15:55:19 +04:00
default :
/* Unsupported PCM format */
return - EINVAL ;
}
2009-11-09 22:02:15 +03:00
/* In McBSP master modes, FRAME (i.e. sample rate) is generated
* by _counting_ BCLKs . Calculate frame size in BCLKs */
2012-02-14 17:23:15 +04:00
master = mcbsp - > fmt & SND_SOC_DAIFMT_MASTER_MASK ;
2009-11-09 22:02:15 +03:00
if ( master = = SND_SOC_DAIFMT_CBS_CFS ) {
2012-02-14 17:23:15 +04:00
div = mcbsp - > clk_div ? mcbsp - > clk_div : 1 ;
framesize = ( mcbsp - > in_freq / div ) / params_rate ( params ) ;
2009-11-09 22:02:15 +03:00
if ( framesize < wlen * channels ) {
printk ( KERN_ERR " %s: not enough bandwidth for desired rate and "
" channels \n " , __func__ ) ;
return - EINVAL ;
}
} else
framesize = wlen * channels ;
2008-10-20 16:29:59 +04:00
/* Set FS period and length in terms of bit clock periods */
2011-09-30 17:07:44 +04:00
regs - > srgr2 & = ~ FPER ( 0xfff ) ;
regs - > srgr1 & = ~ FWID ( 0xff ) ;
2009-04-15 16:38:55 +04:00
switch ( format ) {
2008-10-20 16:29:59 +04:00
case SND_SOC_DAIFMT_I2S :
2010-03-19 13:27:31 +03:00
case SND_SOC_DAIFMT_LEFT_J :
2009-11-09 22:02:15 +03:00
regs - > srgr2 | = FPER ( framesize - 1 ) ;
regs - > srgr1 | = FWID ( ( framesize > > 1 ) - 1 ) ;
2008-10-20 16:29:59 +04:00
break ;
2009-04-15 16:38:56 +04:00
case SND_SOC_DAIFMT_DSP_A :
2008-12-22 11:21:36 +03:00
case SND_SOC_DAIFMT_DSP_B :
2009-11-09 22:02:15 +03:00
regs - > srgr2 | = FPER ( framesize - 1 ) ;
2009-04-15 14:48:16 +04:00
regs - > srgr1 | = FWID ( 0 ) ;
2008-10-20 16:29:59 +04:00
break ;
}
2012-02-14 17:23:15 +04:00
omap_mcbsp_config ( mcbsp , & mcbsp - > cfg_regs ) ;
mcbsp - > wlen = wlen ;
mcbsp - > configured = 1 ;
2008-04-25 15:55:19 +04:00
return 0 ;
}
/*
* This must be called before _set_clkdiv and _set_sysclk since McBSP register
* cache is initialized here
*/
2008-07-07 19:08:07 +04:00
static int omap_mcbsp_dai_set_dai_fmt ( struct snd_soc_dai * cpu_dai ,
2008-04-25 15:55:19 +04:00
unsigned int fmt )
{
2012-02-14 20:20:58 +04:00
struct omap_mcbsp * mcbsp = snd_soc_dai_get_drvdata ( cpu_dai ) ;
2012-02-14 17:23:15 +04:00
struct omap_mcbsp_reg_cfg * regs = & mcbsp - > cfg_regs ;
2011-09-30 11:55:32 +04:00
bool inv_fs = false ;
2008-04-25 15:55:19 +04:00
2012-02-14 17:23:15 +04:00
if ( mcbsp - > configured )
2008-04-25 15:55:19 +04:00
return 0 ;
2012-02-14 17:23:15 +04:00
mcbsp - > fmt = fmt ;
2008-04-25 15:55:19 +04:00
memset ( regs , 0 , sizeof ( * regs ) ) ;
/* Generic McBSP register settings */
regs - > spcr2 | = XINTM ( 3 ) | FREE ;
regs - > spcr1 | = RINTM ( 3 ) ;
2012-08-16 17:41:06 +04:00
/* RFIG and XFIG are not defined in 2430 and on OMAP3+ */
if ( ! mcbsp - > pdata - > has_ccr ) {
2009-08-20 17:18:23 +04:00
regs - > rcr2 | = RFIG ;
regs - > xcr2 | = XFIG ;
}
2012-08-16 17:41:06 +04:00
/* Configure XCCR/RCCR only for revisions which have ccr registers */
if ( mcbsp - > pdata - > has_ccr ) {
2009-08-23 13:24:26 +04:00
regs - > xccr = DXENDLY ( 1 ) | XDMAEN | XDISABLE ;
regs - > rccr = RFULL_CYCLE | RDMAEN | RDISABLE ;
2009-01-29 14:29:46 +03:00
}
2008-04-25 15:55:19 +04:00
switch ( fmt & SND_SOC_DAIFMT_FORMAT_MASK ) {
case SND_SOC_DAIFMT_I2S :
/* 1-bit data delay */
regs - > rcr2 | = RDATDLY ( 1 ) ;
regs - > xcr2 | = XDATDLY ( 1 ) ;
break ;
2010-03-19 13:27:31 +03:00
case SND_SOC_DAIFMT_LEFT_J :
/* 0-bit data delay */
regs - > rcr2 | = RDATDLY ( 0 ) ;
regs - > xcr2 | = XDATDLY ( 0 ) ;
regs - > spcr1 | = RJUST ( 2 ) ;
/* Invert FS polarity configuration */
2011-09-30 11:55:32 +04:00
inv_fs = true ;
2010-03-19 13:27:31 +03:00
break ;
2009-04-15 16:38:56 +04:00
case SND_SOC_DAIFMT_DSP_A :
/* 1-bit data delay */
regs - > rcr2 | = RDATDLY ( 1 ) ;
regs - > xcr2 | = XDATDLY ( 1 ) ;
/* Invert FS polarity configuration */
2011-09-30 11:55:32 +04:00
inv_fs = true ;
2009-04-15 16:38:56 +04:00
break ;
2008-12-22 11:21:36 +03:00
case SND_SOC_DAIFMT_DSP_B :
2008-10-02 13:37:06 +04:00
/* 0-bit data delay */
regs - > rcr2 | = RDATDLY ( 0 ) ;
regs - > xcr2 | = XDATDLY ( 0 ) ;
2009-04-15 14:48:16 +04:00
/* Invert FS polarity configuration */
2011-09-30 11:55:32 +04:00
inv_fs = true ;
2008-10-02 13:37:06 +04:00
break ;
2008-04-25 15:55:19 +04:00
default :
/* Unsupported data format */
return - EINVAL ;
}
switch ( fmt & SND_SOC_DAIFMT_MASTER_MASK ) {
case SND_SOC_DAIFMT_CBS_CFS :
/* McBSP master. Set FS and bit clocks as outputs */
regs - > pcr0 | = FSXM | FSRM |
CLKXM | CLKRM ;
/* Sample rate generator drives the FS */
regs - > srgr2 | = FSGM ;
break ;
2013-07-21 20:24:01 +04:00
case SND_SOC_DAIFMT_CBM_CFS :
/* McBSP slave. FS clock as output */
regs - > srgr2 | = FSGM ;
2015-01-16 12:20:25 +03:00
regs - > pcr0 | = FSXM | FSRM ;
2013-07-21 20:24:01 +04:00
break ;
2008-04-25 15:55:19 +04:00
case SND_SOC_DAIFMT_CBM_CFM :
/* McBSP slave */
break ;
default :
/* Unsupported master/slave configuration */
return - EINVAL ;
}
/* Set bit clock (CLKX/CLKR) and FS polarities */
2011-09-30 11:55:32 +04:00
switch ( fmt & SND_SOC_DAIFMT_INV_MASK ) {
2008-04-25 15:55:19 +04:00
case SND_SOC_DAIFMT_NB_NF :
/*
* Normal BCLK + FS .
* FS active low . TX data driven on falling edge of bit clock
* and RX data sampled on rising edge of bit clock .
*/
regs - > pcr0 | = FSXP | FSRP |
CLKXP | CLKRP ;
break ;
case SND_SOC_DAIFMT_NB_IF :
regs - > pcr0 | = CLKXP | CLKRP ;
break ;
case SND_SOC_DAIFMT_IB_NF :
regs - > pcr0 | = FSXP | FSRP ;
break ;
case SND_SOC_DAIFMT_IB_IF :
break ;
default :
return - EINVAL ;
}
2011-09-30 11:55:32 +04:00
if ( inv_fs = = true )
regs - > pcr0 ^ = FSXP | FSRP ;
2008-04-25 15:55:19 +04:00
return 0 ;
}
2008-07-07 19:08:07 +04:00
static int omap_mcbsp_dai_set_clkdiv ( struct snd_soc_dai * cpu_dai ,
2008-04-25 15:55:19 +04:00
int div_id , int div )
{
2012-02-14 20:20:58 +04:00
struct omap_mcbsp * mcbsp = snd_soc_dai_get_drvdata ( cpu_dai ) ;
2012-02-14 17:23:15 +04:00
struct omap_mcbsp_reg_cfg * regs = & mcbsp - > cfg_regs ;
2008-04-25 15:55:19 +04:00
if ( div_id ! = OMAP_MCBSP_CLKGDV )
return - ENODEV ;
2012-02-14 17:23:15 +04:00
mcbsp - > clk_div = div ;
2011-09-30 17:07:44 +04:00
regs - > srgr1 & = ~ CLKGDV ( 0xff ) ;
2008-04-25 15:55:19 +04:00
regs - > srgr1 | = CLKGDV ( div - 1 ) ;
return 0 ;
}
2008-07-07 19:08:07 +04:00
static int omap_mcbsp_dai_set_dai_sysclk ( struct snd_soc_dai * cpu_dai ,
2008-04-25 15:55:19 +04:00
int clk_id , unsigned int freq ,
int dir )
{
2012-02-14 20:20:58 +04:00
struct omap_mcbsp * mcbsp = snd_soc_dai_get_drvdata ( cpu_dai ) ;
2012-02-14 17:23:15 +04:00
struct omap_mcbsp_reg_cfg * regs = & mcbsp - > cfg_regs ;
2008-04-25 15:55:19 +04:00
int err = 0 ;
2012-02-14 17:23:15 +04:00
if ( mcbsp - > active ) {
if ( freq = = mcbsp - > in_freq )
2011-09-23 12:19:13 +04:00
return 0 ;
else
return - EBUSY ;
2011-09-26 11:56:42 +04:00
}
2011-09-23 12:19:13 +04:00
2012-08-16 17:41:04 +04:00
mcbsp - > in_freq = freq ;
regs - > srgr2 & = ~ CLKSM ;
regs - > pcr0 & = ~ SCLKME ;
2009-11-09 22:02:15 +03:00
2008-04-25 15:55:19 +04:00
switch ( clk_id ) {
case OMAP_MCBSP_SYSCLK_CLK :
regs - > srgr2 | = CLKSM ;
break ;
case OMAP_MCBSP_SYSCLK_CLKS_FCLK :
2012-11-21 21:42:25 +04:00
if ( mcbsp_omap1 ( ) ) {
2010-10-08 21:40:19 +04:00
err = - EINVAL ;
break ;
}
2012-02-14 20:20:58 +04:00
err = omap2_mcbsp_set_clks_src ( mcbsp ,
2010-10-08 21:40:19 +04:00
MCBSP_CLKS_PRCM_SRC ) ;
break ;
2008-04-25 15:55:19 +04:00
case OMAP_MCBSP_SYSCLK_CLKS_EXT :
2012-11-21 21:42:25 +04:00
if ( mcbsp_omap1 ( ) ) {
2010-10-08 21:40:19 +04:00
err = 0 ;
break ;
}
2012-02-14 20:20:58 +04:00
err = omap2_mcbsp_set_clks_src ( mcbsp ,
2010-10-08 21:40:19 +04:00
MCBSP_CLKS_PAD_SRC ) ;
2008-04-25 15:55:19 +04:00
break ;
case OMAP_MCBSP_SYSCLK_CLKX_EXT :
regs - > srgr2 | = CLKSM ;
case OMAP_MCBSP_SYSCLK_CLKR_EXT :
regs - > pcr0 | = SCLKME ;
break ;
default :
err = - ENODEV ;
}
return err ;
}
2011-11-23 14:40:40 +04:00
static const struct snd_soc_dai_ops mcbsp_dai_ops = {
2009-03-03 04:41:00 +03:00
. startup = omap_mcbsp_dai_startup ,
. shutdown = omap_mcbsp_dai_shutdown ,
. trigger = omap_mcbsp_dai_trigger ,
2010-03-03 16:08:09 +03:00
. delay = omap_mcbsp_dai_delay ,
2009-03-03 04:41:00 +03:00
. hw_params = omap_mcbsp_dai_hw_params ,
. set_fmt = omap_mcbsp_dai_set_dai_fmt ,
. set_clkdiv = omap_mcbsp_dai_set_clkdiv ,
. set_sysclk = omap_mcbsp_dai_set_dai_sysclk ,
} ;
2012-02-14 16:52:42 +04:00
static int omap_mcbsp_probe ( struct snd_soc_dai * dai )
{
struct omap_mcbsp * mcbsp = snd_soc_dai_get_drvdata ( dai ) ;
pm_runtime_enable ( mcbsp - > dev ) ;
2014-04-16 16:46:15 +04:00
snd_soc_dai_init_dma_data ( dai ,
& mcbsp - > dma_data [ SNDRV_PCM_STREAM_PLAYBACK ] ,
& mcbsp - > dma_data [ SNDRV_PCM_STREAM_CAPTURE ] ) ;
2012-02-14 16:52:42 +04:00
return 0 ;
}
static int omap_mcbsp_remove ( struct snd_soc_dai * dai )
{
struct omap_mcbsp * mcbsp = snd_soc_dai_get_drvdata ( dai ) ;
pm_runtime_disable ( mcbsp - > dev ) ;
return 0 ;
}
2011-10-10 09:07:08 +04:00
static struct snd_soc_dai_driver omap_mcbsp_dai = {
2012-02-14 16:52:42 +04:00
. probe = omap_mcbsp_probe ,
. remove = omap_mcbsp_remove ,
2010-03-17 23:15:21 +03:00
. playback = {
. channels_min = 1 ,
. channels_max = 16 ,
. rates = OMAP_MCBSP_RATES ,
. formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE ,
} ,
. capture = {
. channels_min = 1 ,
. channels_max = 16 ,
. rates = OMAP_MCBSP_RATES ,
. formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE ,
} ,
. ops = & mcbsp_dai_ops ,
2008-04-25 15:55:19 +04:00
} ;
2008-10-09 16:57:22 +04:00
2013-03-21 14:33:25 +04:00
static const struct snd_soc_component_driver omap_mcbsp_component = {
. name = " omap-mcbsp " ,
} ;
2010-09-08 07:23:43 +04:00
static int omap_mcbsp_st_info_volsw ( struct snd_kcontrol * kcontrol ,
2010-02-22 15:21:12 +03:00
struct snd_ctl_elem_info * uinfo )
{
struct soc_mixer_control * mc =
( struct soc_mixer_control * ) kcontrol - > private_value ;
int max = mc - > max ;
int min = mc - > min ;
uinfo - > type = SNDRV_CTL_ELEM_TYPE_INTEGER ;
uinfo - > count = 1 ;
uinfo - > value . integer . min = min ;
uinfo - > value . integer . max = max ;
return 0 ;
}
2012-08-22 14:11:43 +04:00
# define OMAP_MCBSP_ST_CHANNEL_VOLUME(channel) \
2010-02-22 15:21:12 +03:00
static int \
2012-08-22 14:11:43 +04:00
omap_mcbsp_set_st_ch # # channel # # _volume ( struct snd_kcontrol * kc , \
2010-02-22 15:21:12 +03:00
struct snd_ctl_elem_value * uc ) \
{ \
2012-02-14 20:20:58 +04:00
struct snd_soc_dai * cpu_dai = snd_kcontrol_chip ( kc ) ; \
struct omap_mcbsp * mcbsp = snd_soc_dai_get_drvdata ( cpu_dai ) ; \
2010-02-22 15:21:12 +03:00
struct soc_mixer_control * mc = \
( struct soc_mixer_control * ) kc - > private_value ; \
int max = mc - > max ; \
int min = mc - > min ; \
int val = uc - > value . integer . value [ 0 ] ; \
\
if ( val < min | | val > max ) \
return - EINVAL ; \
\
/* OMAP McBSP implementation uses index values 0..4 */ \
2012-02-14 20:20:58 +04:00
return omap_st_set_chgain ( mcbsp , channel , val ) ; \
2012-08-22 14:11:43 +04:00
} \
\
2010-02-22 15:21:12 +03:00
static int \
2012-08-22 14:11:43 +04:00
omap_mcbsp_get_st_ch # # channel # # _volume ( struct snd_kcontrol * kc , \
2010-02-22 15:21:12 +03:00
struct snd_ctl_elem_value * uc ) \
{ \
2012-02-14 20:20:58 +04:00
struct snd_soc_dai * cpu_dai = snd_kcontrol_chip ( kc ) ; \
struct omap_mcbsp * mcbsp = snd_soc_dai_get_drvdata ( cpu_dai ) ; \
2010-02-22 15:21:12 +03:00
s16 chgain ; \
\
2012-02-14 20:20:58 +04:00
if ( omap_st_get_chgain ( mcbsp , channel , & chgain ) ) \
2010-02-22 15:21:12 +03:00
return - EAGAIN ; \
\
uc - > value . integer . value [ 0 ] = chgain ; \
return 0 ; \
}
2012-08-22 14:11:43 +04:00
OMAP_MCBSP_ST_CHANNEL_VOLUME ( 0 )
OMAP_MCBSP_ST_CHANNEL_VOLUME ( 1 )
2010-02-22 15:21:12 +03:00
static int omap_mcbsp_st_put_mode ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
2012-02-14 20:20:58 +04:00
struct snd_soc_dai * cpu_dai = snd_kcontrol_chip ( kcontrol ) ;
struct omap_mcbsp * mcbsp = snd_soc_dai_get_drvdata ( cpu_dai ) ;
2010-02-22 15:21:12 +03:00
u8 value = ucontrol - > value . integer . value [ 0 ] ;
2012-02-14 20:20:58 +04:00
if ( value = = omap_st_is_enabled ( mcbsp ) )
2010-02-22 15:21:12 +03:00
return 0 ;
if ( value )
2012-02-14 20:20:58 +04:00
omap_st_enable ( mcbsp ) ;
2010-02-22 15:21:12 +03:00
else
2012-02-14 20:20:58 +04:00
omap_st_disable ( mcbsp ) ;
2010-02-22 15:21:12 +03:00
return 1 ;
}
static int omap_mcbsp_st_get_mode ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
2012-02-14 20:20:58 +04:00
struct snd_soc_dai * cpu_dai = snd_kcontrol_chip ( kcontrol ) ;
struct omap_mcbsp * mcbsp = snd_soc_dai_get_drvdata ( cpu_dai ) ;
2010-02-22 15:21:12 +03:00
2012-02-14 20:20:58 +04:00
ucontrol - > value . integer . value [ 0 ] = omap_st_is_enabled ( mcbsp ) ;
2010-02-22 15:21:12 +03:00
return 0 ;
}
2012-08-22 14:11:42 +04:00
# define OMAP_MCBSP_ST_CONTROLS(port) \
static const struct snd_kcontrol_new omap_mcbsp # # port # # _st_controls [ ] = { \
SOC_SINGLE_EXT ( " McBSP " # port " Sidetone Switch " , 1 , 0 , 1 , 0 , \
omap_mcbsp_st_get_mode , omap_mcbsp_st_put_mode ) , \
OMAP_MCBSP_SOC_SINGLE_S16_EXT ( " McBSP " # port " Sidetone Channel 0 Volume " , \
- 32768 , 32767 , \
omap_mcbsp_get_st_ch0_volume , \
omap_mcbsp_set_st_ch0_volume ) , \
OMAP_MCBSP_SOC_SINGLE_S16_EXT ( " McBSP " # port " Sidetone Channel 1 Volume " , \
- 32768 , 32767 , \
omap_mcbsp_get_st_ch1_volume , \
omap_mcbsp_set_st_ch1_volume ) , \
}
2010-02-22 15:21:12 +03:00
2012-08-22 14:11:42 +04:00
OMAP_MCBSP_ST_CONTROLS ( 2 ) ;
OMAP_MCBSP_ST_CONTROLS ( 3 ) ;
2010-02-22 15:21:12 +03:00
2014-04-28 18:07:23 +04:00
int omap_mcbsp_st_add_controls ( struct snd_soc_pcm_runtime * rtd , int port_id )
2010-02-22 15:21:12 +03:00
{
2012-02-14 20:20:58 +04:00
struct snd_soc_dai * cpu_dai = rtd - > cpu_dai ;
struct omap_mcbsp * mcbsp = snd_soc_dai_get_drvdata ( cpu_dai ) ;
2012-08-22 14:11:41 +04:00
if ( ! mcbsp - > st_data ) {
dev_warn ( mcbsp - > dev , " No sidetone data for port \n " ) ;
return 0 ;
}
2010-02-22 15:21:12 +03:00
2014-04-28 18:07:23 +04:00
switch ( port_id ) {
2012-02-14 20:20:58 +04:00
case 2 : /* McBSP 2 */
return snd_soc_add_dai_controls ( cpu_dai ,
omap_mcbsp2_st_controls ,
2010-02-22 15:21:12 +03:00
ARRAY_SIZE ( omap_mcbsp2_st_controls ) ) ;
2012-02-14 20:20:58 +04:00
case 3 : /* McBSP 3 */
return snd_soc_add_dai_controls ( cpu_dai ,
omap_mcbsp3_st_controls ,
2010-02-22 15:21:12 +03:00
ARRAY_SIZE ( omap_mcbsp3_st_controls ) ) ;
default :
2014-04-28 18:07:23 +04:00
dev_err ( mcbsp - > dev , " Port %d not supported \n " , port_id ) ;
2010-02-22 15:21:12 +03:00
break ;
}
return - EINVAL ;
}
EXPORT_SYMBOL_GPL ( omap_mcbsp_st_add_controls ) ;
2012-08-16 17:41:08 +04:00
static struct omap_mcbsp_platform_data omap2420_pdata = {
. reg_step = 4 ,
. reg_size = 2 ,
} ;
static struct omap_mcbsp_platform_data omap2430_pdata = {
. reg_step = 4 ,
. reg_size = 4 ,
. has_ccr = true ,
} ;
static struct omap_mcbsp_platform_data omap3_pdata = {
. reg_step = 4 ,
. reg_size = 4 ,
. has_ccr = true ,
. has_wakeup = true ,
} ;
static struct omap_mcbsp_platform_data omap4_pdata = {
. reg_step = 4 ,
. reg_size = 4 ,
. has_ccr = true ,
. has_wakeup = true ,
} ;
static const struct of_device_id omap_mcbsp_of_match [ ] = {
{
. compatible = " ti,omap2420-mcbsp " ,
. data = & omap2420_pdata ,
} ,
{
. compatible = " ti,omap2430-mcbsp " ,
. data = & omap2430_pdata ,
} ,
{
. compatible = " ti,omap3-mcbsp " ,
. data = & omap3_pdata ,
} ,
{
. compatible = " ti,omap4-mcbsp " ,
. data = & omap4_pdata ,
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , omap_mcbsp_of_match ) ;
2012-12-07 18:26:29 +04:00
static int asoc_mcbsp_probe ( struct platform_device * pdev )
2010-03-17 23:15:21 +03:00
{
2012-02-14 16:52:42 +04:00
struct omap_mcbsp_platform_data * pdata = dev_get_platdata ( & pdev - > dev ) ;
struct omap_mcbsp * mcbsp ;
2012-08-16 17:41:08 +04:00
const struct of_device_id * match ;
2012-02-14 20:20:58 +04:00
int ret ;
2012-08-16 17:41:08 +04:00
match = of_match_device ( omap_mcbsp_of_match , & pdev - > dev ) ;
if ( match ) {
struct device_node * node = pdev - > dev . of_node ;
int buffer_size ;
pdata = devm_kzalloc ( & pdev - > dev ,
sizeof ( struct omap_mcbsp_platform_data ) ,
GFP_KERNEL ) ;
if ( ! pdata )
return - ENOMEM ;
memcpy ( pdata , match - > data , sizeof ( * pdata ) ) ;
if ( ! of_property_read_u32 ( node , " ti,buffer-size " , & buffer_size ) )
pdata - > buffer_size = buffer_size ;
} else if ( ! pdata ) {
2012-02-14 16:52:42 +04:00
dev_err ( & pdev - > dev , " missing platform data. \n " ) ;
return - EINVAL ;
}
mcbsp = devm_kzalloc ( & pdev - > dev , sizeof ( struct omap_mcbsp ) , GFP_KERNEL ) ;
if ( ! mcbsp )
return - ENOMEM ;
mcbsp - > id = pdev - > id ;
mcbsp - > pdata = pdata ;
mcbsp - > dev = & pdev - > dev ;
platform_set_drvdata ( pdev , mcbsp ) ;
ret = omap_mcbsp_init ( pdev ) ;
2014-04-16 16:46:16 +04:00
if ( ret )
return ret ;
2012-02-14 20:20:58 +04:00
2014-07-08 20:25:23 +04:00
ret = devm_snd_soc_register_component ( & pdev - > dev ,
& omap_mcbsp_component ,
& omap_mcbsp_dai , 1 ) ;
2014-04-16 16:46:16 +04:00
if ( ret )
return ret ;
return omap_pcm_platform_register ( & pdev - > dev ) ;
2010-03-17 23:15:21 +03:00
}
2012-12-07 18:26:29 +04:00
static int asoc_mcbsp_remove ( struct platform_device * pdev )
2010-03-17 23:15:21 +03:00
{
2012-02-14 16:52:42 +04:00
struct omap_mcbsp * mcbsp = platform_get_drvdata ( pdev ) ;
if ( mcbsp - > pdata - > ops & & mcbsp - > pdata - > ops - > free )
mcbsp - > pdata - > ops - > free ( mcbsp - > id ) ;
omap_mcbsp_sysfs_remove ( mcbsp ) ;
clk_put ( mcbsp - > fclk ) ;
2010-03-17 23:15:21 +03:00
return 0 ;
}
static struct platform_driver asoc_mcbsp_driver = {
. driver = {
2012-02-14 20:20:58 +04:00
. name = " omap-mcbsp " ,
2012-08-16 17:41:08 +04:00
. of_match_table = omap_mcbsp_of_match ,
2010-03-17 23:15:21 +03:00
} ,
. probe = asoc_mcbsp_probe ,
2012-12-07 18:26:29 +04:00
. remove = asoc_mcbsp_remove ,
2010-03-17 23:15:21 +03:00
} ;
2011-11-25 06:12:16 +04:00
module_platform_driver ( asoc_mcbsp_driver ) ;
2008-12-03 22:26:35 +03:00
2011-08-11 16:44:57 +04:00
MODULE_AUTHOR ( " Jarkko Nikula <jarkko.nikula@bitmer.com> " ) ;
2008-04-25 15:55:19 +04:00
MODULE_DESCRIPTION ( " OMAP I2S SoC Interface " ) ;
MODULE_LICENSE ( " GPL " ) ;
2012-07-12 17:08:16 +04:00
MODULE_ALIAS ( " platform:omap-mcbsp " ) ;