[alsa-devel] fsl_ssi.c: Getting channel slips with fsl_ssi.c in TDM (network) mode.

Caleb Crome caleb at crome.org
Thu Oct 29 20:06:16 CET 2015


STARTUP ISSUE SOLVED (INELEGANTLY)

:-)

On Thu, Oct 29, 2015 at 10:19 AM, Nicolin Chen <nicoleotsuka at gmail.com> wrote:
> On Thu, Oct 29, 2015 at 06:44:12AM -0700, Caleb Crome wrote:
>> > Reported by user space? Are you saying that's an ALSA underrun in
>> > the user space, not a hardware underrun reported by the IRQ in the
>> > driver? They are quite different. ALSA underrun comes from the DMA
>> > buffer gets underrun while the other one results from FIFO feeding
>> > efficiency. For ALSA underrun, enlarging the playback period size
>> > and period number will ease the problem:
>
>> Sometimes they happen at the same time.  So, I run aplay, and all is
>
> The 'they' is indicating ALSA underrun + hardware underrun or ALSA
> underrun + channel slip?

Exactly, they tend to come together, but either can come without the other.

> It's not quite logical for a channel slip
> resulting from an ALSA underrun as it should restart by calling the
> trigger() functions in DAI drivers IIRC.

This actually is exactly what I'm seeing now.  I'm seeing the
*startup* happening from the trigger starting up slipped.  So this
does make perfect sense to me.

I am playing a very short ramp.wav file and seeing how often it starts
up 'slipped' with the extra 0.  It started up incorrectly 20 out of
300 trials.  So, the startup is failing 7% of the time.

It occurred to me that perhaps the problem has to do when exactly when
during the frame-sync period the fsl_ssi_trigger function was called.
Perhaps, if it's called near the end or beginning of a frame, somehow
something gets messed up.  (The docs for the SCR register imply some
of this, but it talks about either 2 or 6 bit clocks, so I'd expect
the error rate to be lower than 7% (more like 2.5%).

So, I implemented a really inelegant patch to synchronize the trigger
with the frame sync signal, and I got ZERO errors out of 500 trials!
This seems to have nailed the startup problem!

In addition, I have run about 20 minutes of audio with no slips or
problems, even though there have been aplay underruns.  This is a
major step forward for me :-)

The idea is to enable the SSI before enabling DMA.  Then wait for a
frame sync by polling.  Once I get the frame sync disable the SSI, and
let the trigger function continue.

How should this be done properly?

diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 73778c2..8cd8284 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -328,6 +328,41 @@ static void fsl_ssi_rxtx_config(struct
fsl_ssi_private *ssi_private,
     }
 }

+/*
+ * wait for a frame sync.  do this by enabling the SSI,
+ * then waiting for sync to happen, then disabling the SSI
+ * and put it back to the state it was at first
+ */
+static void wait_for_tfs(struct regmap *regs)
+{
+    u32 tfs;
+    u32 scr;
+    int maxcount = 100000;
+    regmap_read(regs, CCSR_SSI_SCR, &scr);
+    regmap_update_bits(regs, CCSR_SSI_SCR, 0x3, 0x3);
+    while(maxcount--) {
+        /* clear TFS bit */
+        regmap_update_bits(regs, CCSR_SSI_SISR, CCSR_SSI_SISR_TFS, 0);
+        regmap_read(regs, CCSR_SSI_SISR, &tfs);
+        if ((tfs & CCSR_SSI_SISR_TFS)==0)
+            break; /* tfs went to 0 */
+    }
+    if (maxcount < 0) {
+        printk(KERN_INFO "timed out 1, sisr = 0x%08x\n", tfs);
+    }
+    maxcount = 100000;
+    while(maxcount--) {
+        /* waiting for tfs to go to 1. */
+        regmap_read(regs, CCSR_SSI_SISR, &tfs);
+        if ((tfs & CCSR_SSI_SISR_TFS))
+            break; /* tfs went to 1 */
+    }
+    if (maxcount < 0) {
+        printk(KERN_INFO "timed out 2\n");
+    }
+    regmap_write(regs, CCSR_SSI_SCR, scr);
+}
+
 /*
  * Calculate the bits that have to be disabled for the current stream that is
  * getting disabled. This keeps the bits enabled that are necessary for the
@@ -360,7 +395,10 @@ static void fsl_ssi_config(struct fsl_ssi_private
*ssi_private, bool enable,
     int nr_active_streams;
     u32 scr_val;
     int keep_active;
-
+    wait_for_tfs(regs); /* synchronize with the start of a frame
+                 * to get done with this function well
+                 * before the end of a frame
+                 */
     regmap_read(regs, CCSR_SSI_SCR, &scr_val);

     nr_active_streams = !!(scr_val & CCSR_SSI_SCR_TE) +
@@ -943,9 +980,9 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev,
      * size.
      */
     if (ssi_private->use_dma)
-        wm = ssi_private->fifo_depth - 2;
+        wm = 8;
     else
-        wm = ssi_private->fifo_depth;
+        wm = 8;

     regmap_write(regs, CCSR_SSI_SFCSR,
             CCSR_SSI_SFCSR_TFWM0(wm) | CCSR_SSI_SFCSR_RFWM0(wm) |
@@ -1260,8 +1297,8 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev,
      * We have burstsize be "fifo_depth - 2" to match the SSI
      * watermark setting in fsl_ssi_startup().
      */
-    ssi_private->dma_params_tx.maxburst = ssi_private->fifo_depth - 2;
-    ssi_private->dma_params_rx.maxburst = ssi_private->fifo_depth - 2;
+    ssi_private->dma_params_tx.maxburst = 8;
+    ssi_private->dma_params_rx.maxburst = 8;
     ssi_private->dma_params_tx.addr = ssi_private->ssi_phys + CCSR_SSI_STX0;
     ssi_private->dma_params_rx.addr = ssi_private->ssi_phys + CCSR_SSI_SRX0;

>
>> fine.  Then the user space app will underrun, and then I look at the
>> scope, and the channels have slipped.  So somehow the start/restart
>> after the underrun is not always perfect I guess.
>
>> Is there any mechanism for the DMA fifo underruns to be reported back
>> to user space?  There certainly should be, because the consequences
>
> No. The release from Freescale official tree has a reset procedure
> applied to ESAI underrun but not SSI but I guess you may want to
> refer to that.

Ooh, that can be a problem.   Maybe I'll take a look.  But for the
moment, it appears that, so far, for now, the system is working.

-Caleb


More information about the Alsa-devel mailing list