|
@@ -67,6 +67,8 @@
|
|
|
* @ssi: pointer to the SSI's registers
|
|
|
* @ssi_phys: physical address of the SSI registers
|
|
|
* @irq: IRQ of this SSI
|
|
|
+ * @first_stream: pointer to the stream that was opened first
|
|
|
+ * @second_stream: pointer to second stream
|
|
|
* @dev: struct device pointer
|
|
|
* @playback: the number of playback streams opened
|
|
|
* @capture: the number of capture streams opened
|
|
@@ -79,6 +81,8 @@ struct fsl_ssi_private {
|
|
|
struct ccsr_ssi __iomem *ssi;
|
|
|
dma_addr_t ssi_phys;
|
|
|
unsigned int irq;
|
|
|
+ struct snd_pcm_substream *first_stream;
|
|
|
+ struct snd_pcm_substream *second_stream;
|
|
|
struct device *dev;
|
|
|
unsigned int playback;
|
|
|
unsigned int capture;
|
|
@@ -342,6 +346,49 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream)
|
|
|
*/
|
|
|
}
|
|
|
|
|
|
+ if (!ssi_private->first_stream)
|
|
|
+ ssi_private->first_stream = substream;
|
|
|
+ else {
|
|
|
+ /* This is the second stream open, so we need to impose sample
|
|
|
+ * rate and maybe sample size constraints. Note that this can
|
|
|
+ * cause a race condition if the second stream is opened before
|
|
|
+ * the first stream is fully initialized.
|
|
|
+ *
|
|
|
+ * We provide some protection by checking to make sure the first
|
|
|
+ * stream is initialized, but it's not perfect. ALSA sometimes
|
|
|
+ * re-initializes the driver with a different sample rate or
|
|
|
+ * size. If the second stream is opened before the first stream
|
|
|
+ * has received its final parameters, then the second stream may
|
|
|
+ * be constrained to the wrong sample rate or size.
|
|
|
+ *
|
|
|
+ * FIXME: This code does not handle opening and closing streams
|
|
|
+ * repeatedly. If you open two streams and then close the first
|
|
|
+ * one, you may not be able to open another stream until you
|
|
|
+ * close the second one as well.
|
|
|
+ */
|
|
|
+ struct snd_pcm_runtime *first_runtime =
|
|
|
+ ssi_private->first_stream->runtime;
|
|
|
+
|
|
|
+ if (!first_runtime->rate || !first_runtime->sample_bits) {
|
|
|
+ dev_err(substream->pcm->card->dev,
|
|
|
+ "set sample rate and size in %s stream first\n",
|
|
|
+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK
|
|
|
+ ? "capture" : "playback");
|
|
|
+ return -EAGAIN;
|
|
|
+ }
|
|
|
+
|
|
|
+ snd_pcm_hw_constraint_minmax(substream->runtime,
|
|
|
+ SNDRV_PCM_HW_PARAM_RATE,
|
|
|
+ first_runtime->rate, first_runtime->rate);
|
|
|
+
|
|
|
+ snd_pcm_hw_constraint_minmax(substream->runtime,
|
|
|
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
|
|
|
+ first_runtime->sample_bits,
|
|
|
+ first_runtime->sample_bits);
|
|
|
+
|
|
|
+ ssi_private->second_stream = substream;
|
|
|
+ }
|
|
|
+
|
|
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
|
|
ssi_private->playback++;
|
|
|
|
|
@@ -371,18 +418,16 @@ static int fsl_ssi_prepare(struct snd_pcm_substream *substream)
|
|
|
struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data;
|
|
|
|
|
|
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
|
|
|
- u32 wl;
|
|
|
|
|
|
- wl = CCSR_SSI_SxCCR_WL(snd_pcm_format_width(runtime->format));
|
|
|
+ if (substream == ssi_private->first_stream) {
|
|
|
+ u32 wl;
|
|
|
|
|
|
- clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
|
|
|
+ /* The SSI should always be disabled at this points (SSIEN=0) */
|
|
|
+ wl = CCSR_SSI_SxCCR_WL(snd_pcm_format_width(runtime->format));
|
|
|
|
|
|
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
|
|
+ /* In synchronous mode, the SSI uses STCCR for capture */
|
|
|
clrsetbits_be32(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl);
|
|
|
- else
|
|
|
- clrsetbits_be32(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl);
|
|
|
-
|
|
|
- setbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
|
|
|
+ }
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -407,9 +452,13 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd)
|
|
|
case SNDRV_PCM_TRIGGER_RESUME:
|
|
|
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
|
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
|
|
- setbits32(&ssi->scr, CCSR_SSI_SCR_TE);
|
|
|
+ clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
|
|
|
+ setbits32(&ssi->scr,
|
|
|
+ CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE);
|
|
|
} else {
|
|
|
- setbits32(&ssi->scr, CCSR_SSI_SCR_RE);
|
|
|
+ clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
|
|
|
+ setbits32(&ssi->scr,
|
|
|
+ CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE);
|
|
|
|
|
|
/*
|
|
|
* 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)
|
|
|
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
|
|
ssi_private->capture--;
|
|
|
|
|
|
+ if (ssi_private->first_stream == substream)
|
|
|
+ ssi_private->first_stream = ssi_private->second_stream;
|
|
|
+
|
|
|
+ ssi_private->second_stream = NULL;
|
|
|
+
|
|
|
/*
|
|
|
* If this is the last active substream, disable the SSI and release
|
|
|
* the IRQ.
|