From 440397a668797cbe94736b19e92c64e51dfbb4d6 Mon Sep 17 00:00:00 2001
From: "Qiang.li" <qiang.li@nxp.com>
Date: Thu, 12 Aug 2021 11:23:44 +0800
Subject: [PATCH 1/2] iMX8QM, iMX8QX, ERR050145, ISI overwrite workaround.
 Assign ISI and the memory buffers into a sub partition, in this case, ISI can
 only write these memory buffers, and no permission to write other memory.

---
 drivers/media/platform/imx8/Kconfig        |   5 +
 drivers/media/platform/imx8/mxc-isi-cap.c  | 176 ++++++++++++++++++++++++++++-
 drivers/media/platform/imx8/mxc-isi-core.h |   3 +
 3 files changed, 183 insertions(+), 1 deletion(-)

diff --git a/drivers/media/platform/imx8/Kconfig b/drivers/media/platform/imx8/Kconfig
index bf3a0a8..627be25 100644
--- a/drivers/media/platform/imx8/Kconfig
+++ b/drivers/media/platform/imx8/Kconfig
@@ -1,6 +1,11 @@
 if VIDEO_MX8_CAPTURE
 menu "IMX8 Camera ISI/MIPI Features support"
 
+config IMX8_ISI_OVERRUN_FIX
+	depends on VIDEO_MX8_CAPTURE
+	tristate "IMX8 ISI Overrun Issue Workaround"
+	default y
+
 config IMX8_MEDIA_DEVICE
 	tristate "IMX8 Media Device Driver"
 	select V4L2_FWNODE
diff --git a/drivers/media/platform/imx8/mxc-isi-cap.c b/drivers/media/platform/imx8/mxc-isi-cap.c
index 16d43b6..b979c37 100644
--- a/drivers/media/platform/imx8/mxc-isi-cap.c
+++ b/drivers/media/platform/imx8/mxc-isi-cap.c
@@ -385,6 +385,169 @@ static void cap_vb2_buffer_queue(struct vb2_buffer *vb2)
 	spin_unlock_irqrestore(&mxc_isi->slock, flags);
 }
 
+#ifdef CONFIG_IMX8_ISI_OVERRUN_FIX
+static int mxc_isi_create_parition(struct mxc_isi_dev *mxc_isi)
+{
+	struct mxc_isi_buffer *buf;
+	struct vb2_buffer *vb2;
+	sc_rm_pt_t pt;
+	sc_ipc_t ipc_handle;
+	uint32_t mu_id;
+	sc_err_t sciErr;
+	sc_rsrc_t resource;
+	sc_rm_mr_t mr;
+	sc_faddr_t addr_start, addr_end;
+	int i;
+
+	if (mxc_isi->isi_partition != 0) {
+		dev_err(&mxc_isi->pdev->dev, "%s: isi_partition hasn't been released.\n", __func__);
+		return SC_ERR_UNAVAILABLE;
+	}
+
+	sciErr = sc_ipc_getMuID(&mu_id);
+	if (sciErr != SC_ERR_NONE) {
+		dev_err(&mxc_isi->pdev->dev, "%s: can't get mu id: %d\n", __func__, sciErr);
+		goto EXIT_1;
+	}
+
+	sciErr = sc_ipc_open(&ipc_handle, mu_id);
+	if (sciErr != SC_ERR_NONE) {
+		dev_err(&mxc_isi->pdev->dev, "%s: can't get ipc handler: %d\n", __func__, sciErr);
+		goto EXIT_1;
+	}
+
+	/* Get own partition */
+	sciErr = sc_rm_get_partition(ipc_handle, &pt);
+	if (sciErr != SC_ERR_NONE) {
+		dev_err(&mxc_isi->pdev->dev, "%s: can't get own partition: %d\n", __func__, sciErr);
+		goto EXIT_2;
+	}
+
+	/* Allocate new partition */
+	sciErr = sc_rm_partition_alloc(ipc_handle, &mxc_isi->isi_partition, false, true, true, false, false);
+	if (sciErr != SC_ERR_NONE) {
+		dev_err(&mxc_isi->pdev->dev, "%s: can't alloc partition: %d\n", __func__, sciErr);
+		goto EXIT_2;
+	}
+
+	/* Assign ISI resource */
+	resource = SC_R_ISI_CH0 + mxc_isi->id;
+	sciErr = sc_rm_assign_resource(ipc_handle, mxc_isi->isi_partition, resource);
+	if (sciErr != SC_ERR_NONE) {
+		dev_err(&mxc_isi->pdev->dev, "%s: can't assign resource: %d\n", __func__, sciErr);
+		goto EXIT_3;
+	}
+
+	/* Set ISI resource permission */
+	sciErr = sc_rm_set_peripheral_permissions(ipc_handle, resource, pt, SC_RM_PERM_FULL);
+	if (sciErr != SC_ERR_NONE) {
+		dev_err(&mxc_isi->pdev->dev, "%s: can't set resource permission: %d\n", __func__, sciErr);
+		goto EXIT_3;
+	}
+
+	/* Alloc and assign memory regions for discard buffers */
+/*
+	for (i = 0; i < mxc_isi->pix.num_planes; i++) {
+		addr_start = mxc_isi->discard_buffer_dma[i] & ~0xFFF;
+		addr_end = (mxc_isi->discard_buffer_dma[i] + mxc_isi->discard_size[i] - 1) | 0xFFF;
+
+		mr = 0;
+		sciErr = sc_rm_memreg_alloc(ipc_handle, &mr, addr_start, addr_end);
+		if (sciErr != SC_ERR_NONE) {
+			dev_err(&mxc_isi->pdev->dev, "%s: can't alloc memory region for discard buffer[%d]: %d\n", __func__, i, sciErr);
+			if(mr != 0)
+				sc_rm_memreg_free(ipc_handle, mr);
+			goto EXIT_3;
+		}
+
+		sciErr = sc_rm_assign_memreg(ipc_handle, mxc_isi->isi_partition, mr);
+		if (sciErr != SC_ERR_NONE) {
+			dev_err(&mxc_isi->pdev->dev, "%s: can't assign memory region for discard buffer[%d]: %d\n", __func__, i, sciErr);
+			goto EXIT_3;
+		}
+
+		sciErr = sc_rm_set_memreg_permissions(ipc_handle, mr, pt, SC_RM_PERM_FULL);
+		if (sciErr != SC_ERR_NONE) {
+			dev_err(&mxc_isi->pdev->dev, "%s: can't set memory region permission for discard buffer[%d]: %d\n", __func__, i, sciErr);
+			goto EXIT_3;
+		}
+	}
+*/  //Discard buffer is not needed for memory region.
+
+	/* Alloc and assign memory regions for out_pending buffers */
+	list_for_each_entry(buf, &mxc_isi->isi_cap.out_pending, list) {
+		vb2 = &buf->v4l2_buf.vb2_buf;
+
+		for (i = 0; i < vb2->num_planes; i++) {
+			addr_start = vb2_dma_contig_plane_dma_addr(vb2, i) & ~0xFFF;
+			addr_end = (vb2_dma_contig_plane_dma_addr(vb2, i) + vb2->planes[i].length - 1) | 0xFFF;
+
+			mr = 0;
+			sciErr = sc_rm_memreg_alloc(ipc_handle, &mr, addr_start, addr_end);
+			if (sciErr != SC_ERR_NONE) {
+				dev_err(&mxc_isi->pdev->dev, "%s: can't alloc memory region for out_pending buffer[%d]: %d\n", __func__, i, sciErr);
+				if(mr != 0)
+					sc_rm_memreg_free(ipc_handle, mr);
+				goto EXIT_3;
+			}
+
+			sciErr = sc_rm_assign_memreg(ipc_handle, mxc_isi->isi_partition, mr);
+			if (sciErr != SC_ERR_NONE) {
+				dev_err(&mxc_isi->pdev->dev, "%s: can't assign memory region for out_pending buffer[%d]: %d\n", __func__, i, sciErr);
+				goto EXIT_3;
+			}
+
+			sciErr = sc_rm_set_memreg_permissions(ipc_handle, mr, pt, SC_RM_PERM_FULL);
+			if (sciErr != SC_ERR_NONE) {
+				dev_err(&mxc_isi->pdev->dev, "%s: can't set memory region permission for out_pending buffer[%d]: %d\n", __func__, i, sciErr);
+				goto EXIT_3;
+			}
+		}
+	}
+
+	sc_ipc_close(ipc_handle);
+
+	return SC_ERR_NONE;
+
+EXIT_3:
+	sc_rm_partition_free(ipc_handle, mxc_isi->isi_partition);
+	mxc_isi->isi_partition = 0;
+
+EXIT_2:
+	sc_ipc_close(ipc_handle);
+
+EXIT_1:
+	return sciErr;
+}
+
+static void mxc_isi_free_parition(struct mxc_isi_dev *mxc_isi)
+{
+	sc_ipc_t ipc_handle;
+	uint32_t mu_id;
+	sc_err_t sciErr;
+
+	if (mxc_isi->isi_partition == 0)
+		return;
+
+	sciErr = sc_ipc_getMuID(&mu_id);
+	if (sciErr != SC_ERR_NONE) {
+		dev_err(&mxc_isi->pdev->dev, "%s: can't get mu id: %d\n", __func__, sciErr);
+		return;
+	}
+
+	sciErr = sc_ipc_open(&ipc_handle, mu_id);
+	if (sciErr != SC_ERR_NONE) {
+		dev_err(&mxc_isi->pdev->dev, "%s: can't get ipc handler: %d\n", __func__, sciErr);
+		return;
+	}
+
+	sc_rm_partition_free(ipc_handle, mxc_isi->isi_partition);
+	mxc_isi->isi_partition = 0;
+
+	sc_ipc_close(ipc_handle);
+}
+#endif
+
 static int cap_vb2_start_streaming(struct vb2_queue *q, unsigned int count)
 {
 	struct mxc_isi_dev *mxc_isi = q->drv_priv;
@@ -422,6 +585,10 @@ static int cap_vb2_start_streaming(struct vb2_queue *q, unsigned int count)
 				mxc_isi->discard_buffer[i]);
 	}
 
+#ifdef CONFIG_IMX8_ISI_OVERRUN_FIX
+	mxc_isi_create_parition(mxc_isi);
+#endif
+
 	spin_lock_irqsave(&mxc_isi->slock, flags);
 
 	/* add two list member to out_discard list head */
@@ -431,7 +598,6 @@ static int cap_vb2_start_streaming(struct vb2_queue *q, unsigned int count)
 	mxc_isi->buf_discard[1].discard = true;
 	list_add_tail(&mxc_isi->buf_discard[1].list, &mxc_isi->isi_cap.out_discard);
 
-
 	/* ISI channel output buffer 1 */
 	buf = list_first_entry(&mxc_isi->isi_cap.out_discard,
 					struct mxc_isi_buffer, list);
@@ -468,6 +634,10 @@ static void cap_vb2_stop_streaming(struct vb2_queue *q)
 
 	mxc_isi_channel_disable(mxc_isi);
 
+#ifdef CONFIG_IMX8_ISI_OVERRUN_FIX
+	mxc_isi_free_parition(mxc_isi);
+#endif
+
 	spin_lock_irqsave(&mxc_isi->slock, flags);
 
 	while (!list_empty(&mxc_isi->isi_cap.out_active)) {
@@ -660,6 +830,10 @@ static int mxc_isi_capture_open(struct file *file)
 	atomic_inc(&mxc_isi->open_count);
 	mxc_isi->is_m2m = 0;
 
+#ifdef CONFIG_IMX8_ISI_OVERRUN_FIX
+	mxc_isi->isi_partition = 0;
+#endif
+
 	/* Get remote source pad */
 	source_pad = mxc_isi_get_remote_source_pad(mxc_isi);
 	if (source_pad == NULL) {
diff --git a/drivers/media/platform/imx8/mxc-isi-core.h b/drivers/media/platform/imx8/mxc-isi-core.h
index c26060c..ec952d9 100644
--- a/drivers/media/platform/imx8/mxc-isi-core.h
+++ b/drivers/media/platform/imx8/mxc-isi-core.h
@@ -292,6 +292,9 @@ struct mxc_isi_dev {
 	u32 flags;
 	u32 skip_m2m;
 	u8 chain_buf;
+#ifdef CONFIG_IMX8_ISI_OVERRUN_FIX
+	u8 isi_partition;
+#endif
 
 	atomic_t open_count;
 
-- 
1.9.1

