aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/fsl/fsl_ssi.c
diff options
context:
space:
mode:
authorTimur Tabi <timur@freescale.com>2008-07-28 18:04:39 -0400
committerTakashi Iwai <tiwai@suse.de>2008-07-29 06:27:10 -0400
commitbe41e941d5f1a48bde7f44d09d56e8d2605f98e1 (patch)
treef8f519262a5466623538ce73c1d1c8ef34b9169e /sound/soc/fsl/fsl_ssi.c
parenta7b815169aae65072017efb1fba9dcecc82ba7c1 (diff)
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>
Diffstat (limited to 'sound/soc/fsl/fsl_ssi.c')
-rw-r--r--sound/soc/fsl/fsl_ssi.c74
1 files changed, 64 insertions, 10 deletions
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 71bff33f5528..157a7895ffa1 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -67,6 +67,8 @@
67 * @ssi: pointer to the SSI's registers 67 * @ssi: pointer to the SSI's registers
68 * @ssi_phys: physical address of the SSI registers 68 * @ssi_phys: physical address of the SSI registers
69 * @irq: IRQ of this SSI 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 * @dev: struct device pointer 72 * @dev: struct device pointer
71 * @playback: the number of playback streams opened 73 * @playback: the number of playback streams opened
72 * @capture: the number of capture streams opened 74 * @capture: the number of capture streams opened
@@ -79,6 +81,8 @@ struct fsl_ssi_private {
79 struct ccsr_ssi __iomem *ssi; 81 struct ccsr_ssi __iomem *ssi;
80 dma_addr_t ssi_phys; 82 dma_addr_t ssi_phys;
81 unsigned int irq; 83 unsigned int irq;
84 struct snd_pcm_substream *first_stream;
85 struct snd_pcm_substream *second_stream;
82 struct device *dev; 86 struct device *dev;
83 unsigned int playback; 87 unsigned int playback;
84 unsigned int capture; 88 unsigned int capture;
@@ -342,6 +346,49 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream)
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 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 392 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
346 ssi_private->playback++; 393 ssi_private->playback++;
347 394
@@ -371,18 +418,16 @@ static int fsl_ssi_prepare(struct snd_pcm_substream *substream)
371 struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data; 418 struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data;
372 419
373 struct ccsr_ssi __iomem *ssi = ssi_private->ssi; 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 clrsetbits_be32(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl); 429 clrsetbits_be32(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl);
382 else 430 }
383 clrsetbits_be32(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl);
384
385 setbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
386 431
387 return 0; 432 return 0;
388} 433}
@@ -407,9 +452,13 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd)
407 case SNDRV_PCM_TRIGGER_RESUME: 452 case SNDRV_PCM_TRIGGER_RESUME:
408 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 453 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
409 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 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 } else { 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 * I think we need this delay to allow time for the SSI 464 * I think we need this delay to allow time for the SSI
@@ -452,6 +501,11 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream)
452 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) 501 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
453 ssi_private->capture--; 502 ssi_private->capture--;
454 503
504 if (ssi_private->first_stream == substream)
505 ssi_private->first_stream = ssi_private->second_stream;
506
507 ssi_private->second_stream = NULL;
508
455 /* 509 /*
456 * If this is the last active substream, disable the SSI and release 510 * If this is the last active substream, disable the SSI and release
457 * the IRQ. 511 * the IRQ.