diff options
author | Graeme Gregory <gg@slimlogic.co.uk> | 2009-11-09 14:02:15 -0500 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2009-11-10 06:58:21 -0500 |
commit | 5f63ef9909c187581c7f2c28fbc93866a0d59f7f (patch) | |
tree | 5432fc2068a7ac31fbbaee27627c30ed29226be3 /sound | |
parent | 9e5d86fe6a401f7957f6ea02ee300db0f6c03d03 (diff) |
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>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/omap/omap-mcbsp.c | 63 |
1 files changed, 37 insertions, 26 deletions
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index 3341f49402ca..45be94201c89 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c | |||
@@ -49,6 +49,8 @@ struct omap_mcbsp_data { | |||
49 | */ | 49 | */ |
50 | int active; | 50 | int active; |
51 | int configured; | 51 | int configured; |
52 | unsigned int in_freq; | ||
53 | int clk_div; | ||
52 | }; | 54 | }; |
53 | 55 | ||
54 | #define to_mcbsp(priv) container_of((priv), struct omap_mcbsp_data, bus_id) | 56 | #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, | |||
257 | int dma, bus_id = mcbsp_data->bus_id, id = cpu_dai->id; | 259 | int dma, bus_id = mcbsp_data->bus_id, id = cpu_dai->id; |
258 | int wlen, channels, wpf, sync_mode = OMAP_DMA_SYNC_ELEMENT; | 260 | int wlen, channels, wpf, sync_mode = OMAP_DMA_SYNC_ELEMENT; |
259 | unsigned long port; | 261 | unsigned long port; |
260 | unsigned int format; | 262 | unsigned int format, div, framesize, master; |
261 | 263 | ||
262 | if (cpu_class_is_omap1()) { | 264 | if (cpu_class_is_omap1()) { |
263 | dma = omap1_dma_reqs[bus_id][substream->stream]; | 265 | dma = omap1_dma_reqs[bus_id][substream->stream]; |
@@ -294,28 +296,19 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, | |||
294 | 296 | ||
295 | format = mcbsp_data->fmt & SND_SOC_DAIFMT_FORMAT_MASK; | 297 | format = mcbsp_data->fmt & SND_SOC_DAIFMT_FORMAT_MASK; |
296 | wpf = channels = params_channels(params); | 298 | wpf = channels = params_channels(params); |
297 | switch (channels) { | 299 | if (channels == 2 && format == SND_SOC_DAIFMT_I2S) { |
298 | case 2: | 300 | /* Use dual-phase frames */ |
299 | if (format == SND_SOC_DAIFMT_I2S) { | 301 | regs->rcr2 |= RPHASE; |
300 | /* Use dual-phase frames */ | 302 | regs->xcr2 |= XPHASE; |
301 | regs->rcr2 |= RPHASE; | 303 | /* Set 1 word per (McBSP) frame for phase1 and phase2 */ |
302 | regs->xcr2 |= XPHASE; | 304 | wpf--; |
303 | /* Set 1 word per (McBSP) frame for phase1 and phase2 */ | 305 | regs->rcr2 |= RFRLEN2(wpf - 1); |
304 | wpf--; | 306 | regs->xcr2 |= XFRLEN2(wpf - 1); |
305 | regs->rcr2 |= RFRLEN2(wpf - 1); | ||
306 | regs->xcr2 |= XFRLEN2(wpf - 1); | ||
307 | } | ||
308 | case 1: | ||
309 | case 4: | ||
310 | /* Set word per (McBSP) frame for phase1 */ | ||
311 | regs->rcr1 |= RFRLEN1(wpf - 1); | ||
312 | regs->xcr1 |= XFRLEN1(wpf - 1); | ||
313 | break; | ||
314 | default: | ||
315 | /* Unsupported number of channels */ | ||
316 | return -EINVAL; | ||
317 | } | 307 | } |
318 | 308 | ||
309 | regs->rcr1 |= RFRLEN1(wpf - 1); | ||
310 | regs->xcr1 |= XFRLEN1(wpf - 1); | ||
311 | |||
319 | switch (params_format(params)) { | 312 | switch (params_format(params)) { |
320 | case SNDRV_PCM_FORMAT_S16_LE: | 313 | case SNDRV_PCM_FORMAT_S16_LE: |
321 | /* Set word lengths */ | 314 | /* Set word lengths */ |
@@ -330,15 +323,30 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, | |||
330 | return -EINVAL; | 323 | return -EINVAL; |
331 | } | 324 | } |
332 | 325 | ||
326 | /* In McBSP master modes, FRAME (i.e. sample rate) is generated | ||
327 | * by _counting_ BCLKs. Calculate frame size in BCLKs */ | ||
328 | master = mcbsp_data->fmt & SND_SOC_DAIFMT_MASTER_MASK; | ||
329 | if (master == SND_SOC_DAIFMT_CBS_CFS) { | ||
330 | div = mcbsp_data->clk_div ? mcbsp_data->clk_div : 1; | ||
331 | framesize = (mcbsp_data->in_freq / div) / params_rate(params); | ||
332 | |||
333 | if (framesize < wlen * channels) { | ||
334 | printk(KERN_ERR "%s: not enough bandwidth for desired rate and " | ||
335 | "channels\n", __func__); | ||
336 | return -EINVAL; | ||
337 | } | ||
338 | } else | ||
339 | framesize = wlen * channels; | ||
340 | |||
333 | /* Set FS period and length in terms of bit clock periods */ | 341 | /* Set FS period and length in terms of bit clock periods */ |
334 | switch (format) { | 342 | switch (format) { |
335 | case SND_SOC_DAIFMT_I2S: | 343 | case SND_SOC_DAIFMT_I2S: |
336 | regs->srgr2 |= FPER(wlen * channels - 1); | 344 | regs->srgr2 |= FPER(framesize - 1); |
337 | regs->srgr1 |= FWID(wlen - 1); | 345 | regs->srgr1 |= FWID((framesize >> 1) - 1); |
338 | break; | 346 | break; |
339 | case SND_SOC_DAIFMT_DSP_A: | 347 | case SND_SOC_DAIFMT_DSP_A: |
340 | case SND_SOC_DAIFMT_DSP_B: | 348 | case SND_SOC_DAIFMT_DSP_B: |
341 | regs->srgr2 |= FPER(wlen * channels - 1); | 349 | regs->srgr2 |= FPER(framesize - 1); |
342 | regs->srgr1 |= FWID(0); | 350 | regs->srgr1 |= FWID(0); |
343 | break; | 351 | break; |
344 | } | 352 | } |
@@ -454,6 +462,7 @@ static int omap_mcbsp_dai_set_clkdiv(struct snd_soc_dai *cpu_dai, | |||
454 | if (div_id != OMAP_MCBSP_CLKGDV) | 462 | if (div_id != OMAP_MCBSP_CLKGDV) |
455 | return -ENODEV; | 463 | return -ENODEV; |
456 | 464 | ||
465 | mcbsp_data->clk_div = div; | ||
457 | regs->srgr1 |= CLKGDV(div - 1); | 466 | regs->srgr1 |= CLKGDV(div - 1); |
458 | 467 | ||
459 | return 0; | 468 | return 0; |
@@ -554,6 +563,8 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, | |||
554 | struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs; | 563 | struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs; |
555 | int err = 0; | 564 | int err = 0; |
556 | 565 | ||
566 | mcbsp_data->in_freq = freq; | ||
567 | |||
557 | switch (clk_id) { | 568 | switch (clk_id) { |
558 | case OMAP_MCBSP_SYSCLK_CLK: | 569 | case OMAP_MCBSP_SYSCLK_CLK: |
559 | regs->srgr2 |= CLKSM; | 570 | regs->srgr2 |= CLKSM; |
@@ -598,13 +609,13 @@ static struct snd_soc_dai_ops omap_mcbsp_dai_ops = { | |||
598 | .id = (link_id), \ | 609 | .id = (link_id), \ |
599 | .playback = { \ | 610 | .playback = { \ |
600 | .channels_min = 1, \ | 611 | .channels_min = 1, \ |
601 | .channels_max = 4, \ | 612 | .channels_max = 16, \ |
602 | .rates = OMAP_MCBSP_RATES, \ | 613 | .rates = OMAP_MCBSP_RATES, \ |
603 | .formats = SNDRV_PCM_FMTBIT_S16_LE, \ | 614 | .formats = SNDRV_PCM_FMTBIT_S16_LE, \ |
604 | }, \ | 615 | }, \ |
605 | .capture = { \ | 616 | .capture = { \ |
606 | .channels_min = 1, \ | 617 | .channels_min = 1, \ |
607 | .channels_max = 4, \ | 618 | .channels_max = 16, \ |
608 | .rates = OMAP_MCBSP_RATES, \ | 619 | .rates = OMAP_MCBSP_RATES, \ |
609 | .formats = SNDRV_PCM_FMTBIT_S16_LE, \ | 620 | .formats = SNDRV_PCM_FMTBIT_S16_LE, \ |
610 | }, \ | 621 | }, \ |