aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/fsl/fsl_ssi.c
diff options
context:
space:
mode:
authorTimur Tabi <timur@freescale.com>2011-09-13 13:59:37 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2011-09-15 19:05:29 -0400
commit5e538ecade22a5ec4c8e18d494db0ecf924254eb (patch)
tree6cd093a7757d7b7c8ac616668175f28541a71f92 /sound/soc/fsl/fsl_ssi.c
parent32d2a0c17d81016215381d337dad876dc972ee83 (diff)
ASoC: improve asynchronous mode support in the fsl_ssi driver
The Freescale SSI audio controller supports "synchronous" and "asynchronous" modes. In synchronous mode, playback and capture use the same input clock, so sample rates must be the same during simultaneous playback and capture. Unfortunately, the code which supports asynchronous mode is just broken in various ways. In particular, it was constraining sample sizes as well as the sample rate. The fix also allows us to simplify the code by eliminating the 'asynchronous', 'playback', and 'capture' variables that were used to keep track of playback and capture streams. Unfortunately, it turns out that simulataneous playback and record does not actually work on the only platform that supports asynchronous mode: the Freescale P1022DS reference board. If a second stream is started, the SSI grinds to halt for both streams. This is true even if the P1022 is configured for synchronous mode, so it's likely a hardware problem that needs to be worked around. Signed-off-by: Timur Tabi <timur@freescale.com> Acked-by: Liam Girdwood <lrg@ti.com> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/fsl/fsl_ssi.c')
-rw-r--r--sound/soc/fsl/fsl_ssi.c145
1 files changed, 68 insertions, 77 deletions
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 06ac2b92faf3..0268cf989736 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -78,7 +78,6 @@
78 * @second_stream: pointer to second stream 78 * @second_stream: pointer to second stream
79 * @playback: the number of playback streams opened 79 * @playback: the number of playback streams opened
80 * @capture: the number of capture streams opened 80 * @capture: the number of capture streams opened
81 * @asynchronous: 0=synchronous mode, 1=asynchronous mode
82 * @cpu_dai: the CPU DAI for this device 81 * @cpu_dai: the CPU DAI for this device
83 * @dev_attr: the sysfs device attribute structure 82 * @dev_attr: the sysfs device attribute structure
84 * @stats: SSI statistics 83 * @stats: SSI statistics
@@ -90,9 +89,6 @@ struct fsl_ssi_private {
90 unsigned int irq; 89 unsigned int irq;
91 struct snd_pcm_substream *first_stream; 90 struct snd_pcm_substream *first_stream;
92 struct snd_pcm_substream *second_stream; 91 struct snd_pcm_substream *second_stream;
93 unsigned int playback;
94 unsigned int capture;
95 int asynchronous;
96 unsigned int fifo_depth; 92 unsigned int fifo_depth;
97 struct snd_soc_dai_driver cpu_dai_drv; 93 struct snd_soc_dai_driver cpu_dai_drv;
98 struct device_attribute dev_attr; 94 struct device_attribute dev_attr;
@@ -281,15 +277,19 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
281 struct snd_soc_dai *dai) 277 struct snd_soc_dai *dai)
282{ 278{
283 struct snd_soc_pcm_runtime *rtd = substream->private_data; 279 struct snd_soc_pcm_runtime *rtd = substream->private_data;
284 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai); 280 struct fsl_ssi_private *ssi_private =
281 snd_soc_dai_get_drvdata(rtd->cpu_dai);
282 int synchronous = ssi_private->cpu_dai_drv.symmetric_rates;
285 283
286 /* 284 /*
287 * If this is the first stream opened, then request the IRQ 285 * If this is the first stream opened, then request the IRQ
288 * and initialize the SSI registers. 286 * and initialize the SSI registers.
289 */ 287 */
290 if (!ssi_private->playback && !ssi_private->capture) { 288 if (!ssi_private->first_stream) {
291 struct ccsr_ssi __iomem *ssi = ssi_private->ssi; 289 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
292 290
291 ssi_private->first_stream = substream;
292
293 /* 293 /*
294 * Section 16.5 of the MPC8610 reference manual says that the 294 * Section 16.5 of the MPC8610 reference manual says that the
295 * SSI needs to be disabled before updating the registers we set 295 * SSI needs to be disabled before updating the registers we set
@@ -306,7 +306,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
306 clrsetbits_be32(&ssi->scr, 306 clrsetbits_be32(&ssi->scr,
307 CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN, 307 CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN,
308 CCSR_SSI_SCR_TFR_CLK_DIS | CCSR_SSI_SCR_I2S_MODE_SLAVE 308 CCSR_SSI_SCR_TFR_CLK_DIS | CCSR_SSI_SCR_I2S_MODE_SLAVE
309 | (ssi_private->asynchronous ? 0 : CCSR_SSI_SCR_SYN)); 309 | (synchronous ? CCSR_SSI_SCR_SYN : 0));
310 310
311 out_be32(&ssi->stcr, 311 out_be32(&ssi->stcr,
312 CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 | 312 CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
@@ -323,7 +323,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
323 * master. 323 * master.
324 */ 324 */
325 325
326 /* 4. Enable the interrupts and DMA requests */ 326 /* Enable the interrupts and DMA requests */
327 out_be32(&ssi->sier, SIER_FLAGS); 327 out_be32(&ssi->sier, SIER_FLAGS);
328 328
329 /* 329 /*
@@ -352,58 +352,47 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
352 * this is bad is because at this point, the PCM driver has not 352 * this is bad is because at this point, the PCM driver has not
353 * finished initializing the DMA controller. 353 * finished initializing the DMA controller.
354 */ 354 */
355 } 355 } else {
356 356 if (synchronous) {
357 if (!ssi_private->first_stream) 357 struct snd_pcm_runtime *first_runtime =
358 ssi_private->first_stream = substream; 358 ssi_private->first_stream->runtime;
359 else { 359 /*
360 /* This is the second stream open, so we need to impose sample 360 * This is the second stream open, and we're in
361 * rate and maybe sample size constraints. Note that this can 361 * synchronous mode, so we need to impose sample
362 * cause a race condition if the second stream is opened before 362 * sample size constraints. This is because STCCR is
363 * the first stream is fully initialized. 363 * used for playback and capture in synchronous mode,
364 * 364 * so there's no way to specify different word
365 * We provide some protection by checking to make sure the first 365 * lengths.
366 * stream is initialized, but it's not perfect. ALSA sometimes 366 *
367 * re-initializes the driver with a different sample rate or 367 * Note that this can cause a race condition if the
368 * size. If the second stream is opened before the first stream 368 * second stream is opened before the first stream is
369 * has received its final parameters, then the second stream may 369 * fully initialized. We provide some protection by
370 * be constrained to the wrong sample rate or size. 370 * checking to make sure the first stream is
371 * 371 * initialized, but it's not perfect. ALSA sometimes
372 * FIXME: This code does not handle opening and closing streams 372 * re-initializes the driver with a different sample
373 * repeatedly. If you open two streams and then close the first 373 * rate or size. If the second stream is opened
374 * one, you may not be able to open another stream until you 374 * before the first stream has received its final
375 * close the second one as well. 375 * parameters, then the second stream may be
376 */ 376 * constrained to the wrong sample rate or size.
377 struct snd_pcm_runtime *first_runtime = 377 */
378 ssi_private->first_stream->runtime; 378 if (!first_runtime->sample_bits) {
379 379 dev_err(substream->pcm->card->dev,
380 if (!first_runtime->sample_bits) { 380 "set sample size in %s stream first\n",
381 dev_err(substream->pcm->card->dev, 381 substream->stream ==
382 "set sample size in %s stream first\n", 382 SNDRV_PCM_STREAM_PLAYBACK
383 substream->stream == SNDRV_PCM_STREAM_PLAYBACK 383 ? "capture" : "playback");
384 ? "capture" : "playback"); 384 return -EAGAIN;
385 return -EAGAIN; 385 }
386 }
387 386
388 /* If we're in synchronous mode, then we need to constrain
389 * the sample size as well. We don't support independent sample
390 * rates in asynchronous mode.
391 */
392 if (!ssi_private->asynchronous)
393 snd_pcm_hw_constraint_minmax(substream->runtime, 387 snd_pcm_hw_constraint_minmax(substream->runtime,
394 SNDRV_PCM_HW_PARAM_SAMPLE_BITS, 388 SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
395 first_runtime->sample_bits, 389 first_runtime->sample_bits,
396 first_runtime->sample_bits); 390 first_runtime->sample_bits);
391 }
397 392
398 ssi_private->second_stream = substream; 393 ssi_private->second_stream = substream;
399 } 394 }
400 395
401 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
402 ssi_private->playback++;
403
404 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
405 ssi_private->capture++;
406
407 return 0; 396 return 0;
408} 397}
409 398
@@ -424,24 +413,35 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
424 struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *cpu_dai) 413 struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *cpu_dai)
425{ 414{
426 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai); 415 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
416 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
417 unsigned int sample_size =
418 snd_pcm_format_width(params_format(hw_params));
419 u32 wl = CCSR_SSI_SxCCR_WL(sample_size);
420 int enabled = in_be32(&ssi->scr) & CCSR_SSI_SCR_SSIEN;
427 421
428 if (substream == ssi_private->first_stream) { 422 /*
429 struct ccsr_ssi __iomem *ssi = ssi_private->ssi; 423 * If we're in synchronous mode, and the SSI is already enabled,
430 unsigned int sample_size = 424 * then STCCR is already set properly.
431 snd_pcm_format_width(params_format(hw_params)); 425 */
432 u32 wl = CCSR_SSI_SxCCR_WL(sample_size); 426 if (enabled && ssi_private->cpu_dai_drv.symmetric_rates)
427 return 0;
433 428
434 /* The SSI should always be disabled at this points (SSIEN=0) */ 429 /*
430 * FIXME: The documentation says that SxCCR[WL] should not be
431 * modified while the SSI is enabled. The only time this can
432 * happen is if we're trying to do simultaneous playback and
433 * capture in asynchronous mode. Unfortunately, I have been enable
434 * to get that to work at all on the P1022DS. Therefore, we don't
435 * bother to disable/enable the SSI when setting SxCCR[WL], because
436 * the SSI will stop anyway. Maybe one day, this will get fixed.
437 */
435 438
436 /* In synchronous mode, the SSI uses STCCR for capture */ 439 /* In synchronous mode, the SSI uses STCCR for capture */
437 if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) || 440 if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ||
438 !ssi_private->asynchronous) 441 ssi_private->cpu_dai_drv.symmetric_rates)
439 clrsetbits_be32(&ssi->stccr, 442 clrsetbits_be32(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl);
440 CCSR_SSI_SxCCR_WL_MASK, wl); 443 else
441 else 444 clrsetbits_be32(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl);
442 clrsetbits_be32(&ssi->srccr,
443 CCSR_SSI_SxCCR_WL_MASK, wl);
444 }
445 445
446 return 0; 446 return 0;
447} 447}
@@ -464,7 +464,6 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
464 464
465 switch (cmd) { 465 switch (cmd) {
466 case SNDRV_PCM_TRIGGER_START: 466 case SNDRV_PCM_TRIGGER_START:
467 clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
468 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 467 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
469 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 468 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
470 setbits32(&ssi->scr, 469 setbits32(&ssi->scr,
@@ -500,12 +499,6 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
500 struct snd_soc_pcm_runtime *rtd = substream->private_data; 499 struct snd_soc_pcm_runtime *rtd = substream->private_data;
501 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai); 500 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
502 501
503 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
504 ssi_private->playback--;
505
506 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
507 ssi_private->capture--;
508
509 if (ssi_private->first_stream == substream) 502 if (ssi_private->first_stream == substream)
510 ssi_private->first_stream = ssi_private->second_stream; 503 ssi_private->first_stream = ssi_private->second_stream;
511 504
@@ -514,7 +507,7 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
514 /* 507 /*
515 * If this is the last active substream, disable the SSI. 508 * If this is the last active substream, disable the SSI.
516 */ 509 */
517 if (!ssi_private->playback && !ssi_private->capture) { 510 if (!ssi_private->first_stream) {
518 struct ccsr_ssi __iomem *ssi = ssi_private->ssi; 511 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
519 512
520 clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN); 513 clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
@@ -688,9 +681,7 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev)
688 } 681 }
689 682
690 /* Are the RX and the TX clocks locked? */ 683 /* Are the RX and the TX clocks locked? */
691 if (of_find_property(np, "fsl,ssi-asynchronous", NULL)) 684 if (!of_find_property(np, "fsl,ssi-asynchronous", NULL))
692 ssi_private->asynchronous = 1;
693 else
694 ssi_private->cpu_dai_drv.symmetric_rates = 1; 685 ssi_private->cpu_dai_drv.symmetric_rates = 1;
695 686
696 /* Determine the FIFO depth. */ 687 /* Determine the FIFO depth. */