ASoC: improve asynchronous mode support in the fsl_ssi driver
[deliverable/linux.git] / sound / soc / fsl / fsl_ssi.c
index 06ac2b92faf33926a174b111a09daa0eee45ddf7..0268cf989736f303a224fbd26b0db2e8bf97f2ed 100644 (file)
@@ -78,7 +78,6 @@
  * @second_stream: pointer to second stream
  * @playback: the number of playback streams opened
  * @capture: the number of capture streams opened
- * @asynchronous: 0=synchronous mode, 1=asynchronous mode
  * @cpu_dai: the CPU DAI for this device
  * @dev_attr: the sysfs device attribute structure
  * @stats: SSI statistics
@@ -90,9 +89,6 @@ struct fsl_ssi_private {
        unsigned int irq;
        struct snd_pcm_substream *first_stream;
        struct snd_pcm_substream *second_stream;
-       unsigned int playback;
-       unsigned int capture;
-       int asynchronous;
        unsigned int fifo_depth;
        struct snd_soc_dai_driver cpu_dai_drv;
        struct device_attribute dev_attr;
@@ -281,15 +277,19 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
                           struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+       struct fsl_ssi_private *ssi_private =
+               snd_soc_dai_get_drvdata(rtd->cpu_dai);
+       int synchronous = ssi_private->cpu_dai_drv.symmetric_rates;
 
        /*
         * If this is the first stream opened, then request the IRQ
         * and initialize the SSI registers.
         */
-       if (!ssi_private->playback && !ssi_private->capture) {
+       if (!ssi_private->first_stream) {
                struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
 
+               ssi_private->first_stream = substream;
+
                /*
                 * Section 16.5 of the MPC8610 reference manual says that the
                 * 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,
                clrsetbits_be32(&ssi->scr,
                        CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN,
                        CCSR_SSI_SCR_TFR_CLK_DIS | CCSR_SSI_SCR_I2S_MODE_SLAVE
-                       | (ssi_private->asynchronous ? 0 : CCSR_SSI_SCR_SYN));
+                       | (synchronous ? CCSR_SSI_SCR_SYN : 0));
 
                out_be32(&ssi->stcr,
                         CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
@@ -323,7 +323,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
                 * master.
                 */
 
-               /* 4. Enable the interrupts and DMA requests */
+               /* Enable the interrupts and DMA requests */
                out_be32(&ssi->sier, SIER_FLAGS);
 
                /*
@@ -352,58 +352,47 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
                 * this is bad is because at this point, the PCM driver has not
                 * finished initializing the DMA controller.
                 */
-       }
-
-       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->sample_bits) {
-                       dev_err(substream->pcm->card->dev,
-                               "set sample size in %s stream first\n",
-                               substream->stream == SNDRV_PCM_STREAM_PLAYBACK
-                               ? "capture" : "playback");
-                       return -EAGAIN;
-               }
+       } else {
+               if (synchronous) {
+                       struct snd_pcm_runtime *first_runtime =
+                               ssi_private->first_stream->runtime;
+                       /*
+                        * This is the second stream open, and we're in
+                        * synchronous mode, so we need to impose sample
+                        * sample size constraints. This is because STCCR is
+                        * used for playback and capture in synchronous mode,
+                        * so there's no way to specify different word
+                        * lengths.
+                        *
+                        * 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.
+                        */
+                       if (!first_runtime->sample_bits) {
+                               dev_err(substream->pcm->card->dev,
+                                       "set sample size in %s stream first\n",
+                                       substream->stream ==
+                                       SNDRV_PCM_STREAM_PLAYBACK
+                                       ? "capture" : "playback");
+                               return -EAGAIN;
+                       }
 
-               /* If we're in synchronous mode, then we need to constrain
-                * the sample size as well.  We don't support independent sample
-                * rates in asynchronous mode.
-                */
-               if (!ssi_private->asynchronous)
                        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++;
-
-       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
-               ssi_private->capture++;
-
        return 0;
 }
 
@@ -424,24 +413,35 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
        struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *cpu_dai)
 {
        struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
+       struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+       unsigned int sample_size =
+               snd_pcm_format_width(params_format(hw_params));
+       u32 wl = CCSR_SSI_SxCCR_WL(sample_size);
+       int enabled = in_be32(&ssi->scr) & CCSR_SSI_SCR_SSIEN;
 
-       if (substream == ssi_private->first_stream) {
-               struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
-               unsigned int sample_size =
-                       snd_pcm_format_width(params_format(hw_params));
-               u32 wl = CCSR_SSI_SxCCR_WL(sample_size);
+       /*
+        * If we're in synchronous mode, and the SSI is already enabled,
+        * then STCCR is already set properly.
+        */
+       if (enabled && ssi_private->cpu_dai_drv.symmetric_rates)
+               return 0;
 
-               /* The SSI should always be disabled at this points (SSIEN=0) */
+       /*
+        * FIXME: The documentation says that SxCCR[WL] should not be
+        * modified while the SSI is enabled.  The only time this can
+        * happen is if we're trying to do simultaneous playback and
+        * capture in asynchronous mode.  Unfortunately, I have been enable
+        * to get that to work at all on the P1022DS.  Therefore, we don't
+        * bother to disable/enable the SSI when setting SxCCR[WL], because
+        * the SSI will stop anyway.  Maybe one day, this will get fixed.
+        */
 
-               /* In synchronous mode, the SSI uses STCCR for capture */
-               if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ||
-                   !ssi_private->asynchronous)
-                       clrsetbits_be32(&ssi->stccr,
-                                       CCSR_SSI_SxCCR_WL_MASK, wl);
-               else
-                       clrsetbits_be32(&ssi->srccr,
-                                       CCSR_SSI_SxCCR_WL_MASK, wl);
-       }
+       /* In synchronous mode, the SSI uses STCCR for capture */
+       if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ||
+           ssi_private->cpu_dai_drv.symmetric_rates)
+               clrsetbits_be32(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl);
+       else
+               clrsetbits_be32(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl);
 
        return 0;
 }
@@ -464,7 +464,6 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
-               clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
                        setbits32(&ssi->scr,
@@ -500,12 +499,6 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
 
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               ssi_private->playback--;
-
-       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
-               ssi_private->capture--;
-
        if (ssi_private->first_stream == substream)
                ssi_private->first_stream = ssi_private->second_stream;
 
@@ -514,7 +507,7 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
        /*
         * If this is the last active substream, disable the SSI.
         */
-       if (!ssi_private->playback && !ssi_private->capture) {
+       if (!ssi_private->first_stream) {
                struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
 
                clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
@@ -688,9 +681,7 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev)
        }
 
        /* Are the RX and the TX clocks locked? */
-       if (of_find_property(np, "fsl,ssi-asynchronous", NULL))
-               ssi_private->asynchronous = 1;
-       else
+       if (!of_find_property(np, "fsl,ssi-asynchronous", NULL))
                ssi_private->cpu_dai_drv.symmetric_rates = 1;
 
        /* Determine the FIFO depth. */
This page took 0.026381 seconds and 5 git commands to generate.