[PATCH 4.14 200/209] media: stm32-dcmi: fix DMA corruption when stopping streaming

From: Greg Kroah-Hartman
Date: Wed Dec 04 2019 - 13:09:18 EST


From: Hugues Fruchet <hugues.fruchet@xxxxxx>

commit b3ce6f6ff3c260ee53b0f2236e5fd950d46957da upstream.

Avoid call of dmaengine_terminate_all() between
dmaengine_prep_slave_single() and dmaengine_submit() by locking
the whole DMA submission sequence.

Signed-off-by: Hugues Fruchet <hugues.fruchet@xxxxxx>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xxxxxxxxx>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@xxxxxxxxxx>
Signed-off-by: Mathieu Poirier <mathieu.poirier@xxxxxxxxxx>
Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>

---
drivers/media/platform/stm32/stm32-dcmi.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)

--- a/drivers/media/platform/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/stm32/stm32-dcmi.c
@@ -161,6 +161,9 @@ struct stm32_dcmi {
u32 misr;
int errors_count;
int buffers_count;
+
+ /* Ensure DMA operations atomicity */
+ struct mutex dma_lock;
};

static inline struct stm32_dcmi *notifier_to_dcmi(struct v4l2_async_notifier *n)
@@ -291,6 +294,13 @@ static int dcmi_start_dma(struct stm32_d
return ret;
}

+ /*
+ * Avoid call of dmaengine_terminate_all() between
+ * dmaengine_prep_slave_single() and dmaengine_submit()
+ * by locking the whole DMA submission sequence
+ */
+ mutex_lock(&dcmi->dma_lock);
+
/* Prepare a DMA transaction */
desc = dmaengine_prep_slave_single(dcmi->dma_chan, buf->paddr,
buf->size,
@@ -298,6 +308,7 @@ static int dcmi_start_dma(struct stm32_d
if (!desc) {
dev_err(dcmi->dev, "%s: DMA dmaengine_prep_slave_single failed for buffer size %zu\n",
__func__, buf->size);
+ mutex_unlock(&dcmi->dma_lock);
return -EINVAL;
}

@@ -309,9 +320,12 @@ static int dcmi_start_dma(struct stm32_d
dcmi->dma_cookie = dmaengine_submit(desc);
if (dma_submit_error(dcmi->dma_cookie)) {
dev_err(dcmi->dev, "%s: DMA submission failed\n", __func__);
+ mutex_unlock(&dcmi->dma_lock);
return -ENXIO;
}

+ mutex_unlock(&dcmi->dma_lock);
+
dma_async_issue_pending(dcmi->dma_chan);

return 0;
@@ -690,7 +704,9 @@ static void dcmi_stop_streaming(struct v
spin_unlock_irq(&dcmi->irqlock);

/* Stop all pending DMA operations */
+ mutex_lock(&dcmi->dma_lock);
dmaengine_terminate_all(dcmi->dma_chan);
+ mutex_unlock(&dcmi->dma_lock);

clk_disable(dcmi->mclk);

@@ -1662,6 +1678,7 @@ static int dcmi_probe(struct platform_de

spin_lock_init(&dcmi->irqlock);
mutex_init(&dcmi->lock);
+ mutex_init(&dcmi->dma_lock);
init_completion(&dcmi->complete);
INIT_LIST_HEAD(&dcmi->buffers);