Hi all,
I am seeing a sound problem leading to no more sound after an XRUN .
I see more or less what is happening and have a work around but am not
entirely sure of the correct solution.
The problem occurs on an i.MX6DL using mainline kernel 5.4 with the
fsl_ssi driver and a SGTL5000 audio codec connected over I2S.
The userspace is Android 8 using tinnyhal + tinyalsa.
This uses ioctl to push samples rather than mmap.
The scenario is:
1) A buffer underrun occurs, causing -EPIPE to be returned to userspace.
2) Userpsapce (the tinyalsa component) does a pcm_prepare() to recover
(which
completes OK).
3) Userspace starts sending data again and then, when the start threshold
is exceeded this kernel log is generated
fsl-ssi-dai 2028000.ssi: Timeout waiting TX FIFO filling
4) From this point on no more sound is played, further writes timeout
after 10s and return -EIO.
My analsysis is as follows:
When the underrun occurs snd_dmaengine_pcm_trigger(SNDRV_PCM_TRIGGER_STOP)
is performed which does
dmaengine_terminate_async()
When the stream is restarted
snd_dmaengine_pcm_trigger(SNDRV_PCM_TRIGGER_START
does
dmaengine_submit()
dma_async_issue_pending()
Because dmaengine_terminate_async() is asynchronus it sometimes completes
after the dmaengine_submit(), causing the new DMA request to be cancelled
before it has started.
This results in the "Timeout waiting TX FIFO filling" message (in
fsl_ssi_config_enable())
because it enables the SSI and expects data to be transfered to the FIFO by
DMA.
The message is only a warning, (void function with no error return)
So without DMA running the buffer quicky fills up, resulting in all future
writes timeouting waiting for buffer space.
Where I'm not sure is if this is an ALSA bug or a bug in the i.MX6 SDMA
controller driver. IE is it OK to do dmaengine_terminate_async() and later
dmaengine_submit() on the same DMA channel without waiting for the
terminate to complete?
My workaround is to wait for the terminate to complete before returning
-EPIPE (just after releasing the lock which prevents sleeping earlier).
Ie this:
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 2236b5e..b03dac3 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -18,6 +18,9 @@
#include <sound/pcm_params.h>
#include <sound/timer.h>
+#include <linux/dmaengine.h>
+#include <sound/dmaengine_pcm.h>
+
#include "pcm_local.h"
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
@@ -2239,6 +2242,10 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct
snd_pcm_substream *substream,
}
}
_end_unlock:
+ /* MF: Workaround for broken sound after XRUN. */
+ if (err == -EPIPE)
+ dmaengine_synchronize(snd_dmaengine_pcm_get_chan(substream));
+
runtime->twake = 0;
if (xfer > 0 && err >= 0)
snd_pcm_update_state(substream, runtime);
Regards,
Martin