From 3abf65738c67f85c2cecec403bb65e2ba5043304 Mon Sep 17 00:00:00 2001
From: Pratyush Yadav <p.yadav@ti.com>
Date: Thu, 18 Feb 2021 17:39:41 +0530
Subject: [PATCH] HACK: media: ti-vpe: csi2rx: Drain DMA when stopping stream

Some data might be stuck in the DMA pipeline because the application
does not tell us how many frames it wants to capture. So there will
always be some time delay between the application requesting the last
frame it needs and stopping the stream which will stop DMA. Drain that
data so it does not corrupt the next frame captured when the stream is
re-started later.

Marking this as a hack for now because it is not clear yet whether this
is a hardware problem or a software problem. If it does turn out to be a
hardware problem, it can be presented as a workaround instead.

Signed-off-by: Pratyush Yadav <p.yadav@ti.com>
Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
---
 drivers/media/platform/ti-vpe/ti-csi2rx.c | 64 +++++++++++++++++++++++
 1 file changed, 64 insertions(+)

diff --git a/drivers/media/platform/ti-vpe/ti-csi2rx.c b/drivers/media/platform/ti-vpe/ti-csi2rx.c
index 967afb073348b..4345d80778823 100644
--- a/drivers/media/platform/ti-vpe/ti-csi2rx.c
+++ b/drivers/media/platform/ti-vpe/ti-csi2rx.c
@@ -40,6 +40,8 @@
 #define CSI_DF_RGB444			0x20
 #define CSI_DF_RGB888			0x24
 
+#define DRAIN_TIMEOUT_MS		50
+
 struct ti_csi2rx_fmt {
 	u32				fourcc;	/* Four character code. */
 	u32				code;	/* Mbus code. */
@@ -73,6 +75,7 @@ struct ti_csi2rx_dev {
 	struct dma_chan			*dma;
 	struct ti_csi2rx_dmaq		dmaq;
 	u32				sequence;
+	struct completion		drain_complete;
 };
 
 static const struct ti_csi2rx_fmt formats[] = {
@@ -693,6 +696,57 @@ static int ti_csi2rx_start_dma(struct ti_csi2rx_dev *csi,
 	return 0;
 }
 
+static void ti_csi2rx_drain_callback(void *param)
+{
+	struct ti_csi2rx_dev *csi = param;
+
+	complete(&csi->drain_complete);
+}
+
+static int ti_csi2rx_drain_dma(struct ti_csi2rx_dev *csi)
+{
+	void *buf;
+	struct dma_async_tx_descriptor *desc;
+	struct device *dev = csi->dma->device->dev;
+	size_t len = csi->v_fmt.fmt.pix.sizeimage;
+	dma_addr_t addr;
+	dma_cookie_t cookie;
+	int ret;
+
+	buf = dma_alloc_coherent(dev, len, &addr, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	desc = dmaengine_prep_slave_single(csi->dma, addr, len, DMA_DEV_TO_MEM,
+					   DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!desc) {
+		ret = -EIO;
+		goto out;
+	}
+
+	desc->callback = ti_csi2rx_drain_callback;
+	desc->callback_param = csi;
+	init_completion(&csi->drain_complete);
+
+	cookie = dmaengine_submit(desc);
+	ret = dma_submit_error(cookie);
+	if (ret)
+		goto out;
+
+	dma_async_issue_pending(csi->dma);
+
+	if (!wait_for_completion_timeout(&csi->drain_complete,
+					 msecs_to_jiffies(DRAIN_TIMEOUT_MS))) {
+		dmaengine_terminate_sync(csi->dma);
+		ret = -ETIMEDOUT;
+		goto out;
+	}
+
+out:
+	dma_free_coherent(dev, len, buf, addr);
+	return ret;
+}
+
 static int ti_csi2rx_queue_setup(struct vb2_queue *q, unsigned int *nbuffers,
 				 unsigned int *nplanes, unsigned int sizes[],
 				 struct device *alloc_devs[])
@@ -790,6 +844,16 @@ static void ti_csi2rx_stop_streaming(struct vb2_queue *vq)
 	if (ret)
 		dev_err(csi->dev, "Failed to stop DMA\n");
 
+	/*
+	 * Some data might be stuck in the DMA pipeline because the application
+	 * does not tell us how many frames it wants to capture. So there will
+	 * always be some time delay between the application requesting the last
+	 * frame it needs and stopping the stream which will stop DMA. Drain
+	 * that data so it does not corrupt the next frame captured when the
+	 * stream is re-started later.
+	 */
+	ti_csi2rx_drain_dma(csi);
+
 	writel(0, csi->shim + SHIM_DMACNTX);
 
 	list_for_each_entry_safe(buf, tmp, &csi->dmaq.list, list) {
-- 
GitLab