ASoC: omap-mcbsp - add support for upto 16 channels.

This patch increases the number of supported audio channels from 4
to 16 and has been sponsored by Shotspotter Inc. It also fixes a
FSYNC rate calculation bug when McBSP is FSYNC master.

Signed-off-by: Graeme Gregory <gg@slimlogic.co.uk>
Signed-off-by: Liam Girdwood <lrg@slimlogic.co.uk>
Acked-by: Peter Ujfalusi <peter.ujfalusi@nokia.com>
Tested-by: Peter Ujfalusi <peter.ujfalusi@nokia.com>
Acked-by: Jarkko Nikula <jhnikula@gmail.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
This commit is contained in:
Graeme Gregory 2009-11-09 19:02:15 +00:00 committed by Mark Brown
parent 9e5d86fe6a
commit 5f63ef9909

View file

@ -49,6 +49,8 @@ struct omap_mcbsp_data {
*/ */
int active; int active;
int configured; int configured;
unsigned int in_freq;
int clk_div;
}; };
#define to_mcbsp(priv) container_of((priv), struct omap_mcbsp_data, bus_id) #define to_mcbsp(priv) container_of((priv), struct omap_mcbsp_data, bus_id)
@ -257,7 +259,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
int dma, bus_id = mcbsp_data->bus_id, id = cpu_dai->id; int dma, bus_id = mcbsp_data->bus_id, id = cpu_dai->id;
int wlen, channels, wpf, sync_mode = OMAP_DMA_SYNC_ELEMENT; int wlen, channels, wpf, sync_mode = OMAP_DMA_SYNC_ELEMENT;
unsigned long port; unsigned long port;
unsigned int format; unsigned int format, div, framesize, master;
if (cpu_class_is_omap1()) { if (cpu_class_is_omap1()) {
dma = omap1_dma_reqs[bus_id][substream->stream]; dma = omap1_dma_reqs[bus_id][substream->stream];
@ -294,9 +296,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
format = mcbsp_data->fmt & SND_SOC_DAIFMT_FORMAT_MASK; format = mcbsp_data->fmt & SND_SOC_DAIFMT_FORMAT_MASK;
wpf = channels = params_channels(params); wpf = channels = params_channels(params);
switch (channels) { if (channels == 2 && format == SND_SOC_DAIFMT_I2S) {
case 2:
if (format == SND_SOC_DAIFMT_I2S) {
/* Use dual-phase frames */ /* Use dual-phase frames */
regs->rcr2 |= RPHASE; regs->rcr2 |= RPHASE;
regs->xcr2 |= XPHASE; regs->xcr2 |= XPHASE;
@ -305,16 +305,9 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
regs->rcr2 |= RFRLEN2(wpf - 1); regs->rcr2 |= RFRLEN2(wpf - 1);
regs->xcr2 |= XFRLEN2(wpf - 1); regs->xcr2 |= XFRLEN2(wpf - 1);
} }
case 1:
case 4:
/* Set word per (McBSP) frame for phase1 */
regs->rcr1 |= RFRLEN1(wpf - 1); regs->rcr1 |= RFRLEN1(wpf - 1);
regs->xcr1 |= XFRLEN1(wpf - 1); regs->xcr1 |= XFRLEN1(wpf - 1);
break;
default:
/* Unsupported number of channels */
return -EINVAL;
}
switch (params_format(params)) { switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE: case SNDRV_PCM_FORMAT_S16_LE:
@ -330,15 +323,30 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
return -EINVAL; return -EINVAL;
} }
/* In McBSP master modes, FRAME (i.e. sample rate) is generated
* by _counting_ BCLKs. Calculate frame size in BCLKs */
master = mcbsp_data->fmt & SND_SOC_DAIFMT_MASTER_MASK;
if (master == SND_SOC_DAIFMT_CBS_CFS) {
div = mcbsp_data->clk_div ? mcbsp_data->clk_div : 1;
framesize = (mcbsp_data->in_freq / div) / params_rate(params);
if (framesize < wlen * channels) {
printk(KERN_ERR "%s: not enough bandwidth for desired rate and "
"channels\n", __func__);
return -EINVAL;
}
} else
framesize = wlen * channels;
/* Set FS period and length in terms of bit clock periods */ /* Set FS period and length in terms of bit clock periods */
switch (format) { switch (format) {
case SND_SOC_DAIFMT_I2S: case SND_SOC_DAIFMT_I2S:
regs->srgr2 |= FPER(wlen * channels - 1); regs->srgr2 |= FPER(framesize - 1);
regs->srgr1 |= FWID(wlen - 1); regs->srgr1 |= FWID((framesize >> 1) - 1);
break; break;
case SND_SOC_DAIFMT_DSP_A: case SND_SOC_DAIFMT_DSP_A:
case SND_SOC_DAIFMT_DSP_B: case SND_SOC_DAIFMT_DSP_B:
regs->srgr2 |= FPER(wlen * channels - 1); regs->srgr2 |= FPER(framesize - 1);
regs->srgr1 |= FWID(0); regs->srgr1 |= FWID(0);
break; break;
} }
@ -454,6 +462,7 @@ static int omap_mcbsp_dai_set_clkdiv(struct snd_soc_dai *cpu_dai,
if (div_id != OMAP_MCBSP_CLKGDV) if (div_id != OMAP_MCBSP_CLKGDV)
return -ENODEV; return -ENODEV;
mcbsp_data->clk_div = div;
regs->srgr1 |= CLKGDV(div - 1); regs->srgr1 |= CLKGDV(div - 1);
return 0; return 0;
@ -554,6 +563,8 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs; struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
int err = 0; int err = 0;
mcbsp_data->in_freq = freq;
switch (clk_id) { switch (clk_id) {
case OMAP_MCBSP_SYSCLK_CLK: case OMAP_MCBSP_SYSCLK_CLK:
regs->srgr2 |= CLKSM; regs->srgr2 |= CLKSM;
@ -598,13 +609,13 @@ static struct snd_soc_dai_ops omap_mcbsp_dai_ops = {
.id = (link_id), \ .id = (link_id), \
.playback = { \ .playback = { \
.channels_min = 1, \ .channels_min = 1, \
.channels_max = 4, \ .channels_max = 16, \
.rates = OMAP_MCBSP_RATES, \ .rates = OMAP_MCBSP_RATES, \
.formats = SNDRV_PCM_FMTBIT_S16_LE, \ .formats = SNDRV_PCM_FMTBIT_S16_LE, \
}, \ }, \
.capture = { \ .capture = { \
.channels_min = 1, \ .channels_min = 1, \
.channels_max = 4, \ .channels_max = 16, \
.rates = OMAP_MCBSP_RATES, \ .rates = OMAP_MCBSP_RATES, \
.formats = SNDRV_PCM_FMTBIT_S16_LE, \ .formats = SNDRV_PCM_FMTBIT_S16_LE, \
}, \ }, \