From 2f7c68cb55ecb7331f2381deb497c27155f32faf Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Wed, 03 Jan 2024 09:43:39 +0000
Subject: [PATCH] update kernel to 5.10.198

---
 kernel/drivers/video/rockchip/mpp/mpp_vepu2.c |  442 +++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 415 insertions(+), 27 deletions(-)

diff --git a/kernel/drivers/video/rockchip/mpp/mpp_vepu2.c b/kernel/drivers/video/rockchip/mpp/mpp_vepu2.c
index 75188f2..fb5f5e5 100644
--- a/kernel/drivers/video/rockchip/mpp/mpp_vepu2.c
+++ b/kernel/drivers/video/rockchip/mpp/mpp_vepu2.c
@@ -22,6 +22,7 @@
 #include <linux/proc_fs.h>
 #include <linux/nospec.h>
 #include <soc/rockchip/pm_domains.h>
+#include <soc/rockchip/rockchip_iommu.h>
 
 #include "mpp_debug.h"
 #include "mpp_common.h"
@@ -36,6 +37,8 @@
 #define VEPU2_REG_HW_ID_INDEX		-1 /* INVALID */
 #define VEPU2_REG_START_INDEX			0
 #define VEPU2_REG_END_INDEX			183
+#define VEPU2_REG_OUT_INDEX			(77)
+#define VEPU2_REG_STRM_INDEX			(53)
 
 #define VEPU2_REG_ENC_EN			0x19c
 #define VEPU2_REG_ENC_EN_INDEX			(103)
@@ -97,6 +100,8 @@
 	u32 width;
 	u32 height;
 	u32 pixels;
+	struct mpp_dma_buffer *bs_buf;
+	u32 offset_bs;
 };
 
 struct vepu_session_priv {
@@ -121,6 +126,18 @@
 #endif
 	struct reset_control *rst_a;
 	struct reset_control *rst_h;
+	/* for ccu(central control unit) */
+	struct vepu_ccu *ccu;
+	bool disable_work;
+};
+
+struct vepu_ccu {
+	u32 core_num;
+	/* lock for core attach */
+	spinlock_t lock;
+	struct mpp_dev *main_core;
+	struct mpp_dev *cores[MPP_MAX_CORE_NUM];
+	unsigned long core_idle;
 };
 
 static struct mpp_hw_info vepu_v2_hw_info = {
@@ -167,7 +184,13 @@
 			       struct mpp_task_msgs *msgs)
 {
 	int ret;
+	int fd_bs;
 	int fmt = VEPU2_GET_FORMAT(task->reg[VEPU2_REG_ENC_EN_INDEX]);
+
+	if (session->msg_flags & MPP_FLAGS_REG_NO_OFFSET)
+		fd_bs = task->reg[VEPU2_REG_OUT_INDEX];
+	else
+		fd_bs = task->reg[VEPU2_REG_OUT_INDEX] & 0x3ff;
 
 	ret = mpp_translate_reg_address(session, &task->mpp_task,
 					fmt, task->reg, &task->off_inf);
@@ -176,6 +199,15 @@
 
 	mpp_translate_reg_offset_info(&task->mpp_task,
 				      &task->off_inf, task->reg);
+
+	if (fmt == VEPU2_FMT_JPEGE) {
+		struct mpp_dma_buffer *bs_buf = mpp_dma_find_buffer_fd(session->dma, fd_bs);
+
+		task->offset_bs = mpp_query_reg_offset_info(&task->off_inf, VEPU2_REG_OUT_INDEX);
+		if (bs_buf && task->offset_bs > 0)
+			mpp_dma_buf_sync(bs_buf, 0, task->offset_bs, DMA_TO_DEVICE, false);
+		task->bs_buf = bs_buf;
+	}
 
 	return 0;
 }
@@ -281,12 +313,54 @@
 	return NULL;
 }
 
+static void *vepu_prepare(struct mpp_dev *mpp, struct mpp_task *mpp_task)
+{
+	struct vepu_dev *enc = to_vepu_dev(mpp);
+	struct vepu_ccu *ccu = enc->ccu;
+	unsigned long core_idle;
+	unsigned long flags;
+	s32 core_id;
+	u32 i;
+
+	spin_lock_irqsave(&ccu->lock, flags);
+
+	core_idle = ccu->core_idle;
+
+	for (i = 0; i < ccu->core_num; i++) {
+		struct mpp_dev *mpp = ccu->cores[i];
+
+		if (mpp && mpp->disable)
+			clear_bit(mpp->core_id, &core_idle);
+	}
+
+	core_id = find_first_bit(&core_idle, ccu->core_num);
+	if (core_id >= ARRAY_SIZE(ccu->cores)) {
+		mpp_task = NULL;
+		mpp_dbg_core("core %d all busy %lx\n", core_id, ccu->core_idle);
+		goto done;
+	}
+
+	core_id = array_index_nospec(core_id, MPP_MAX_CORE_NUM);
+	clear_bit(core_id, &ccu->core_idle);
+	mpp_task->mpp = ccu->cores[core_id];
+	mpp_task->core_id = core_id;
+
+	mpp_dbg_core("core cnt %d core %d set idle %lx -> %lx\n",
+		     ccu->core_num, core_id, core_idle, ccu->core_idle);
+
+done:
+	spin_unlock_irqrestore(&ccu->lock, flags);
+
+	return mpp_task;
+}
+
 static int vepu_run(struct mpp_dev *mpp,
 		    struct mpp_task *mpp_task)
 {
 	u32 i;
 	u32 reg_en;
 	struct vepu_task *task = to_vepu_task(mpp_task);
+	u32 timing_en = mpp->srv->timing_en;
 
 	mpp_debug_enter();
 
@@ -305,16 +379,32 @@
 
 		mpp_write_req(mpp, task->reg, s, e, reg_en);
 	}
+
+	/* flush tlb before starting hardware */
+	mpp_iommu_flush_tlb(mpp->iommu_info);
+
 	/* init current task */
 	mpp->cur_task = mpp_task;
+
+	mpp_task_run_begin(mpp_task, timing_en, MPP_WORK_TIMEOUT_DELAY);
+
 	/* Last, flush the registers */
 	wmb();
 	mpp_write(mpp, VEPU2_REG_ENC_EN,
 		  task->reg[reg_en] | VEPU2_ENC_START);
 
+	mpp_task_run_end(mpp_task, timing_en);
+
 	mpp_debug_leave();
 
 	return 0;
+}
+
+static int vepu_px30_run(struct mpp_dev *mpp,
+		    struct mpp_task *mpp_task)
+{
+	mpp_iommu_flush_tlb(mpp->iommu_info);
+	return vepu_run(mpp, mpp_task);
 }
 
 static int vepu_irq(struct mpp_dev *mpp)
@@ -333,6 +423,9 @@
 	u32 err_mask;
 	struct vepu_task *task = NULL;
 	struct mpp_task *mpp_task = mpp->cur_task;
+	unsigned long core_idle;
+	struct vepu_dev *enc = to_vepu_dev(mpp);
+	struct vepu_ccu *ccu = enc->ccu;
 
 	/* FIXME use a spin lock here */
 	if (!mpp_task) {
@@ -354,6 +447,14 @@
 		atomic_inc(&mpp->reset_request);
 
 	mpp_task_finish(mpp_task->session, mpp_task);
+	/* the whole vepu has no ccu that manage multi core */
+	if (ccu) {
+		core_idle = ccu->core_idle;
+		set_bit(mpp->core_id, &ccu->core_idle);
+
+		mpp_dbg_core("core %d isr idle %lx -> %lx\n", mpp->core_id, core_idle,
+			ccu->core_idle);
+	}
 
 	mpp_debug_leave();
 
@@ -380,6 +481,11 @@
 	/* revert hack for irq status */
 	task->reg[VEPU2_REG_INT_INDEX] = task->irq_status;
 
+	if (task->bs_buf)
+		mpp_dma_buf_sync(task->bs_buf, 0,
+				 task->reg[VEPU2_REG_STRM_INDEX] / 8 +
+				 task->offset_bs,
+				 DMA_FROM_DEVICE, true);
 	mpp_debug_leave();
 
 	return 0;
@@ -522,7 +628,7 @@
 	}
 	seq_puts(seq, "\n");
 	/* item data*/
-	seq_printf(seq, "|%8p|", session);
+	seq_printf(seq, "|%8d|", session->index);
 	seq_printf(seq, "%8s|", mpp_device_name[session->device_type]);
 	for (i = ENC_INFO_BASE; i < ENC_INFO_BUTT; i++) {
 		u32 flag = priv->codec_info[i].flag;
@@ -555,8 +661,9 @@
 	mutex_lock(&mpp->srv->session_lock);
 	list_for_each_entry_safe(session, n,
 				 &mpp->srv->session_list,
-				 session_link) {
-		if (session->device_type != MPP_DEVICE_VEPU2)
+				 service_link) {
+		if (session->device_type != MPP_DEVICE_VEPU2 &&
+		    session->device_type != MPP_DEVICE_VEPU2_JPEG)
 			continue;
 		if (!session->priv)
 			continue;
@@ -571,13 +678,28 @@
 static int vepu_procfs_init(struct mpp_dev *mpp)
 {
 	struct vepu_dev *enc = to_vepu_dev(mpp);
+	char name[32];
 
-	enc->procfs = proc_mkdir(mpp->dev->of_node->name, mpp->srv->procfs);
+	if (!mpp->dev || !mpp->dev->of_node || !mpp->dev->of_node->name ||
+	    !mpp->srv || !mpp->srv->procfs)
+		return -EINVAL;
+	if (enc->ccu)
+		snprintf(name, sizeof(name) - 1, "%s%d",
+			mpp->dev->of_node->name, mpp->core_id);
+	else
+		snprintf(name, sizeof(name) - 1, "%s",
+			mpp->dev->of_node->name);
+
+	enc->procfs = proc_mkdir(name, mpp->srv->procfs);
 	if (IS_ERR_OR_NULL(enc->procfs)) {
 		mpp_err("failed on open procfs\n");
 		enc->procfs = NULL;
 		return -EIO;
 	}
+
+	/* for common mpp_dev options */
+	mpp_procfs_create_common(enc->procfs, mpp);
+
 	mpp_procfs_create_u32("aclk", 0644,
 			      enc->procfs, &enc->aclk_info.debug_rate_hz);
 	mpp_procfs_create_u32("session_buffers", 0644,
@@ -588,6 +710,17 @@
 
 	return 0;
 }
+
+static int vepu_procfs_ccu_init(struct mpp_dev *mpp)
+{
+	struct vepu_dev *enc = to_vepu_dev(mpp);
+
+	if (!enc->procfs)
+		goto done;
+
+done:
+	return 0;
+}
 #else
 static inline int vepu_procfs_remove(struct mpp_dev *mpp)
 {
@@ -595,6 +728,11 @@
 }
 
 static inline int vepu_procfs_init(struct mpp_dev *mpp)
+{
+	return 0;
+}
+
+static inline int vepu_procfs_ccu_init(struct mpp_dev *mpp)
 {
 	return 0;
 }
@@ -721,18 +859,68 @@
 static int vepu_reset(struct mpp_dev *mpp)
 {
 	struct vepu_dev *enc = to_vepu_dev(mpp);
+	struct vepu_ccu *ccu = enc->ccu;
 
+	mpp_write(mpp, VEPU2_REG_ENC_EN, 0);
+	udelay(5);
 	if (enc->rst_a && enc->rst_h) {
 		/* Don't skip this or iommu won't work after reset */
-		rockchip_pmu_idle_request(mpp->dev, true);
+		mpp_pmu_idle_request(mpp, true);
 		mpp_safe_reset(enc->rst_a);
 		mpp_safe_reset(enc->rst_h);
 		udelay(5);
 		mpp_safe_unreset(enc->rst_a);
 		mpp_safe_unreset(enc->rst_h);
-		rockchip_pmu_idle_request(mpp->dev, false);
+		mpp_pmu_idle_request(mpp, false);
 	}
 	mpp_write(mpp, VEPU2_REG_INT, VEPU2_INT_CLEAR);
+
+	if (ccu) {
+		set_bit(mpp->core_id, &ccu->core_idle);
+		mpp_dbg_core("core %d reset idle %lx\n", mpp->core_id, ccu->core_idle);
+	}
+
+	return 0;
+}
+
+static int vepu2_iommu_fault_handle(struct iommu_domain *iommu, struct device *iommu_dev,
+				    unsigned long iova, int status, void *arg)
+{
+	struct mpp_dev *mpp = (struct mpp_dev *)arg;
+	struct mpp_task *mpp_task;
+	struct vepu_dev *enc = to_vepu_dev(mpp);
+	struct vepu_ccu *ccu = enc->ccu;
+
+	dev_err(iommu_dev, "fault addr 0x%08lx status %x arg %p\n",
+		iova, status, arg);
+
+	if (ccu) {
+		int i;
+		struct mpp_dev *core;
+
+		for (i = 0; i < ccu->core_num; i++) {
+			core = ccu->cores[i];
+			if (core->iommu_info && (&core->iommu_info->pdev->dev == iommu_dev)) {
+				mpp = core;
+				break;
+			}
+		}
+	}
+
+	if (!mpp) {
+		dev_err(iommu_dev, "pagefault without device to handle\n");
+		return 0;
+	}
+	mpp_task = mpp->cur_task;
+	if (mpp_task)
+		mpp_task_dump_mem_region(mpp, mpp_task);
+
+	mpp_task_dump_hw_reg(mpp);
+	/*
+	 * Mask iommu irq, in order for iommu not repeatedly trigger pagefault.
+	 * Until the pagefault task finish by hw timeout.
+	 */
+	rockchip_iommu_mask_irq(mpp->dev);
 
 	return 0;
 }
@@ -771,6 +959,36 @@
 	.dump_session = vepu_dump_session,
 };
 
+static struct mpp_dev_ops vepu_px30_dev_ops = {
+	.alloc_task = vepu_alloc_task,
+	.run = vepu_px30_run,
+	.irq = vepu_irq,
+	.isr = vepu_isr,
+	.finish = vepu_finish,
+	.result = vepu_result,
+	.free_task = vepu_free_task,
+	.ioctl = vepu_control,
+	.init_session = vepu_init_session,
+	.free_session = vepu_free_session,
+	.dump_session = vepu_dump_session,
+};
+
+static struct mpp_dev_ops vepu_ccu_dev_ops = {
+	.alloc_task = vepu_alloc_task,
+	.prepare = vepu_prepare,
+	.run = vepu_run,
+	.irq = vepu_irq,
+	.isr = vepu_isr,
+	.finish = vepu_finish,
+	.result = vepu_result,
+	.free_task = vepu_free_task,
+	.ioctl = vepu_control,
+	.init_session = vepu_init_session,
+	.free_session = vepu_free_session,
+	.dump_session = vepu_dump_session,
+};
+
+
 static const struct mpp_dev_var vepu_v2_data = {
 	.device_type = MPP_DEVICE_VEPU2,
 	.hw_info = &vepu_v2_hw_info,
@@ -784,7 +1002,15 @@
 	.hw_info = &vepu_v2_hw_info,
 	.trans_info = trans_rk_vepu2,
 	.hw_ops = &vepu_px30_hw_ops,
-	.dev_ops = &vepu_v2_dev_ops,
+	.dev_ops = &vepu_px30_dev_ops,
+};
+
+static const struct mpp_dev_var vepu_ccu_data = {
+	.device_type = MPP_DEVICE_VEPU2_JPEG,
+	.hw_info = &vepu_v2_hw_info,
+	.trans_info = trans_rk_vepu2,
+	.hw_ops = &vepu_v2_hw_ops,
+	.dev_ops = &vepu_ccu_dev_ops,
 };
 
 static const struct of_device_id mpp_vepu2_dt_match[] = {
@@ -798,10 +1024,83 @@
 		.data = &vepu_px30_data,
 	},
 #endif
+#ifdef CONFIG_CPU_RK3588
+	{
+		.compatible = "rockchip,vpu-jpege-core",
+		.data = &vepu_ccu_data,
+	},
+	{
+		.compatible = "rockchip,vpu-jpege-ccu",
+	},
+#endif
 	{},
 };
 
-static int vepu_probe(struct platform_device *pdev)
+static int vepu_ccu_probe(struct platform_device *pdev)
+{
+	struct vepu_ccu *ccu;
+	struct device *dev = &pdev->dev;
+
+	ccu = devm_kzalloc(dev, sizeof(*ccu), GFP_KERNEL);
+	if (!ccu)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, ccu);
+	spin_lock_init(&ccu->lock);
+	return 0;
+}
+
+static int vepu_attach_ccu(struct device *dev, struct vepu_dev *enc)
+{
+	struct device_node *np;
+	struct platform_device *pdev;
+	struct vepu_ccu *ccu;
+	unsigned long flags;
+
+	np = of_parse_phandle(dev->of_node, "rockchip,ccu", 0);
+	if (!np || !of_device_is_available(np))
+		return -ENODEV;
+
+	pdev = of_find_device_by_node(np);
+	of_node_put(np);
+	if (!pdev)
+		return -ENODEV;
+
+	ccu = platform_get_drvdata(pdev);
+	if (!ccu)
+		return -ENOMEM;
+
+	spin_lock_irqsave(&ccu->lock, flags);
+	ccu->core_num++;
+	ccu->cores[enc->mpp.core_id] = &enc->mpp;
+	set_bit(enc->mpp.core_id, &ccu->core_idle);
+	spin_unlock_irqrestore(&ccu->lock, flags);
+
+	/* attach the ccu-domain to current core */
+	if (!ccu->main_core) {
+		/**
+		 * set the first device for the main-core,
+		 * then the domain of the main-core named ccu-domain
+		 */
+		ccu->main_core = &enc->mpp;
+	} else {
+		struct mpp_iommu_info *ccu_info, *cur_info;
+
+		/* set the ccu domain for current device */
+		ccu_info = ccu->main_core->iommu_info;
+		cur_info = enc->mpp.iommu_info;
+
+		if (cur_info)
+			cur_info->domain = ccu_info->domain;
+		mpp_iommu_attach(cur_info);
+	}
+	enc->ccu = ccu;
+
+	dev_info(dev, "attach ccu success\n");
+	return 0;
+}
+
+static int vepu_core_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
 	struct vepu_dev *enc = NULL;
@@ -809,18 +1108,73 @@
 	const struct of_device_id *match = NULL;
 	int ret = 0;
 
-	dev_info(dev, "probe device\n");
 	enc = devm_kzalloc(dev, sizeof(struct vepu_dev), GFP_KERNEL);
 	if (!enc)
 		return -ENOMEM;
 
 	mpp = &enc->mpp;
-	platform_set_drvdata(pdev, enc);
+	platform_set_drvdata(pdev, mpp);
 
 	if (pdev->dev.of_node) {
 		match = of_match_node(mpp_vepu2_dt_match, pdev->dev.of_node);
 		if (match)
 			mpp->var = (struct mpp_dev_var *)match->data;
+
+		mpp->core_id = of_alias_get_id(pdev->dev.of_node, "jpege");
+	}
+
+	ret = mpp_dev_probe(mpp, pdev);
+	if (ret) {
+		dev_err(dev, "probe sub driver failed\n");
+		return -EINVAL;
+	}
+	/* current device attach to ccu */
+	ret = vepu_attach_ccu(dev, enc);
+	if (ret)
+		return ret;
+
+	ret = devm_request_threaded_irq(dev, mpp->irq,
+					mpp_dev_irq,
+					mpp_dev_isr_sched,
+					IRQF_SHARED,
+					dev_name(dev), mpp);
+	if (ret) {
+		dev_err(dev, "register interrupter runtime failed\n");
+		return -EINVAL;
+	}
+
+	mpp->fault_handler = vepu2_iommu_fault_handle;
+	mpp->session_max_buffers = VEPU2_SESSION_MAX_BUFFERS;
+	vepu_procfs_init(mpp);
+	vepu_procfs_ccu_init(mpp);
+	/* if current is main-core, register current device to mpp service */
+	if (mpp == enc->ccu->main_core)
+		mpp_dev_register_srv(mpp, mpp->srv);
+
+	return 0;
+}
+
+static int vepu_probe_default(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct vepu_dev *enc = NULL;
+	struct mpp_dev *mpp = NULL;
+	const struct of_device_id *match = NULL;
+	int ret = 0;
+
+	enc = devm_kzalloc(dev, sizeof(struct vepu_dev), GFP_KERNEL);
+	if (!enc)
+		return -ENOMEM;
+
+	mpp = &enc->mpp;
+	platform_set_drvdata(pdev, mpp);
+
+	if (pdev->dev.of_node) {
+		match = of_match_node(mpp_vepu2_dt_match, pdev->dev.of_node);
+		if (match)
+			mpp->var = (struct mpp_dev_var *)match->data;
+
+		mpp->core_id = of_alias_get_id(pdev->dev.of_node, "vepu");
 	}
 
 	ret = mpp_dev_probe(mpp, pdev);
@@ -839,43 +1193,77 @@
 		return -EINVAL;
 	}
 
+	mpp->fault_handler = vepu2_iommu_fault_handle;
 	mpp->session_max_buffers = VEPU2_SESSION_MAX_BUFFERS;
 	vepu_procfs_init(mpp);
 	/* register current device to mpp service */
 	mpp_dev_register_srv(mpp, mpp->srv);
-	dev_info(dev, "probing finish\n");
 
 	return 0;
+}
+
+static int vepu_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+
+	dev_info(dev, "probing start\n");
+
+	if (strstr(np->name, "ccu"))
+		ret = vepu_ccu_probe(pdev);
+	else if (strstr(np->name, "core"))
+		ret = vepu_core_probe(pdev);
+	else
+		ret = vepu_probe_default(pdev);
+
+	dev_info(dev, "probing finish\n");
+
+	return ret;
 }
 
 static int vepu_remove(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
-	struct vepu_dev *enc = platform_get_drvdata(pdev);
+	struct device_node *np = dev->of_node;
 
-	dev_info(dev, "remove device\n");
-	mpp_dev_remove(&enc->mpp);
-	vepu_procfs_remove(&enc->mpp);
+	if (strstr(np->name, "ccu")) {
+		dev_info(dev, "remove ccu device\n");
+	} else if (strstr(np->name, "core")) {
+		struct mpp_dev *mpp = dev_get_drvdata(dev);
+		struct vepu_dev *enc = to_vepu_dev(mpp);
+
+		dev_info(dev, "remove core\n");
+		if (enc->ccu) {
+			s32 core_id = mpp->core_id;
+			struct vepu_ccu *ccu = enc->ccu;
+			unsigned long flags;
+
+			spin_lock_irqsave(&ccu->lock, flags);
+			ccu->core_num--;
+			ccu->cores[core_id] = NULL;
+			clear_bit(core_id, &ccu->core_idle);
+			spin_unlock_irqrestore(&ccu->lock, flags);
+		}
+		mpp_dev_remove(&enc->mpp);
+		vepu_procfs_remove(&enc->mpp);
+	} else {
+		struct mpp_dev *mpp = dev_get_drvdata(dev);
+
+		dev_info(dev, "remove device\n");
+		mpp_dev_remove(mpp);
+		vepu_procfs_remove(mpp);
+	}
 
 	return 0;
 }
 
 static void vepu_shutdown(struct platform_device *pdev)
 {
-	int ret;
-	int val;
 	struct device *dev = &pdev->dev;
-	struct vepu_dev *enc = platform_get_drvdata(pdev);
-	struct mpp_dev *mpp = &enc->mpp;
 
-	dev_info(dev, "shutdown device\n");
-
-	atomic_inc(&mpp->srv->shutdown_request);
-	ret = readx_poll_timeout(atomic_read,
-				 &mpp->task_count,
-				 val, val == 0, 20000, 200000);
-	if (ret == -ETIMEDOUT)
-		dev_err(dev, "wait total running time out\n");
+	if (!strstr(dev_name(dev), "ccu"))
+		mpp_dev_shutdown(pdev);
 }
 
 struct platform_driver rockchip_vepu2_driver = {

--
Gitblit v1.6.2