Skip to content

Commit 5e538ec

Browse files
Timur Tabibroonie
authored andcommitted
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 <[email protected]> Acked-by: Liam Girdwood <[email protected]> Signed-off-by: Mark Brown <[email protected]>
1 parent 32d2a0c commit 5e538ec

File tree

1 file changed

+68
-77
lines changed

1 file changed

+68
-77
lines changed

sound/soc/fsl/fsl_ssi.c

Lines changed: 68 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@
7878
* @second_stream: pointer to second stream
7979
* @playback: the number of playback streams opened
8080
* @capture: the number of capture streams opened
81-
* @asynchronous: 0=synchronous mode, 1=asynchronous mode
8281
* @cpu_dai: the CPU DAI for this device
8382
* @dev_attr: the sysfs device attribute structure
8483
* @stats: SSI statistics
@@ -90,9 +89,6 @@ struct fsl_ssi_private {
9089
unsigned int irq;
9190
struct snd_pcm_substream *first_stream;
9291
struct snd_pcm_substream *second_stream;
93-
unsigned int playback;
94-
unsigned int capture;
95-
int asynchronous;
9692
unsigned int fifo_depth;
9793
struct snd_soc_dai_driver cpu_dai_drv;
9894
struct device_attribute dev_attr;
@@ -281,15 +277,19 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
281277
struct snd_soc_dai *dai)
282278
{
283279
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;
285283

286284
/*
287285
* If this is the first stream opened, then request the IRQ
288286
* and initialize the SSI registers.
289287
*/
290-
if (!ssi_private->playback && !ssi_private->capture) {
288+
if (!ssi_private->first_stream) {
291289
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
292290

291+
ssi_private->first_stream = substream;
292+
293293
/*
294294
* Section 16.5 of the MPC8610 reference manual says that the
295295
* 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,
306306
clrsetbits_be32(&ssi->scr,
307307
CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN,
308308
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));
310310

311311
out_be32(&ssi->stcr,
312312
CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
@@ -323,7 +323,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
323323
* master.
324324
*/
325325

326-
/* 4. Enable the interrupts and DMA requests */
326+
/* Enable the interrupts and DMA requests */
327327
out_be32(&ssi->sier, SIER_FLAGS);
328328

329329
/*
@@ -352,58 +352,47 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
352352
* this is bad is because at this point, the PCM driver has not
353353
* finished initializing the DMA controller.
354354
*/
355-
}
356-
357-
if (!ssi_private->first_stream)
358-
ssi_private->first_stream = substream;
359-
else {
360-
/* This is the second stream open, so we need to impose sample
361-
* rate and maybe sample size constraints. Note that this can
362-
* cause a race condition if the second stream is opened before
363-
* the first stream is fully initialized.
364-
*
365-
* We provide some protection by checking to make sure the first
366-
* stream is initialized, but it's not perfect. ALSA sometimes
367-
* re-initializes the driver with a different sample rate or
368-
* size. If the second stream is opened before the first stream
369-
* has received its final parameters, then the second stream may
370-
* be constrained to the wrong sample rate or size.
371-
*
372-
* FIXME: This code does not handle opening and closing streams
373-
* repeatedly. If you open two streams and then close the first
374-
* one, you may not be able to open another stream until you
375-
* close the second one as well.
376-
*/
377-
struct snd_pcm_runtime *first_runtime =
378-
ssi_private->first_stream->runtime;
379-
380-
if (!first_runtime->sample_bits) {
381-
dev_err(substream->pcm->card->dev,
382-
"set sample size in %s stream first\n",
383-
substream->stream == SNDRV_PCM_STREAM_PLAYBACK
384-
? "capture" : "playback");
385-
return -EAGAIN;
386-
}
355+
} else {
356+
if (synchronous) {
357+
struct snd_pcm_runtime *first_runtime =
358+
ssi_private->first_stream->runtime;
359+
/*
360+
* This is the second stream open, and we're in
361+
* synchronous mode, so we need to impose sample
362+
* sample size constraints. This is because STCCR is
363+
* used for playback and capture in synchronous mode,
364+
* so there's no way to specify different word
365+
* lengths.
366+
*
367+
* Note that this can cause a race condition if the
368+
* second stream is opened before the first stream is
369+
* fully initialized. We provide some protection by
370+
* checking to make sure the first stream is
371+
* initialized, but it's not perfect. ALSA sometimes
372+
* re-initializes the driver with a different sample
373+
* rate or size. If the second stream is opened
374+
* before the first stream has received its final
375+
* parameters, then the second stream may be
376+
* constrained to the wrong sample rate or size.
377+
*/
378+
if (!first_runtime->sample_bits) {
379+
dev_err(substream->pcm->card->dev,
380+
"set sample size in %s stream first\n",
381+
substream->stream ==
382+
SNDRV_PCM_STREAM_PLAYBACK
383+
? "capture" : "playback");
384+
return -EAGAIN;
385+
}
387386

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)
393387
snd_pcm_hw_constraint_minmax(substream->runtime,
394388
SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
395389
first_runtime->sample_bits,
396390
first_runtime->sample_bits);
391+
}
397392

398393
ssi_private->second_stream = substream;
399394
}
400395

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-
407396
return 0;
408397
}
409398

@@ -424,24 +413,35 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
424413
struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *cpu_dai)
425414
{
426415
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;
427421

428-
if (substream == ssi_private->first_stream) {
429-
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
430-
unsigned int sample_size =
431-
snd_pcm_format_width(params_format(hw_params));
432-
u32 wl = CCSR_SSI_SxCCR_WL(sample_size);
422+
/*
423+
* If we're in synchronous mode, and the SSI is already enabled,
424+
* then STCCR is already set properly.
425+
*/
426+
if (enabled && ssi_private->cpu_dai_drv.symmetric_rates)
427+
return 0;
433428

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+
*/
435438

436-
/* In synchronous mode, the SSI uses STCCR for capture */
437-
if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ||
438-
!ssi_private->asynchronous)
439-
clrsetbits_be32(&ssi->stccr,
440-
CCSR_SSI_SxCCR_WL_MASK, wl);
441-
else
442-
clrsetbits_be32(&ssi->srccr,
443-
CCSR_SSI_SxCCR_WL_MASK, wl);
444-
}
439+
/* In synchronous mode, the SSI uses STCCR for capture */
440+
if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ||
441+
ssi_private->cpu_dai_drv.symmetric_rates)
442+
clrsetbits_be32(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl);
443+
else
444+
clrsetbits_be32(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl);
445445

446446
return 0;
447447
}
@@ -464,7 +464,6 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
464464

465465
switch (cmd) {
466466
case SNDRV_PCM_TRIGGER_START:
467-
clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
468467
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
469468
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
470469
setbits32(&ssi->scr,
@@ -500,12 +499,6 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
500499
struct snd_soc_pcm_runtime *rtd = substream->private_data;
501500
struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
502501

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-
509502
if (ssi_private->first_stream == substream)
510503
ssi_private->first_stream = ssi_private->second_stream;
511504

@@ -514,7 +507,7 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
514507
/*
515508
* If this is the last active substream, disable the SSI.
516509
*/
517-
if (!ssi_private->playback && !ssi_private->capture) {
510+
if (!ssi_private->first_stream) {
518511
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
519512

520513
clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
@@ -688,9 +681,7 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev)
688681
}
689682

690683
/* Are the RX and the TX clocks locked? */
691-
if (of_find_property(np, "fsl,ssi-asynchronous", NULL))
692-
ssi_private->asynchronous = 1;
693-
else
684+
if (!of_find_property(np, "fsl,ssi-asynchronous", NULL))
694685
ssi_private->cpu_dai_drv.symmetric_rates = 1;
695686

696687
/* Determine the FIFO depth. */

0 commit comments

Comments
 (0)