From a2054ae60d9d950ac7b88e5918562bec39a99ef3 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 7 Aug 2018 16:12:59 -0500 Subject: [PATCH] dai: fix error handling when DMA is stuck When the DMA is stuck, e.g. in slave mode when the external clock is not active, the Linux kernel will trigger a stop after a timeout. Since the DMA will never complete, the current code returns an IPC error on stop (DMA completion fails) and will not reset DMA channel status and reference counts. Force the DMA and DAI to be stopped to solve the problem. We probably need to look at all error handling since it's likely to be a recurring issue. Also simplify the Xrun code, no need for an extra return. Signed-off-by: Pierre-Louis Bossart --- src/audio/dai.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/audio/dai.c b/src/audio/dai.c index 9c2ed5f57ba9..dab19bbedb3e 100644 --- a/src/audio/dai.c +++ b/src/audio/dai.c @@ -552,6 +552,7 @@ static int dai_comp_trigger(struct comp_dev *dev, int cmd) switch (cmd) { case COMP_TRIGGER_START: + trace_dai("tsa"); if (!dd->pointer_init) dai_pointer_init(dev); /* only start the DAI if we are not XRUN handling */ @@ -595,15 +596,15 @@ static int dai_comp_trigger(struct comp_dev *dev, int cmd) platform_dai_wallclock(dev, &dd->wallclock); break; case COMP_TRIGGER_XRUN: + trace_dai("txr"); dd->xrun = 1; /* stop the DAI unconditionally */ dai_trigger(dd->dai, COMP_TRIGGER_STOP, dev->params.direction); ret = dma_stop(dd->dma, dd->chan); - if (ret < 0) - return ret; break; case COMP_TRIGGER_PAUSE: case COMP_TRIGGER_STOP: + trace_dai("tsp"); wait_init(&dd->complete); /* wait for DMA to complete */ @@ -612,6 +613,10 @@ static int dai_comp_trigger(struct comp_dev *dev, int cmd) if (ret < 0) { trace_dai_error("ed0"); trace_error_value(cmd); + /* forced stop of DMA+DAI to avoid refcount issues */ + dai_trigger(dd->dai, COMP_TRIGGER_STOP, + dev->params.direction); + ret = dma_stop(dd->dma, dd->chan); } break; default: