Commit be41e941d5f1a48bde7f44d09d56e8d2605f98e1

Authored by Timur Tabi
Committed by Takashi Iwai
1 parent a7b815169a

ALSA: asoc: restrict sample rate and size in Freescale MPC8610 sound drivers

The Freescale MPC8610 SSI device has the option of using one clock for both
transmit and receive (synchronous mode), or independent clocks (asynchronous).
The SSI driver, however, programs the SSI into synchronous mode and then
tries to program the clock registers independently.  The result is that the wrong
sample size is usually generated during recording.

This patch fixes the discrepancy by restricting the sample rate and sample size
of the playback and capture streams.  The SSI driver remembers which stream
is opened first.  When a second stream is opened, that stream is constrained
to the same sample rate and size as the first stream.

A future version of this driver will lift the sample size restriction.
Supporting independent sample rates is more difficult, because only certain
codecs provide dual independent clocks.

Signed-off-by: Timur Tabi <timur@freescale.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>

Showing 2 changed files with 70 additions and 11 deletions Side-by-side Diff

sound/soc/fsl/fsl_dma.c
... ... @@ -132,12 +132,17 @@
132 132 * Since each link descriptor has a 32-bit byte count field, we set
133 133 * period_bytes_max to the largest 32-bit number. We also have no maximum
134 134 * number of periods.
  135 + *
  136 + * Note that we specify SNDRV_PCM_INFO_JOINT_DUPLEX here, but only because a
  137 + * limitation in the SSI driver requires the sample rates for playback and
  138 + * capture to be the same.
135 139 */
136 140 static const struct snd_pcm_hardware fsl_dma_hardware = {
137 141  
138 142 .info = SNDRV_PCM_INFO_INTERLEAVED |
139 143 SNDRV_PCM_INFO_MMAP |
140   - SNDRV_PCM_INFO_MMAP_VALID,
  144 + SNDRV_PCM_INFO_MMAP_VALID |
  145 + SNDRV_PCM_INFO_JOINT_DUPLEX,
141 146 .formats = FSLDMA_PCM_FORMATS,
142 147 .rates = FSLDMA_PCM_RATES,
143 148 .rate_min = 5512,
sound/soc/fsl/fsl_ssi.c
... ... @@ -67,6 +67,8 @@
67 67 * @ssi: pointer to the SSI's registers
68 68 * @ssi_phys: physical address of the SSI registers
69 69 * @irq: IRQ of this SSI
  70 + * @first_stream: pointer to the stream that was opened first
  71 + * @second_stream: pointer to second stream
70 72 * @dev: struct device pointer
71 73 * @playback: the number of playback streams opened
72 74 * @capture: the number of capture streams opened
... ... @@ -79,6 +81,8 @@
79 81 struct ccsr_ssi __iomem *ssi;
80 82 dma_addr_t ssi_phys;
81 83 unsigned int irq;
  84 + struct snd_pcm_substream *first_stream;
  85 + struct snd_pcm_substream *second_stream;
82 86 struct device *dev;
83 87 unsigned int playback;
84 88 unsigned int capture;
... ... @@ -342,6 +346,49 @@
342 346 */
343 347 }
344 348  
  349 + if (!ssi_private->first_stream)
  350 + ssi_private->first_stream = substream;
  351 + else {
  352 + /* This is the second stream open, so we need to impose sample
  353 + * rate and maybe sample size constraints. Note that this can
  354 + * cause a race condition if the second stream is opened before
  355 + * the first stream is fully initialized.
  356 + *
  357 + * We provide some protection by checking to make sure the first
  358 + * stream is initialized, but it's not perfect. ALSA sometimes
  359 + * re-initializes the driver with a different sample rate or
  360 + * size. If the second stream is opened before the first stream
  361 + * has received its final parameters, then the second stream may
  362 + * be constrained to the wrong sample rate or size.
  363 + *
  364 + * FIXME: This code does not handle opening and closing streams
  365 + * repeatedly. If you open two streams and then close the first
  366 + * one, you may not be able to open another stream until you
  367 + * close the second one as well.
  368 + */
  369 + struct snd_pcm_runtime *first_runtime =
  370 + ssi_private->first_stream->runtime;
  371 +
  372 + if (!first_runtime->rate || !first_runtime->sample_bits) {
  373 + dev_err(substream->pcm->card->dev,
  374 + "set sample rate and size in %s stream first\n",
  375 + substream->stream == SNDRV_PCM_STREAM_PLAYBACK
  376 + ? "capture" : "playback");
  377 + return -EAGAIN;
  378 + }
  379 +
  380 + snd_pcm_hw_constraint_minmax(substream->runtime,
  381 + SNDRV_PCM_HW_PARAM_RATE,
  382 + first_runtime->rate, first_runtime->rate);
  383 +
  384 + snd_pcm_hw_constraint_minmax(substream->runtime,
  385 + SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
  386 + first_runtime->sample_bits,
  387 + first_runtime->sample_bits);
  388 +
  389 + ssi_private->second_stream = substream;
  390 + }
  391 +
345 392 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
346 393 ssi_private->playback++;
347 394  
348 395  
349 396  
350 397  
351 398  
352 399  
... ... @@ -371,19 +418,17 @@
371 418 struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data;
372 419  
373 420 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
374   - u32 wl;
375 421  
376   - wl = CCSR_SSI_SxCCR_WL(snd_pcm_format_width(runtime->format));
  422 + if (substream == ssi_private->first_stream) {
  423 + u32 wl;
377 424  
378   - clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
  425 + /* The SSI should always be disabled at this points (SSIEN=0) */
  426 + wl = CCSR_SSI_SxCCR_WL(snd_pcm_format_width(runtime->format));
379 427  
380   - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  428 + /* In synchronous mode, the SSI uses STCCR for capture */
381 429 clrsetbits_be32(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl);
382   - else
383   - clrsetbits_be32(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl);
  430 + }
384 431  
385   - setbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
386   -
387 432 return 0;
388 433 }
389 434  
390 435  
... ... @@ -407,9 +452,13 @@
407 452 case SNDRV_PCM_TRIGGER_RESUME:
408 453 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
409 454 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
410   - setbits32(&ssi->scr, CCSR_SSI_SCR_TE);
  455 + clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
  456 + setbits32(&ssi->scr,
  457 + CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE);
411 458 } else {
412   - setbits32(&ssi->scr, CCSR_SSI_SCR_RE);
  459 + clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
  460 + setbits32(&ssi->scr,
  461 + CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE);
413 462  
414 463 /*
415 464 * I think we need this delay to allow time for the SSI
... ... @@ -451,6 +500,11 @@
451 500  
452 501 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
453 502 ssi_private->capture--;
  503 +
  504 + if (ssi_private->first_stream == substream)
  505 + ssi_private->first_stream = ssi_private->second_stream;
  506 +
  507 + ssi_private->second_stream = NULL;
454 508  
455 509 /*
456 510 * If this is the last active substream, disable the SSI and release