XRUN recovery was introduced as deferred reset and later converted to a
work item. The reset reads ESAI registers and can restart the transmit
and receive paths. fsl_esai_remove() currently suspends the device before
cancelling the work, so a running reset can overlap regcache and clock
shutdown. The managed IRQ can also queue another reset after cancellation.
Disable and drain the work before runtime PM teardown. This lets an
already running reset finish while the device is active and rejects later
queue attempts until the managed IRQ is released. The IRQ handler's own
register accesses during teardown are a separate issue.
This issue was identified as part of our ongoing research while
reviewing kernel code.
Fixes: 7ccafa2b3879 ("ASoC: fsl_esai: recover the channel swap after xrun")
Assisted-by: LLM
Co-developed-by: Ijae Kim <[email protected]>
Signed-off-by: Ijae Kim <[email protected]>
Signed-off-by: Myeonghun Pak <[email protected]>
---
Compile-tested with GCC, W=1 (arm64). No runtime testing.
Backports before the tasklet-to-work conversion need adaptation; check
disable_work_sync() availability.
sound/soc/fsl/fsl_esai.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c
index 4a530a6..83a9963 100644
--- a/sound/soc/fsl/fsl_esai.c
+++ b/sound/soc/fsl/fsl_esai.c
@@ -10,6 +10,7 @@
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/pm_runtime.h>
+#include <linux/workqueue.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
@@ -1099,11 +1100,11 @@ static void fsl_esai_remove(struct platform_device
*pdev)
{
struct fsl_esai *esai_priv = platform_get_drvdata(pdev);
+ disable_work_sync(&esai_priv->work);
+
pm_runtime_disable(&pdev->dev);
if (!pm_runtime_status_suspended(&pdev->dev))
fsl_esai_runtime_suspend(&pdev->dev);
-
- cancel_work_sync(&esai_priv->work);
}
static const struct of_device_id fsl_esai_dt_ids[] = {