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