linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/4] ASoC: mediatek: Add support of VCP on Mediatek mt8196 SoC.
@ 2025-09-14 12:29 Xiangzhi Tang
  2025-09-14 12:29 ` [PATCH v2 1/4] dt-bindings: remoteproc: Add VCP support for mt8196 Xiangzhi Tang
                   ` (4 more replies)
  0 siblings, 5 replies; 12+ messages in thread
From: Xiangzhi Tang @ 2025-09-14 12:29 UTC (permalink / raw)
  To: Bjorn Andersson, Mathieu Poirier, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
	AngeloGioacchino Del Regno, Xiangzhi Tang
  Cc: linux-remoteproc, devicetree, linux-kernel, linux-arm-kernel,
	linux-mediatek, Jjian Zhou, Hailong Fan, Xiangzhi Tang

Add support MediaTek's Video Companion Processor(VCP) host driver to
control the MediaTek VCP Risc-V coprocessor.

> This series is based on linux-next, tag: next-20250912.
> 
> Changes in v2:
> - Refactor: split vcp driver patch to mult diff
> - Fix reviewer's comments
> This series patches dependent on:
> [1]
> https://patchwork.kernel.org/project/linux-mediatek/patch/20250623120154.109429-2-angelogioacchino.delregno@collabora.com/
> [2]
> https://patchwork.kernel.org/project/linux-mediatek/patch/20250822021217.1598-3-jjian.zhou@mediatek.com/

Xiangzhi Tang (4):
  dt-bindings: remoteproc: Add VCP support for mt8196
  remoterpoc: mediatek: vcp: Add vcp remoteproc driver
  remoterpoc: mediatek: vcp: Add ipi-mbox communication
  remoterpoc: mediatek: vcp: Add vcp suspned and resume feature

 .../remoteproc/mediatek,mt8196-vcp.yaml       |  165 +++
 drivers/remoteproc/Kconfig                    |   12 +
 drivers/remoteproc/Makefile                   |    3 +
 drivers/remoteproc/mtk_vcp_common.c           | 1015 +++++++++++++++++
 drivers/remoteproc/mtk_vcp_common.h           |  284 +++++
 drivers/remoteproc/mtk_vcp_rproc.c            |  544 +++++++++
 drivers/remoteproc/mtk_vcp_rproc.h            |   93 ++
 include/linux/remoteproc/mtk_vcp_public.h     |  141 +++
 include/linux/soc/mediatek/mtk_sip_svc.h      |    3 +
 9 files changed, 2260 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/remoteproc/mediatek,mt8196-vcp.yaml
 create mode 100644 drivers/remoteproc/mtk_vcp_common.c
 create mode 100644 drivers/remoteproc/mtk_vcp_common.h
 create mode 100644 drivers/remoteproc/mtk_vcp_rproc.c
 create mode 100644 drivers/remoteproc/mtk_vcp_rproc.h
 create mode 100644 include/linux/remoteproc/mtk_vcp_public.h

-- 
2.46.0



^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH v2 1/4] dt-bindings: remoteproc: Add VCP support for mt8196
  2025-09-14 12:29 [PATCH v2 0/4] ASoC: mediatek: Add support of VCP on Mediatek mt8196 SoC Xiangzhi Tang
@ 2025-09-14 12:29 ` Xiangzhi Tang
  2025-09-14 13:18   ` Krzysztof Kozlowski
                     ` (2 more replies)
  2025-09-14 12:29 ` [PATCH v2 2/4] remoterpoc: mediatek: vcp: Add vcp remoteproc driver Xiangzhi Tang
                   ` (3 subsequent siblings)
  4 siblings, 3 replies; 12+ messages in thread
From: Xiangzhi Tang @ 2025-09-14 12:29 UTC (permalink / raw)
  To: Bjorn Andersson, Mathieu Poirier, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
	AngeloGioacchino Del Regno, Xiangzhi Tang
  Cc: linux-remoteproc, devicetree, linux-kernel, linux-arm-kernel,
	linux-mediatek, Jjian Zhou, Hailong Fan, Xiangzhi Tang

Add the new binding document for MediaTek Video Companion
Processor(VCP) on MediaTek mt8196.

Signed-off-by: Xiangzhi Tang <xiangzhi.tang@mediatek.com>
---
 .../remoteproc/mediatek,mt8196-vcp.yaml       | 165 ++++++++++++++++++
 1 file changed, 165 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/remoteproc/mediatek,mt8196-vcp.yaml

diff --git a/Documentation/devicetree/bindings/remoteproc/mediatek,mt8196-vcp.yaml b/Documentation/devicetree/bindings/remoteproc/mediatek,mt8196-vcp.yaml
new file mode 100644
index 000000000000..71a55943843b
--- /dev/null
+++ b/Documentation/devicetree/bindings/remoteproc/mediatek,mt8196-vcp.yaml
@@ -0,0 +1,165 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/remoteproc/mediatek,mt8196-vcp.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek Video Companion Processor (VCP)
+
+maintainers:
+  - Xiangzhi Tang <Xiangzhi.Tang@mediatek.com>
+
+description:
+  The MediaTek VCP enables the SoC control the MediaTek Video Companion Risc-V coprocessor.
+
+properties:
+  compatible:
+    enum:
+      - mediatek,mt8196-vcp
+
+  reg:
+    items:
+      - description: sram base
+      - description: cfg group IO
+      - description: cfg core group IO
+      - description: cfg sec group IO
+      - description: vcp rdy group IO
+
+  reg-names:
+    items:
+      - const: sram
+      - const: cfg
+      - const: cfg_core
+      - const: cfg_sec
+      - const: vcp_vlp_ao_rsvd7
+
+  interrupts:
+    maxItems: 1
+
+  mboxes:
+    maxItems: 5
+
+  mbox-names:
+    maxItems: 5
+
+  power-domains:
+    maxItems: 1
+
+  iommus:
+    description:
+      Using MediaTek iommu to apply larb ports for Multimedia Memory
+      Management Unit and address translation
+      Documentation/devicetree/bindings/iommu/arm,smmu-v3.yaml
+    maxItems: 1
+
+  memory-region:
+    maxItems: 1
+
+patternProperties:
+  "^vcp@[a-f0-9]+$":
+    type: object
+    description:
+      The MediaTek VCP integrated to SoC might be a multi-core version.
+      The other cores are represented as child nodes of the boot core.
+      There are some integration differences for the IP like the usage of
+      address translator for translating SoC bus addresses into address
+      space for the processor.
+
+      The SRAM are shared by all cores, each VCP core only using a piece
+      SRAM memory. The power of SRAM should be enabled before booting VCP cores.
+      The size of SRAM are varied on differnt SoCs.
+
+      The VCP cores has differences on different SoCs to support for
+      Hart.
+
+    properties:
+      compatible:
+        enum:
+          - mediatek,vcp-core
+
+      reg:
+        description: The base address and size of SRAM.
+        maxItems: 1
+
+      reg-names:
+        const: sram
+
+      mtk,vcp-core-twohart:
+        enum: [0, 1]
+        $ref: /schemas/types.yaml#/definitions/uint32
+
+      mtk,vcp-sram-offset:
+        description:
+          Allocated SRAM memory for each VCP core used.
+        $ref: /schemas/types.yaml#/definitions/uint32
+
+    required:
+      - compatible
+      - reg
+      - reg-names
+      - mtk,vcp-core-twohart
+      - mtk,vcp-sram-offset
+
+    additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - reg-names
+  - interrupts
+  - mboxes
+  - mbox-names
+  - power-domains
+  - iommus
+  - memory-region
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/interrupt-controller/irq.h>
+    #include <dt-bindings/power/mt8196-power.h>
+
+    vcp: vcp@31800000 {
+        compatible = "mediatek,mt8196-vcp";
+        reg = <0x31800000 0x60000>,
+              <0x31a04000 0xa000>,
+              <0x31bd0000 0x1000>,
+              <0x31a70020 0x100>,
+              <0x1c00091c 0x4>;
+        reg-names = "sram",
+                    "cfg",
+                    "cfg_core",
+                    "cfg_sec",
+                    "vcp_vlp_ao_rsvd7";
+
+        interrupts = <GIC_SPI 787 IRQ_TYPE_LEVEL_HIGH 0>;
+
+        mboxes = <&vcp_mailbox0>,
+                 <&vcp_mailbox1>,
+                 <&vcp_mailbox2>,
+                 <&vcp_mailbox3>,
+                 <&vcp_mailbox4>;
+        mbox-names = "mbox0", "mbox1", "mbox2", "mbox3", "mbox4";
+
+        power-domains = <&scpsys MT8196_POWER_DOMAIN_MM_PROC_DORMANT>;
+        iommus = <&mm_smmu 160>;
+        memory-region = <&vcp_resv_mem>;
+
+        vcp@0 {
+            compatible = "mediatek,vcp-core";
+            reg = <0x0 0x31000>;
+            reg-names = "sram";
+            mtk,vcp-core-twohart = <1>;
+            mtk,vcp-sram-offset = <0x0>;
+        };
+
+        vcp@31000 {
+            compatible = "mediatek,vcp-core";
+            reg = <0x31000 0x60000>;
+            reg-names = "sram";
+            mtk,vcp-core-twohart = <0>;
+            mtk,vcp-sram-offset = <0x31000>;
+        };
+    };
-- 
2.46.0



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v2 2/4] remoterpoc: mediatek: vcp: Add vcp remoteproc driver
  2025-09-14 12:29 [PATCH v2 0/4] ASoC: mediatek: Add support of VCP on Mediatek mt8196 SoC Xiangzhi Tang
  2025-09-14 12:29 ` [PATCH v2 1/4] dt-bindings: remoteproc: Add VCP support for mt8196 Xiangzhi Tang
@ 2025-09-14 12:29 ` Xiangzhi Tang
  2025-09-14 13:28   ` Krzysztof Kozlowski
  2025-09-15  9:14   ` AngeloGioacchino Del Regno
  2025-09-14 12:29 ` [PATCH v2 3/4] remoterpoc: mediatek: vcp: Add ipi-mbox communication Xiangzhi Tang
                   ` (2 subsequent siblings)
  4 siblings, 2 replies; 12+ messages in thread
From: Xiangzhi Tang @ 2025-09-14 12:29 UTC (permalink / raw)
  To: Bjorn Andersson, Mathieu Poirier, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
	AngeloGioacchino Del Regno, Xiangzhi Tang
  Cc: linux-remoteproc, devicetree, linux-kernel, linux-arm-kernel,
	linux-mediatek, Jjian Zhou, Hailong Fan, Xiangzhi Tang

Add host driver to control the mediatek Risc-V coprocessor

1.Support rproc mechanism to load vcm firmware from filesystem
2.Support SMC services to request ATF to setting vcp boot sequence

Signed-off-by: Xiangzhi Tang <xiangzhi.tang@mediatek.com>
---
 drivers/remoteproc/Kconfig                |  10 +
 drivers/remoteproc/Makefile               |   3 +
 drivers/remoteproc/mtk_vcp_common.c       | 677 ++++++++++++++++++++++
 drivers/remoteproc/mtk_vcp_common.h       | 225 +++++++
 drivers/remoteproc/mtk_vcp_rproc.c        | 326 +++++++++++
 drivers/remoteproc/mtk_vcp_rproc.h        |  71 +++
 include/linux/remoteproc/mtk_vcp_public.h |  78 +++
 include/linux/soc/mediatek/mtk_sip_svc.h  |   3 +
 8 files changed, 1393 insertions(+)
 create mode 100644 drivers/remoteproc/mtk_vcp_common.c
 create mode 100644 drivers/remoteproc/mtk_vcp_common.h
 create mode 100644 drivers/remoteproc/mtk_vcp_rproc.c
 create mode 100644 drivers/remoteproc/mtk_vcp_rproc.h
 create mode 100644 include/linux/remoteproc/mtk_vcp_public.h

diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index 48a0d3a69ed0..1a9bb49a8a28 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -64,6 +64,16 @@ config MTK_SCP
 
 	  It's safe to say N here.
 
+config MTK_VCP_RPROC
+	tristate "MediaTek VCP support"
+	depends on ARCH_MEDIATEK || COMPILE_TEST
+	depends on ARCH_DMA_ADDR_T_64BIT
+	help
+	  Say y here to support MediaTek's Video Companion Processor (VCP) via
+	  the remote processor framework.
+
+	  It's safe to say N here.
+
 config OMAP_REMOTEPROC
 	tristate "OMAP remoteproc support"
 	depends on ARCH_OMAP4 || SOC_OMAP5 || SOC_DRA7XX
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index 1c7598b8475d..ad48d85c5019 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -15,6 +15,9 @@ obj-$(CONFIG_IMX_REMOTEPROC)		+= imx_rproc.o
 obj-$(CONFIG_IMX_DSP_REMOTEPROC)	+= imx_dsp_rproc.o
 obj-$(CONFIG_INGENIC_VPU_RPROC)		+= ingenic_rproc.o
 obj-$(CONFIG_MTK_SCP)			+= mtk_scp.o mtk_scp_ipi.o
+obj-$(CONFIG_MTK_VCP_RPROC)		+= mtk_vcp.o
+mtk_vcp-$(CONFIG_MTK_VCP_RPROC)		+= mtk_vcp_rproc.o
+mtk_vcp-$(CONFIG_MTK_VCP_RPROC)		+= mtk_vcp_common.o
 obj-$(CONFIG_OMAP_REMOTEPROC)		+= omap_remoteproc.o
 obj-$(CONFIG_WKUP_M3_RPROC)		+= wkup_m3_rproc.o
 obj-$(CONFIG_DA8XX_REMOTEPROC)		+= da8xx_remoteproc.o
diff --git a/drivers/remoteproc/mtk_vcp_common.c b/drivers/remoteproc/mtk_vcp_common.c
new file mode 100644
index 000000000000..d53e311c4981
--- /dev/null
+++ b/drivers/remoteproc/mtk_vcp_common.c
@@ -0,0 +1,677 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 MediaTek Corporation. All rights reserved.
+ */
+
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/dma-buf.h>
+#include <linux/dma-heap.h>
+#include <linux/dma-mapping.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/iommu.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_fdt.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/slab.h>
+#include <uapi/linux/dma-heap.h>
+
+#include "mtk_vcp_common.h"
+#include "mtk_vcp_rproc.h"
+
+struct vcp_feature_tb feature_table[NUM_FEATURE_ID] = {
+	{
+		.feature    = RTOS_FEATURE_ID,
+		.core_id    = VCP_CORE_TOTAL,
+	},
+	{
+		.feature    = VDEC_FEATURE_ID,
+		.core_id    = VCP_ID,
+	},
+	{
+		.feature    = VENC_FEATURE_ID,
+		.core_id    = VCP_ID,
+	},
+	{
+		.feature    = MMDVFS_MMUP_FEATURE_ID,
+		.core_id    = MMUP_ID,
+	},
+	{
+		.feature    = MMDVFS_VCP_FEATURE_ID,
+		.core_id    = VCP_ID,
+	},
+	{
+		.feature    = MMDEBUG_FEATURE_ID,
+		.core_id    = MMUP_ID,
+	},
+	{
+		.feature    = VMM_FEATURE_ID,
+		.core_id    = MMUP_ID,
+	},
+	{
+		.feature    = VDISP_FEATURE_ID,
+		.core_id    = MMUP_ID,
+	},
+	{
+		.feature    = MMQOS_FEATURE_ID,
+		.core_id    = VCP_ID,
+	},
+};
+
+static struct vcp_reserve_mblock vcp_reserve_mblock[] = {
+	{
+		.num = VCP_RTOS_MEM_ID,
+		.start_phys = 0x0,
+		.start_iova = 0x0,
+		.start_virt = 0x0,
+		.size = 0x0,
+	},
+	{
+		.num = VDEC_MEM_ID,
+		.start_phys = 0x0,
+		.start_iova = 0x0,
+		.start_virt = 0x0,
+		.size = 0x0,
+	},
+	{
+		.num = VENC_MEM_ID,
+		.start_phys = 0x0,
+		.start_iova = 0x0,
+		.start_virt = 0x0,
+		.size = 0x0,
+	},
+	{
+		.num = MMDVFS_VCP_MEM_ID,
+		.start_phys = 0x0,
+		.start_iova = 0x0,
+		.start_virt = 0x0,
+		.size = 0x0,
+	},
+	{
+		.num = MMDVFS_MMUP_MEM_ID,
+		.start_phys = 0x0,
+		.start_iova = 0x0,
+		.start_virt = 0x0,
+		.size = 0x0,
+	},
+	{
+		.num = MMQOS_MEM_ID,
+		.start_phys = 0x0,
+		.start_iova = 0x0,
+		.start_virt = 0x0,
+		.size = 0x0,
+	},
+};
+
+phys_addr_t vcp_get_reserve_mem_phys(enum vcp_reserve_mem_id_t id)
+{
+	if (id >= 0 && id < NUMS_MEM_ID)
+		return vcp_reserve_mblock[id].start_phys;
+
+	return 0;
+}
+
+dma_addr_t vcp_get_reserve_mem_iova(enum vcp_reserve_mem_id_t id)
+{
+	if (id >= 0 && id < NUMS_MEM_ID)
+		return vcp_reserve_mblock[id].start_iova;
+
+	return 0;
+}
+
+void __iomem *vcp_get_reserve_mem_virt(enum vcp_reserve_mem_id_t id)
+{
+	if (id >= 0 && id < NUMS_MEM_ID)
+		return vcp_reserve_mblock[id].start_virt;
+
+	return NULL;
+}
+
+u32 vcp_get_reserve_mem_size(enum vcp_reserve_mem_id_t id)
+{
+	if (id >= 0 && id < NUMS_MEM_ID)
+		return vcp_reserve_mblock[id].size;
+
+	return 0;
+}
+
+void __iomem *vcp_get_internal_sram_virt(struct mtk_vcp_device *vcp)
+{
+	return vcp->vcp_cluster->sram_base;
+}
+
+int vcp_reserve_memory_ioremap(struct mtk_vcp_device *vcp)
+{
+#define MEMORY_TBL_ELEM_NUM (2)
+	u32 num = (u32)(sizeof(vcp_reserve_mblock)
+			/ sizeof(vcp_reserve_mblock[0]));
+	enum vcp_reserve_mem_id_t id;
+	u32 vcp_mem_num;
+	u32 i, m_idx, m_size;
+	u32 offset;
+	struct device_node *rmem_node;
+	struct resource res;
+	struct iommu_domain *domain;
+	void __iomem *share_memory_virt;
+	phys_addr_t mblock_start_phys;
+	dma_addr_t share_memory_iova;
+	size_t share_memory_size;
+	int ret;
+
+	if (num != NUMS_MEM_ID) {
+		dev_err(vcp->dev, "actual memory num(%u) is not match mem ID table (%u)\n",
+			num, NUMS_MEM_ID);
+		WARN_ON(1);
+		return -EINVAL;
+	}
+
+	rmem_node = of_parse_phandle(vcp->dev->of_node, "memory-region", 0);
+	if (!rmem_node) {
+		dev_err(vcp->dev, "No reserved memory region found.\n");
+		return -EINVAL;
+	}
+
+	ret = of_address_to_resource(rmem_node, 0, &res);
+	if (ret) {
+		dev_err(vcp->dev, "failed to parse reserved memory: %d\n", ret);
+		return ret;
+	}
+
+	mblock_start_phys = (phys_addr_t)res.start;
+
+	/* Set reserved memory table */
+	vcp_mem_num = of_property_count_u32_elems(vcp->dev->of_node, "vcp-mem-tbl")
+		      / MEMORY_TBL_ELEM_NUM;
+	if (vcp_mem_num <= 0) {
+		dev_warn(vcp->dev, "vcp-mem-tbl not found\n");
+		vcp_mem_num = 0;
+	}
+
+	for (i = 0; i < vcp_mem_num; i++) {
+		ret = of_property_read_u32_index(vcp->dev->of_node, "vcp-mem-tbl",
+						 i * MEMORY_TBL_ELEM_NUM, &m_idx);
+		if (ret) {
+			dev_err(vcp->dev, "cannot get memory index(%d)\n", i);
+			return -EINVAL;
+		}
+
+		ret = of_property_read_u32_index(vcp->dev->of_node, "vcp-mem-tbl",
+						 (i * MEMORY_TBL_ELEM_NUM) + 1, &m_size);
+		if (ret) {
+			dev_err(vcp->dev, "Cannot get memory size(%d)(%d)\n", i, m_idx);
+			return -EINVAL;
+		}
+
+		if (m_idx >= NUMS_MEM_ID) {
+			dev_warn(vcp->dev, "skip unexpected index, %d\n", m_idx);
+			continue;
+		}
+
+		vcp_reserve_mblock[m_idx].size = m_size;
+	}
+
+	vcp_reserve_mblock[VCP_RTOS_MEM_ID].start_phys = mblock_start_phys;
+	vcp_reserve_mblock[VCP_RTOS_MEM_ID].start_virt = devm_ioremap(vcp->dev,
+				vcp_reserve_mblock[VCP_RTOS_MEM_ID].start_phys,
+				vcp_reserve_mblock[VCP_RTOS_MEM_ID].size);
+	domain = iommu_get_domain_for_dev(vcp->dev);
+	ret = iommu_map(domain, vcp->platdata->rtos_static_iova,
+			vcp_reserve_mblock[VCP_RTOS_MEM_ID].start_phys,
+			vcp_reserve_mblock[VCP_RTOS_MEM_ID].size,
+			IOMMU_READ | IOMMU_WRITE | IOMMU_PRIV, GFP_KERNEL);
+	if (ret) {
+		dev_err(vcp->dev, "%s iommu map fail, ret:%d.\n", __func__, ret);
+		return ret;
+	}
+	vcp_reserve_mblock[VCP_RTOS_MEM_ID].start_iova = vcp->platdata->rtos_static_iova;
+
+	share_memory_size = 0;
+	for (id = VDEC_MEM_ID; id < NUMS_MEM_ID; id++) {
+		if (vcp_reserve_mblock[id].size == 0)
+			continue;
+		share_memory_size += vcp_reserve_mblock[id].size;
+	}
+
+	ret = dma_set_mask_and_coherent(vcp->dev, DMA_BIT_MASK(DMA_MAX_MASK_BIT));
+	if (ret) {
+		dev_err(vcp->dev, "64-bit DMA enable failed\n");
+		return ret;
+	}
+
+	if (!vcp->dev->dma_parms) {
+		vcp->dev->dma_parms = devm_kzalloc(vcp->dev,
+						   sizeof(*vcp->dev->dma_parms),
+						   GFP_KERNEL);
+		if (vcp->dev->dma_parms) {
+			dma_set_max_seg_size(vcp->dev, (u32)DMA_BIT_MASK(33));
+		} else {
+			dev_err(vcp->dev, "Failed to set DMA parms\n");
+			return -EINVAL;
+		}
+	}
+	share_memory_virt = dma_alloc_coherent(vcp->dev,
+					       share_memory_size,
+					       &share_memory_iova,
+					       GFP_KERNEL);
+	if (!share_memory_virt)
+		return -ENOMEM;
+	offset = 0;
+	for (id = VDEC_MEM_ID; id < NUMS_MEM_ID; id++)  {
+		if (vcp_reserve_mblock[id].size == 0)
+			continue;
+
+		vcp_reserve_mblock[id].start_phys = vcp_reserve_mblock[VCP_RTOS_MEM_ID].start_phys +
+						    vcp_reserve_mblock[VCP_RTOS_MEM_ID].size +
+						    offset;
+		vcp_reserve_mblock[id].start_iova = share_memory_iova + offset;
+		vcp_reserve_mblock[id].start_virt = share_memory_virt + offset;
+		offset += (u32)vcp_reserve_mblock[id].size;
+	}
+
+	vcp->vcp_cluster->share_mem_iova = share_memory_iova;
+	vcp->vcp_cluster->share_mem_size = share_memory_size;
+
+	return 0;
+}
+
+static bool is_vcp_ready_by_coreid(enum vcp_core_id core_id)
+{
+	struct device *dev;
+	struct mtk_vcp_device *vcp;
+
+	dev = feature_table[RTOS_FEATURE_ID].priv;
+	if (!dev)
+		return false;
+	vcp = platform_get_drvdata(to_platform_device(dev));
+	if (!vcp)
+		return false;
+
+	switch (core_id) {
+	case VCP_ID:
+		return vcp->vcp_cluster->vcp_ready[VCP_ID];
+	case MMUP_ID:
+		return vcp->vcp_cluster->vcp_ready[MMUP_ID];
+	case VCP_CORE_TOTAL:
+	default:
+		return vcp->vcp_cluster->vcp_ready[VCP_ID] &
+		       vcp->vcp_cluster->vcp_ready[MMUP_ID];
+	}
+}
+
+u32 wait_core_hart_shutdown(struct mtk_vcp_device *vcp,
+			    enum vcp_core_id core_id)
+{
+	u32 retry;
+	bool twohart_support;
+	u32 core_hart0;
+	u32 core_hart1;
+
+	twohart_support = vcp->vcp_cluster->twohart[core_id];
+
+	for (retry = VCP_AWAKE_TIMEOUT; retry > 0; retry--) {
+		switch (core_id) {
+		case VCP_ID:
+			core_hart0 = readl(vcp->vcp_cluster->cfg + VCP_C0_GPR5_H0_REBOOT);
+			if (twohart_support)
+				core_hart1 = readl(vcp->vcp_cluster->cfg + VCP_C0_GPR6_H1_REBOOT);
+			break;
+		case MMUP_ID:
+			core_hart0 = readl(vcp->vcp_cluster->cfg + VCP_C1_GPR5_H0_REBOOT);
+			if (twohart_support)
+				core_hart1 = readl(vcp->vcp_cluster->cfg + VCP_C1_GPR6_H1_REBOOT);
+			break;
+		case VCP_CORE_TOTAL:
+		default:
+			break;
+		}
+
+		if (twohart_support) {
+			if (core_hart0 == CORE_RDY_TO_REBOOT &&
+			    core_hart1 == CORE_RDY_TO_REBOOT)
+				break;
+		} else {
+			if (core_hart0 == CORE_RDY_TO_REBOOT)
+				break;
+		}
+		usleep_range(USDELAY_RANGE_MIN, USDELAY_RANGE_MAX);
+	}
+
+	return retry;
+}
+
+static int reset_vcp(struct mtk_vcp_device *vcp)
+{
+	struct arm_smccc_res res;
+
+	if (vcp->vcp_cluster->core_nums >= MMUP_ID) {
+		/* write vcp reserved memory address/size to GRP1/GRP2
+		 * to let vcp setup MPU
+		 */
+		writel((u32)VCP_PACK_IOVA(vcp->vcp_cluster->share_mem_iova),
+		       vcp->vcp_cluster->cfg + VCP_C1_GPR1_DRAM_RESV_ADDR);
+		writel((u32)vcp->vcp_cluster->share_mem_size,
+		       vcp->vcp_cluster->cfg + VCP_C1_GPR2_DRAM_RESV_SIZE);
+
+		arm_smccc_smc(MTK_SIP_TINYSYS_VCP_CONTROL,
+			      MTK_TINYSYS_MMUP_KERNEL_OP_RESET_RELEASE,
+			      1, 0, 0, 0, 0, 0, &res);
+	}
+
+	/* write vcp reserved memory address/size to GRP1/GRP2
+	 * to let vcp setup MPU
+	 */
+	writel((u32)VCP_PACK_IOVA(vcp->vcp_cluster->share_mem_iova),
+	       vcp->vcp_cluster->cfg + VCP_C0_GPR1_DRAM_RESV_ADDR);
+	writel((u32)vcp->vcp_cluster->share_mem_size,
+	       vcp->vcp_cluster->cfg + VCP_C0_GPR2_DRAM_RESV_SIZE);
+
+	arm_smccc_smc(MTK_SIP_TINYSYS_VCP_CONTROL,
+		      MTK_TINYSYS_VCP_KERNEL_OP_RESET_RELEASE,
+		      1, 0, 0, 0, 0, 0, &res);
+	return 0;
+}
+
+static int vcp_enable_pm_clk(struct mtk_vcp_device *vcp, enum feature_id id)
+{
+	mutex_lock(&vcp->vcp_cluster->vcp_pw_clk_mutex);
+	if (vcp->vcp_cluster->pwclkcnt == 0) {
+		if (!is_vcp_ready_by_coreid(VCP_CORE_TOTAL)) {
+			if (reset_vcp(vcp)) {
+				mutex_unlock(&vcp->vcp_cluster->vcp_pw_clk_mutex);
+				return -EINVAL;
+			}
+		}
+	}
+	vcp->vcp_cluster->pwclkcnt++;
+	mutex_unlock(&vcp->vcp_cluster->vcp_pw_clk_mutex);
+
+	return 0;
+}
+
+static int vcp_disable_pm_clk(struct mtk_vcp_device *vcp, enum feature_id id)
+{
+	mutex_lock(&vcp->vcp_cluster->vcp_pw_clk_mutex);
+	vcp->vcp_cluster->pwclkcnt--;
+	if (vcp->vcp_cluster->pwclkcnt < 0) {
+		for (u32 i = 0; i < NUM_FEATURE_ID; i++)
+			dev_warn(vcp->dev, "%s Check feature id %d enable cnt %d\n",
+				 __func__, feature_table[i].feature, feature_table[i].enable);
+		vcp->vcp_cluster->pwclkcnt = 0;
+		return -EINVAL;
+	}
+	mutex_unlock(&vcp->vcp_cluster->vcp_pw_clk_mutex);
+
+	return 0;
+}
+
+int vcp_A_register_feature(struct mtk_vcp_device *vcp, enum feature_id id)
+{
+	int ret;
+
+	if (id >= NUM_FEATURE_ID) {
+		dev_err(vcp->dev, "%s unsupported feature id %d\n",
+			__func__, id);
+		return -EINVAL;
+	}
+
+	mutex_lock(&vcp->vcp_cluster->vcp_feature_mutex);
+	for (u32 i = 0; i < NUM_FEATURE_ID; i++) {
+		if (feature_table[i].feature == id)
+			feature_table[i].enable++;
+		if (id == RTOS_FEATURE_ID)
+			feature_table[i].priv = vcp->dev;
+	}
+	ret = vcp_enable_pm_clk(vcp, id);
+	mutex_unlock(&vcp->vcp_cluster->vcp_feature_mutex);
+
+	return ret;
+}
+
+int vcp_A_deregister_feature(struct mtk_vcp_device *vcp, enum feature_id id)
+{
+	int ret;
+
+	if (id >= NUM_FEATURE_ID) {
+		dev_warn(vcp->dev, "%s unsupported feature id %d\n",
+			 __func__, id);
+		return -EINVAL;
+	}
+	mutex_lock(&vcp->vcp_cluster->vcp_feature_mutex);
+	for (u32 i = 0; i < NUM_FEATURE_ID; i++) {
+		if (feature_table[i].feature == id) {
+			if (feature_table[i].enable == 0) {
+				dev_warn(vcp->dev, "%s unbalanced feature id %d enable cnt %d\n",
+					 __func__, id, feature_table[i].enable);
+				mutex_unlock(&vcp->vcp_cluster->vcp_feature_mutex);
+				return -EINVAL;
+			}
+			feature_table[i].enable--;
+		}
+	}
+	ret = vcp_disable_pm_clk(vcp, id);
+	mutex_unlock(&vcp->vcp_cluster->vcp_feature_mutex);
+
+	return ret;
+}
+
+static size_t load_part_binary(void __iomem *image_buf,
+			       const u8 *fw_src,
+			       size_t size,
+			       const char *part_bin_name)
+{
+	const u8 *fw_ptr = fw_src;
+	u32 offset;
+	u32 align_size;
+	const struct mkimg_hdr *img_hdr_info;
+
+	if (!fw_src || !image_buf || size < VCP_IMAGE_HEADER_SIZE)
+		return 0;
+
+	offset = 0;
+	while (offset < size) {
+		img_hdr_info = (const struct mkimg_hdr *)(fw_ptr + offset);
+		align_size = round_up(img_hdr_info->dsz, ALIGN_16);
+		offset += VCP_IMAGE_HEADER_SIZE;
+		if (img_hdr_info->magic != VCM_IMAGE_MAGIC ||
+		    strncmp(img_hdr_info->name, part_bin_name, VCM_IMAGE_NAME_MAXSZ - 1)) {
+			offset += align_size;
+		} else {
+			memcpy_toio(image_buf, fw_ptr + offset, img_hdr_info->dsz);
+			offset += align_size;
+			return img_hdr_info->dsz;
+		}
+	}
+
+	return 0;
+}
+
+static int load_vcp_bin(const u8 *fw_src,
+			size_t size,
+			void __iomem *img_buf_va,
+			phys_addr_t img_buf_pa,
+			dma_addr_t img_buf_iova,
+			struct mtk_vcp_device *vcp)
+{
+	u32 fw_size;
+	u32 dram_img_size;
+	u32 dram_backup_img_offset;
+	struct vcp_region_info_st vcp_region_info = {};
+	struct arm_smccc_res res;
+
+	fw_size = load_part_binary(vcp->vcp_cluster->sram_base +
+				   vcp->vcp_cluster->sram_offset[VCP_ID],
+				   fw_src, size, VCP_HFRP_PART_NAME);
+	if (!fw_size) {
+		dev_err(vcp->dev, "load %s failed\n", VCP_HFRP_PART_NAME);
+		return -EINVAL;
+	}
+
+	dram_img_size = load_part_binary(img_buf_va + VCP_DRAM_IMG_OFFSET,
+					 fw_src, size, VCP_HFRP_DRAM_PART_NAME);
+	if (!dram_img_size) {
+		dev_err(vcp->dev, "load %s failed\n", VCP_HFRP_DRAM_PART_NAME);
+		return -EINVAL;
+	}
+
+	vcp_region_info.struct_size = sizeof(struct vcp_region_info_st);
+
+	dram_backup_img_offset = VCP_DRAM_IMG_OFFSET + round_up(dram_img_size, ALIGN_1024);
+
+	vcp_region_info.ap_dram_start = VCP_PACK_IOVA(img_buf_iova + VCP_DRAM_IMG_OFFSET);
+	vcp_region_info.ap_dram_backup_start = VCP_PACK_IOVA(img_buf_iova + dram_backup_img_offset);
+	vcp_region_info.ap_dram_size = dram_img_size;
+
+	vcp_region_info.l2tcm_offset = vcp->vcp_cluster->sram_offset[MMUP_ID];
+
+	memcpy_toio(vcp->vcp_cluster->sram_base +
+		    vcp->vcp_cluster->sram_offset[VCP_ID] + REGION_OFFSET,
+		    &vcp_region_info, sizeof(vcp_region_info));
+
+	arm_smccc_smc(MTK_SIP_TINYSYS_VCP_CONTROL,
+		      MTK_TINYSYS_MMUP_KERNEL_OP_SET_L2TCM_OFFSET,
+		      vcp->vcp_cluster->sram_offset[MMUP_ID],
+		      0, 0, 0, 0, 0, &res);
+
+	return 0;
+}
+
+static int load_mmup_bin(const u8 *fw_src,
+			 size_t size,
+			 void __iomem *img_buf_va,
+			 phys_addr_t img_buf_pa,
+			 dma_addr_t img_buf_iova,
+			 struct mtk_vcp_device *vcp)
+{
+	u32 fw_size;
+	u32 dram_img_size;
+	u32 dram_backup_img_offset;
+	struct vcp_region_info_st vcp_region_info = {};
+	struct arm_smccc_res res;
+
+	fw_size = load_part_binary(vcp->vcp_cluster->sram_base +
+				   vcp->vcp_cluster->sram_offset[MMUP_ID],
+				   fw_src, size, VCP_MMUP_PART_NAME);
+	if (!fw_size) {
+		dev_err(vcp->dev, "load %s failed\n", VCP_MMUP_PART_NAME);
+		return -EINVAL;
+	}
+
+	dram_img_size = load_part_binary(img_buf_va + MMUP_DRAM_IMG_OFFSET, fw_src, size,
+					 VCP_MMUP_DRAM_PART_NAME);
+	if (!dram_img_size) {
+		dev_err(vcp->dev, "load %s failed\n", VCP_MMUP_DRAM_PART_NAME);
+		return -EINVAL;
+	}
+
+	vcp_region_info.struct_size = sizeof(struct vcp_region_info_st);
+
+	dram_backup_img_offset = MMUP_DRAM_IMG_OFFSET + round_up(dram_img_size, ALIGN_1024);
+	vcp_region_info.ap_dram_start = VCP_PACK_IOVA(img_buf_iova + MMUP_DRAM_IMG_OFFSET);
+	vcp_region_info.ap_dram_backup_start = VCP_PACK_IOVA(img_buf_iova + dram_backup_img_offset);
+	vcp_region_info.ap_dram_size = dram_img_size;
+
+	memcpy_toio(vcp->vcp_cluster->sram_base +
+		    vcp->vcp_cluster->sram_offset[MMUP_ID] + REGION_OFFSET,
+		    &vcp_region_info, sizeof(vcp_region_info));
+
+	arm_smccc_smc(MTK_SIP_TINYSYS_VCP_CONTROL,
+		      MTK_TINYSYS_MMUP_KERNEL_OP_SET_FW_SIZE,
+		      fw_size, 0, 0, 0, 0, 0, &res);
+
+	return 0;
+}
+
+int mtk_vcp_load(struct rproc *rproc, const struct firmware *fw)
+{
+	struct arm_smccc_res res;
+	struct mtk_vcp_device *vcp = (struct mtk_vcp_device *)rproc->priv;
+	dma_addr_t img_buf_iova;
+	phys_addr_t img_buf_phys;
+	void __iomem *img_buf_va;
+	int ret;
+
+	if (!vcp) {
+		dev_err(vcp->dev, "vcp device is no exist!\n");
+		return -EINVAL;
+	}
+
+	if (fw->size < VCP_IMAGE_HEADER_SIZE ||
+	    fw->size > vcp->ops->vcp_get_mem_size(VCP_RTOS_MEM_ID)) {
+		dev_err(vcp->dev, "firmware is oversize/undersize\n");
+		return -EINVAL;
+	}
+
+	writel(0x1, vcp->vcp_cluster->cfg_core + VCP_R_CORE0_SW_RSTN_SET);
+	writel(0x1, vcp->vcp_cluster->cfg_core + VCP_R_CORE1_SW_RSTN_SET);
+
+	memset_io(vcp->vcp_cluster->sram_base, 0, vcp->vcp_cluster->sram_size);
+
+	img_buf_iova = vcp->ops->vcp_get_mem_iova(VCP_RTOS_MEM_ID);
+	img_buf_phys = vcp->ops->vcp_get_mem_phys(VCP_RTOS_MEM_ID);
+	img_buf_va = vcp->ops->vcp_get_mem_virt(VCP_RTOS_MEM_ID);
+
+	arm_smccc_smc(MTK_SIP_TINYSYS_VCP_CONTROL,
+		      MTK_TINYSYS_VCP_KERNEL_OP_COLD_BOOT_VCP,
+		      0, 0, 0, 0, 0, 0, &res);
+
+	ret = load_vcp_bin(fw->data, fw->size,
+			   img_buf_va, img_buf_phys,
+			   img_buf_iova, vcp);
+	if (ret) {
+		dev_err(vcp->dev, "load vcp bin failed\n");
+		return -EINVAL;
+	}
+
+	ret = load_mmup_bin(fw->data, fw->size,
+			    img_buf_va, img_buf_phys,
+			    img_buf_iova, vcp);
+	if (ret) {
+		dev_err(vcp->dev, "load mmup bin failed\n");
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+static irqreturn_t vcp_irq_handler(int irq, void *priv)
+{
+	u32 reg0, reg1;
+	struct mtk_vcp_device *vcp = priv;
+
+	disable_irq_nosync(irq);
+
+	reg0 = readl(vcp->vcp_cluster->cfg_core + R_CORE0_WDT_IRQ);
+	reg1 = vcp->vcp_cluster->core_nums > VCP_ID ?
+	       readl(vcp->vcp_cluster->cfg_core + R_CORE1_WDT_IRQ) : 0;
+
+	if (reg0)
+		writel(B_WDT_IRQ, vcp->vcp_cluster->cfg_core + R_CORE0_WDT_IRQ);
+	if (reg1)
+		writel(B_WDT_IRQ, vcp->vcp_cluster->cfg_core + R_CORE1_WDT_IRQ);
+
+	if (reg0 || reg1)
+		return IRQ_HANDLED;
+
+	return IRQ_NONE;
+}
+
+int vcp_wdt_irq_init(struct mtk_vcp_device *vcp)
+{
+	int ret;
+
+	ret = devm_request_irq(vcp->dev, platform_get_irq(vcp->pdev, 0),
+			       vcp_irq_handler, IRQF_ONESHOT,
+			       vcp->pdev->name, vcp);
+	if (ret)
+		dev_err(vcp->dev, "failed to request wdt irq\n");
+
+	return ret;
+}
+
+MODULE_AUTHOR("Xiangzhi Tang <xiangzhi.tang@mediatek.com>");
+MODULE_DESCRIPTION("MTK VCP Controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/remoteproc/mtk_vcp_common.h b/drivers/remoteproc/mtk_vcp_common.h
new file mode 100644
index 000000000000..8340f0bd4fdc
--- /dev/null
+++ b/drivers/remoteproc/mtk_vcp_common.h
@@ -0,0 +1,225 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Copyright (c) 2025 MediaTek Inc.
+ */
+
+#ifndef __MTK_VCP_COMMON_H
+#define __MTK_VCP_COMMON_H
+
+#include <linux/arm-smccc.h>
+#include <linux/bitops.h>
+#include <linux/firmware.h>
+#include <linux/soc/mediatek/mtk_sip_svc.h>
+#include <linux/remoteproc/mtk_vcp_public.h>
+
+/* vcp timeout definition */
+#define VCP_AWAKE_TIMEOUT 1000
+#define USDELAY_RANGE_MIN 1000
+#define USDELAY_RANGE_MAX 2000
+
+/* vcp platform define */
+#define DMA_MAX_MASK_BIT 33
+
+/* vcp load image define */
+#define VCM_IMAGE_MAGIC             (0x58881688)
+#define VCM_IMAGE_NAME_MAXSZ        (32)
+#define VCP_IMAGE_HEADER_SIZE       (0x200)
+
+#define VCP_DRAM_IMG_OFFSET         (0x200000)
+#define MMUP_DRAM_IMG_OFFSET        (0x1200000)
+
+#define REGION_OFFSET               (0x4)
+#define ALIGN_1024                  (1024)
+#define ALIGN_16                    (16)
+#define VCP_HFRP_PART_NAME          "tinysys-vcp-RV55_A"
+#define VCP_MMUP_PART_NAME          "tinysys-mmup-RV33_A"
+#define VCP_HFRP_DRAM_PART_NAME     "tinysys-vcp-RV55_A_dram"
+#define VCP_MMUP_DRAM_PART_NAME     "tinysys-mmup-RV33_A_dram"
+
+/* vcp memory iove pack convert define */
+#define VCP_PACK_IOVA(addr)     ((uint32_t)((addr) | (((uint64_t)(addr) >> 32) & 0xF)))
+#define VCP_UNPACK_IOVA(addr)   \
+	((uint64_t)(addr & 0xFFFFFFF0) | (((uint64_t)(addr) & 0xF) << 32))
+
+/* vcp register define */
+#define VCP_R_CORE0_SW_RSTN_SET         (0x0004)
+#define VCP_R_CORE1_SW_RSTN_SET         (0x000C)
+#define R_GIPC_IN_SET                   (0x0028)
+#define R_GIPC_IN_CLR                   (0x002C)
+#define GIPC_MMUP_SHUT                  (BIT(10))
+#define GIPC_VCP_HART0_SHUT             (BIT(14))
+#define B_GIPC4_SETCLR_3                (BIT(19))
+#define R_CORE0_WDT_IRQ                 (0x0050)
+#define R_CORE1_WDT_IRQ                 (0x0054)
+#define B_WDT_IRQ                       (BIT(0))
+#define AP_R_GPR2                       (0x0068)
+#define B_CORE0_SUSPEND                 (BIT(0))
+#define B_CORE0_RESUME                  (BIT(1))
+#define AP_R_GPR3                       (0x006C)
+#define B_CORE1_SUSPEND                 (BIT(0))
+#define B_CORE1_RESUME                  (BIT(1))
+
+#define R_CORE0_STATUS                  (0x6070)
+#define B_CORE_GATED                    (BIT(0))
+#define B_HART0_HALT                    (BIT(1))
+#define B_HART1_HALT                    (BIT(2))
+#define B_CORE_AXIS_BUSY                (BIT(4))
+#define R_CORE1_STATUS                  (0x9070)
+#define VCP_C0_GPR0_SUSPEND_RESUME_FLAG (0x6040)
+#define VCP_C0_GPR1_DRAM_RESV_ADDR      (0x6044)
+#define VCP_C0_GPR2_DRAM_RESV_SIZE      (0x6048)
+#define VCP_C0_GPR3_DRAM_RESV_LOGGER    (0x604C)
+#define VCP_C0_GPR5_H0_REBOOT           (0x6054)
+#define CORE_RDY_TO_REBOOT              (0x0034)
+#define VCP_C0_GPR6_H1_REBOOT           (0x6058)
+#define VCP_C1_GPR0_SUSPEND_RESUME_FLAG (0x9040)
+#define VCP_C1_GPR1_DRAM_RESV_ADDR      (0x9044)
+#define VCP_C1_GPR2_DRAM_RESV_SIZE      (0x9048)
+#define VCP_C1_GPR3_DRAM_RESV_LOGGER    (0x904C)
+#define VCP_C1_GPR5_H0_REBOOT           (0x9054)
+#define VCP_C1_GPR6_H1_REBOOT           (0x9058)
+
+/* sec GPR */
+#define R_GPR2_SEC                      (0x0008)
+#define MMUP_AP_SUSPEND                 (BIT(0))
+#define R_GPR3_SEC                      (0x000C)
+#define VCP_AP_SUSPEND                  (BIT(0))
+
+/* vcp rdy */
+#define VLP_AO_RSVD7                    (0x0000)
+#define READY_BIT                       (BIT(1))
+
+/* vcp Core ID definition */
+enum vcp_core_id {
+	VCP_ID          = 0,
+	MMUP_ID         = 1,
+	VCP_CORE_TOTAL  = 2,
+};
+
+/* vcp kernel smc server id */
+enum mtk_tinysys_vcp_kernel_op {
+	MTK_TINYSYS_VCP_KERNEL_OP_RESET_SET = 0,
+	MTK_TINYSYS_VCP_KERNEL_OP_RESET_RELEASE,
+	MTK_TINYSYS_VCP_KERNEL_OP_COLD_BOOT_VCP,
+	MTK_TINYSYS_MMUP_KERNEL_OP_RESET_SET,
+	MTK_TINYSYS_MMUP_KERNEL_OP_RESET_RELEASE,
+	MTK_TINYSYS_MMUP_KERNEL_OP_SET_L2TCM_OFFSET,
+	MTK_TINYSYS_MMUP_KERNEL_OP_SET_FW_SIZE,
+	MTK_TINYSYS_MMUP_KERNEL_OP_COLD_BOOT_MMUP,
+	MTK_TINYSYS_VCP_KERNEL_OP_NUM,
+};
+
+/**
+ * struct mkimg_hdr - mtk image header format.
+ *
+ * @magic: mtk vcp image magic id
+ * @dsz: mtk vcp image part binary size
+ * @name: mtk vcp image part binary parttion name
+ */
+struct mkimg_hdr {
+	u32 magic;
+	u32 dsz;
+	char name[VCM_IMAGE_NAME_MAXSZ];
+};
+
+/**
+ * struct vcp_feature_tb - feature table structure definition.
+ *
+ * @feature: feature id
+ * @core_id: feature using vcp core id
+ * @enable: whether the feature is enabled or not
+ */
+struct vcp_feature_tb {
+	enum vcp_core_id core_id;
+	u32 feature;
+	u32 enable;
+	void *priv;
+};
+
+/**
+ * struct vcp_reserve_mblock - vcp reserved memory structure.
+ *
+ * @vcp_reserve_mem_id_t: reserved memory id
+ * @start_phys: reserved memory phy addr
+ * @start_iova: reserved memory dma map addr
+ * @start_virt: reserved memory CPU virt addr
+ * @size: reserved memory size
+ */
+struct vcp_reserve_mblock {
+	enum vcp_reserve_mem_id_t num;
+	phys_addr_t start_phys;
+	dma_addr_t start_iova;
+	void __iomem *start_virt;
+	size_t size;
+};
+
+/**
+ * struct vcp_region_info_st - config vcp image info sync to vcp bootloader.
+ *
+ * @ap_loader_start: (optional) - config vcp bootloader to copy loader start addr
+ * @ap_loader_size: (optional) - config vcp bootloader to copy loader size
+ * @ap_firmware_start: (optional) - config vcp bootloader to copy firmware start addr
+ * @ap_firmware_size: (optional) - config vcp bootloader to copy firmware size
+ * @ap_dram_start: config vcp run dram binary start addr
+ * @ap_dram_size: config vcp run dram binary size
+ * @ap_dram_backup_start: config vcp backup dram binary start addr
+ * @struct_size: vcp image region info structure size
+ * @l2tcm_offset: vcp two core using l2sram layout
+ * @TaskContext_ptr: (optional) - vcp task context ptr for debug
+ * @vcpctl:  (optional) - vcp control info
+ * @regdump_start: (optional) - regdump start addr for debug
+ * @regdump_size: (optional) - regdump size for debug
+ * @ap_params_start: (optional) - params start addr
+ * @sramlog_buf_offset: (optional) - sramlog_buf_offset for debug
+ * @sramlog_end_idx_offset: (optional) - sramlog_end_idx_offset for debug
+ * @sramlog_buf_maxlen: (optional) - sramlog_buf_maxlen for debug
+ * @ap_loader_start_pa: (optional) - config vcp bootloader for loader start pa
+ * @coredump_offset: (optional) - coredump_offset offset for debug
+ * @coredump_dram_offset: (optional) - coredump_dram_offset offset for debug
+ */
+struct vcp_region_info_st {
+	u32 ap_loader_start;
+	u32 ap_loader_size;
+	u32 ap_firmware_start;
+	u32 ap_firmware_size;
+	u32 ap_dram_start;
+	u32 ap_dram_size;
+	u32 ap_dram_backup_start;
+	u32 struct_size;
+	u32 l2tcm_offset;
+	u32 TaskContext_ptr;
+	u32 vcpctl;
+	u32 regdump_start;
+	u32 regdump_size;
+	u32 ap_params_start;
+	u32 sramlog_buf_offset;
+	u32 sramlog_end_idx_offset;
+	u32 sramlog_buf_maxlen;
+	u32 ap_loader_start_pa;
+	u32 coredump_offset;
+	u32 coredump_dram_offset;
+};
+
+/* vcp common reserved memory APIs */
+int vcp_reserve_memory_ioremap(struct mtk_vcp_device *vcp);
+phys_addr_t vcp_get_reserve_mem_phys(enum vcp_reserve_mem_id_t id);
+dma_addr_t vcp_get_reserve_mem_iova(enum vcp_reserve_mem_id_t id);
+void __iomem *vcp_get_reserve_mem_virt(enum vcp_reserve_mem_id_t id);
+void __iomem *vcp_get_internal_sram_virt(struct mtk_vcp_device *vcp);
+u32 vcp_get_reserve_mem_size(enum vcp_reserve_mem_id_t id);
+
+/* vcp common load image API */
+int mtk_vcp_load(struct rproc *rproc, const struct firmware *fw);
+
+/* vcp common wdt irq init API */
+int vcp_wdt_irq_init(struct mtk_vcp_device *vcp);
+
+/* vcp common feature register/deregister APIs */
+int vcp_A_register_feature(struct mtk_vcp_device *vcp,
+			   enum feature_id id);
+int vcp_A_deregister_feature(struct mtk_vcp_device *vcp,
+			     enum feature_id id);
+
+/* vcp common core hart shutdown API */
+u32 wait_core_hart_shutdown(struct mtk_vcp_device *vcp, enum vcp_core_id core_id);
+#endif
diff --git a/drivers/remoteproc/mtk_vcp_rproc.c b/drivers/remoteproc/mtk_vcp_rproc.c
new file mode 100644
index 000000000000..bf4736ce6795
--- /dev/null
+++ b/drivers/remoteproc/mtk_vcp_rproc.c
@@ -0,0 +1,326 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 MediaTek Corporation. All rights reserved.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/remoteproc.h>
+
+#include "mtk_vcp_common.h"
+#include "mtk_vcp_rproc.h"
+#include "remoteproc_internal.h"
+
+/**
+ * vcp_get() - get a reference to VCP.
+ *
+ * @pdev: the platform device of the module requesting VCP platform
+ *        device for using VCP API.
+ *
+ * Return: Return NULL if failed.  otherwise reference to VCP.
+ **/
+struct mtk_vcp_device *vcp_get(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *vcp_node;
+	struct platform_device *vcp_pdev;
+
+	vcp_node = of_parse_phandle(dev->of_node, "mediatek,vcp", 0);
+	if (!vcp_node) {
+		dev_err(dev, "can't get VCP node\n");
+		return NULL;
+	}
+
+	vcp_pdev = of_find_device_by_node(vcp_node);
+	of_node_put(vcp_node);
+
+	if (WARN_ON(!vcp_pdev)) {
+		dev_err(dev, "VCP pdev failed\n");
+		return NULL;
+	}
+
+	return platform_get_drvdata(vcp_pdev);
+}
+EXPORT_SYMBOL_GPL(vcp_get);
+
+/**
+ * vcp_put() - "free" the VCP
+ *
+ * @vcp: mtk_vcp_device structure from vcp_get().
+ *
+ **/
+void vcp_put(struct mtk_vcp_device *vcp)
+{
+	put_device(vcp->dev);
+}
+EXPORT_SYMBOL_GPL(vcp_put);
+
+static int mtk_vcp_start(struct rproc *rproc)
+{
+	struct mtk_vcp_device *vcp = (struct mtk_vcp_device *)rproc->priv;
+	struct arm_smccc_res res;
+
+	/* core 0 */
+	arm_smccc_smc(MTK_SIP_TINYSYS_VCP_CONTROL,
+		      MTK_TINYSYS_VCP_KERNEL_OP_RESET_SET,
+		      1, 0, 0, 0, 0, 0, &res);
+
+	/* core 1 */
+	arm_smccc_smc(MTK_SIP_TINYSYS_VCP_CONTROL,
+		      MTK_TINYSYS_MMUP_KERNEL_OP_RESET_SET,
+		      1, 0, 0, 0, 0, 0, &res);
+
+	if (vcp_A_register_feature(vcp, RTOS_FEATURE_ID) < 0) {
+		dev_err(vcp->dev, "bootup fail\n");
+		vcp_A_deregister_feature(vcp, RTOS_FEATURE_ID);
+	} else {
+		dev_info(vcp->dev, "bootup successfully\n");
+	}
+
+	return 0;
+}
+
+static int mtk_vcp_stop(struct rproc *rproc)
+{
+	struct mtk_vcp_device *vcp = (struct mtk_vcp_device *)rproc->priv;
+
+	vcp_A_deregister_feature(vcp, RTOS_FEATURE_ID);
+
+	return 0;
+}
+
+static const struct rproc_ops mtk_vcp_ops = {
+	.load		= mtk_vcp_load,
+	.start		= mtk_vcp_start,
+	.stop		= mtk_vcp_stop,
+};
+
+static int vcp_multi_core_init(struct platform_device *pdev,
+			       struct mtk_vcp_of_cluster *vcp_cluster,
+			       enum vcp_core_id core_id)
+{
+	int ret;
+
+	ret = of_property_read_u32(pdev->dev.of_node, "mtk,vcp-core-twohart",
+				   &vcp_cluster->twohart[core_id]);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to twohart property\n");
+		return ret;
+	}
+	ret = of_property_read_u32(pdev->dev.of_node, "mtk,core-sram-offset",
+				   &vcp_cluster->sram_offset[core_id]);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to sram-offset property\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static struct mtk_vcp_device *vcp_rproc_init(struct platform_device *pdev,
+					     struct mtk_vcp_of_cluster *vcp_cluster)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev_of_node(dev);
+	struct device_node *child;
+	struct platform_device *cpdev;
+	struct mtk_vcp_device *vcp;
+	struct rproc *rproc;
+	const struct mtk_vcp_of_data *vcp_of_data;
+	u32 core_id;
+	int ret;
+
+	vcp_of_data = of_device_get_match_data(dev);
+	rproc = devm_rproc_alloc(dev, np->name, &mtk_vcp_ops,
+				 vcp_of_data->platdata.fw_name,
+				 sizeof(struct mtk_vcp_device));
+	if (!rproc) {
+		dev_err(dev, "unable to allocate remoteproc\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	vcp  = rproc->priv;
+	vcp->rproc = rproc;
+	vcp->pdev = pdev;
+	vcp->dev = dev;
+	vcp->ops = &vcp_of_data->ops;
+	vcp->platdata = &vcp_of_data->platdata;
+	vcp->vcp_cluster = vcp_cluster;
+
+	rproc->auto_boot = vcp_of_data->platdata.auto_boot;
+	rproc->sysfs_read_only = vcp_of_data->platdata.sysfs_read_only;
+	mutex_init(&vcp->vcp_cluster->vcp_feature_mutex);
+	mutex_init(&vcp->vcp_cluster->vcp_pw_clk_mutex);
+	platform_set_drvdata(pdev, vcp);
+
+	ret = vcp_reserve_memory_ioremap(vcp);
+	if (ret) {
+		dev_err(dev, "vcp_reserve_memory_ioremap failed ret = %d\n", ret);
+		return ERR_PTR(ret);
+	}
+
+	core_id = 0;
+	for_each_available_child_of_node(np, child) {
+		if (of_device_is_compatible(child, "mediatek,vcp-core")) {
+			cpdev = of_find_device_by_node(child);
+			if (!cpdev) {
+				ret = -ENODEV;
+				dev_err(dev, "Not found platform device for core\n");
+				return ERR_PTR(ret);
+			}
+			ret = vcp_multi_core_init(cpdev, vcp_cluster, core_id);
+			core_id++;
+		}
+	}
+	vcp->vcp_cluster->core_nums = core_id;
+
+	ret = vcp_wdt_irq_init(vcp);
+	if (ret) {
+		dev_err(dev, "vcp_wdt_irq_init failed\n");
+		return ERR_PTR(ret);
+	}
+
+	pm_runtime_get_sync(dev);
+
+	return vcp;
+}
+
+static int vcp_cluster_init(struct platform_device *pdev,
+			    struct mtk_vcp_of_cluster *vcp_cluster)
+{
+	struct mtk_vcp_device *vcp;
+	int ret;
+
+	vcp = vcp_rproc_init(pdev, vcp_cluster);
+	if (IS_ERR(vcp))
+		return PTR_ERR(vcp);
+
+	ret = rproc_add(vcp->rproc);
+	if (ret) {
+		dev_err(vcp->dev, "Failed to add rproc\n");
+		rproc_del(vcp->rproc);
+	}
+
+	return ret;
+}
+
+static int vcp_device_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct device *dev = &pdev->dev;
+	struct mtk_vcp_of_cluster *vcp_cluster;
+	int ret;
+
+	pm_runtime_enable(dev);
+
+	vcp_cluster = devm_kzalloc(dev, sizeof(*vcp_cluster), GFP_KERNEL);
+	if (!vcp_cluster)
+		return -ENOMEM;
+
+	vcp_cluster->cfg = devm_platform_ioremap_resource_byname(pdev, "cfg");
+	if (IS_ERR(vcp_cluster->cfg))
+		return dev_err_probe(dev, PTR_ERR(vcp_cluster->cfg),
+				     "Failed to parse and map cfg memory\n");
+
+	vcp_cluster->cfg_sec = devm_platform_ioremap_resource_byname(pdev, "cfg_sec");
+	if (IS_ERR(vcp_cluster->cfg_sec))
+		return dev_err_probe(dev, PTR_ERR(vcp_cluster->cfg_sec),
+				     "Failed to parse and map cfg_sec memory\n");
+
+	vcp_cluster->cfg_core = devm_platform_ioremap_resource_byname(pdev, "cfg_core");
+	if (IS_ERR(vcp_cluster->cfg_core))
+		return dev_err_probe(dev, PTR_ERR(vcp_cluster->cfg_core),
+				     "Failed to parse and map cfg_core memory\n");
+
+	vcp_cluster->vcp_rdy = devm_platform_ioremap_resource_byname(pdev, "vcp_vlp_ao_rsvd7");
+	if (IS_ERR(vcp_cluster->vcp_rdy))
+		return dev_err_probe(dev, PTR_ERR(vcp_cluster->vcp_rdy),
+				     "Failed to parse and map vcp_rdy memory\n");
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram");
+	vcp_cluster->sram_base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(vcp_cluster->sram_base))
+		return dev_err_probe(dev, PTR_ERR(vcp_cluster->sram_base),
+				     "Failed to parse and map sram memory\n");
+	vcp_cluster->sram_size = (u32)resource_size(res);
+
+	ret = devm_of_platform_populate(dev);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to populate platform devices\n");
+
+	ret = vcp_cluster_init(pdev, vcp_cluster);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static void vcp_device_remove(struct platform_device *pdev)
+{
+	struct mtk_vcp_device *vcp = platform_get_drvdata(pdev);
+
+	pm_runtime_disable(&pdev->dev);
+
+	rproc_del(vcp->rproc);
+}
+
+static void vcp_device_shutdown(struct platform_device *pdev)
+{
+	struct mtk_vcp_device *vcp = platform_get_drvdata(pdev);
+	u32 ret;
+
+	writel(GIPC_VCP_HART0_SHUT, vcp->vcp_cluster->cfg_core + R_GIPC_IN_SET);
+	if (vcp->vcp_cluster->core_nums > VCP_ID) {
+		ret = wait_core_hart_shutdown(vcp, VCP_ID);
+		if (!ret)
+			dev_err(&pdev->dev,
+				"wait VCP_ID core hart shutdown timeout\n");
+		else
+			writel(GIPC_MMUP_SHUT, vcp->vcp_cluster->cfg_core + R_GIPC_IN_SET);
+	}
+}
+
+static const struct mtk_vcp_of_data mt8196_of_data = {
+	.ops = {
+		.vcp_register_feature = vcp_A_register_feature,
+		.vcp_deregister_feature = vcp_A_deregister_feature,
+		.vcp_get_mem_phys = vcp_get_reserve_mem_phys,
+		.vcp_get_mem_iova = vcp_get_reserve_mem_iova,
+		.vcp_get_mem_virt = vcp_get_reserve_mem_virt,
+		.vcp_get_mem_size = vcp_get_reserve_mem_size,
+		.vcp_get_sram_virt = vcp_get_internal_sram_virt,
+	},
+	.platdata = {
+		.auto_boot = true,
+		.sysfs_read_only = true,
+		.rtos_static_iova = 0x180600000,
+		.fw_name = "mediatek/mt8196/vcp.img",
+	},
+};
+
+static const struct of_device_id mtk_vcp_of_match[] = {
+	{ .compatible = "mediatek,mt8196-vcp", .data = &mt8196_of_data},
+	{}
+};
+MODULE_DEVICE_TABLE(of, mtk_vcp_of_match);
+
+static struct platform_driver mtk_vcp_device = {
+	.probe = vcp_device_probe,
+	.remove_new = vcp_device_remove,
+	.shutdown = vcp_device_shutdown,
+	.driver = {
+		.name = "mtk-vcp",
+		.of_match_table = mtk_vcp_of_match,
+	},
+};
+
+module_platform_driver(mtk_vcp_device);
+
+MODULE_AUTHOR("Xiangzhi Tang <xiangzhi.tang@mediatek.com>");
+MODULE_DESCRIPTION("MTK VCP Controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/remoteproc/mtk_vcp_rproc.h b/drivers/remoteproc/mtk_vcp_rproc.h
new file mode 100644
index 000000000000..6c7a99bf919b
--- /dev/null
+++ b/drivers/remoteproc/mtk_vcp_rproc.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Copyright (c) 2025 MediaTek Inc.
+ */
+
+#ifndef __MTK_VCP_RPROC_H__
+#define __MTK_VCP_RPROC_H__
+
+#include <linux/remoteproc/mtk_vcp_public.h>
+
+/*
+ * struct mtk_vcp_of_cluster - vcp cluster priv data.
+ *
+ * @sram_base: sram_base get from dtb
+ * @cfg: cfg register get from dtb
+ * @cfg_sec: cfg_sec register get from dtb
+ * @cfg_core: cfg_core register get from dtb
+ * @vcp_rdy: vlp vcp_rdy register get from dtb
+ * @sram_size: total sram size get from dtb
+ * @core_nums: total core numbers get from dtb
+ * @twohart: core weo hart support flag
+ * @sram_offset: core sram memory layout
+ * @pwclkcnt: power and clock config count data
+ * @share_mem_iova: shared memory iova base
+ * @share_mem_size: shared memory size
+ * @vcp_feature_mutex: vcp feature register mutex structure
+ * @vcp_pw_clk_mutex: vcp feature lock pw_clk mutex structure
+ */
+struct mtk_vcp_of_cluster {
+	void __iomem *sram_base;
+	void __iomem *cfg;
+	void __iomem *cfg_sec;
+	void __iomem *cfg_core;
+	void __iomem *vcp_rdy;
+	u32 sram_size;
+	u32 core_nums;
+	u32 twohart[VCP_CORE_TOTAL];
+	u32 sram_offset[VCP_CORE_TOTAL];
+	int pwclkcnt;
+	dma_addr_t share_mem_iova;
+	size_t share_mem_size;
+	struct mutex vcp_feature_mutex;
+	struct mutex vcp_pw_clk_mutex;
+};
+
+/**
+ * struct mtk_vcp_platdata - vcp platform priv data.
+ *
+ * @auto_boot: rproc auto_boot flag
+ * @sysfs_read_only: rproc sysfs_read_only flag
+ * @rtos_static_iova: vcp dram binary static map iova
+ * @fw_name: vcp image name and path
+ */
+struct mtk_vcp_platdata {
+	bool auto_boot;
+	bool sysfs_read_only;
+	dma_addr_t rtos_static_iova;
+	char *fw_name;
+};
+
+/**
+ * struct mtk_vcp_of_data - const vcp device data.
+ *
+ * @mtk_vcp_ops: mtk_vcp_ops structure
+ * @mtk_vcp_platdata: mtk_vcp_platdata structure
+ */
+struct mtk_vcp_of_data {
+	const struct mtk_vcp_ops ops;
+	const struct mtk_vcp_platdata platdata;
+};
+#endif
diff --git a/include/linux/remoteproc/mtk_vcp_public.h b/include/linux/remoteproc/mtk_vcp_public.h
new file mode 100644
index 000000000000..b4de5e5d63d8
--- /dev/null
+++ b/include/linux/remoteproc/mtk_vcp_public.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Copyright (c) 2025 MediaTek Inc.
+ */
+
+#ifndef __MTK_VCP_PUBLIC_H__
+#define __MTK_VCP_PUBLIC_H__
+
+#include <linux/platform_device.h>
+#include <linux/remoteproc.h>
+
+/* vcp reserve memory ID definition */
+enum vcp_reserve_mem_id_t {
+	VCP_RTOS_MEM_ID,
+	VDEC_MEM_ID,
+	VENC_MEM_ID,
+	MMDVFS_VCP_MEM_ID,
+	MMDVFS_MMUP_MEM_ID,
+	MMQOS_MEM_ID,
+	NUMS_MEM_ID,
+};
+
+/* vcp feature ID list */
+enum feature_id {
+	RTOS_FEATURE_ID,
+	VDEC_FEATURE_ID,
+	VENC_FEATURE_ID,
+	GCE_FEATURE_ID,
+	MMDVFS_MMUP_FEATURE_ID,
+	MMDVFS_VCP_FEATURE_ID,
+	MMQOS_FEATURE_ID,
+	MMDEBUG_FEATURE_ID,
+	HWCCF_FEATURE_ID,
+	HWCCF_DEBUG_FEATURE_ID,
+	IMGSYS_FEATURE_ID,
+	VDISP_FEATURE_ID,
+	VMM_FEATURE_ID,
+	NUM_FEATURE_ID,
+};
+
+struct mtk_vcp_device {
+	struct platform_device *pdev;
+	struct device *dev;
+	struct rproc *rproc;
+	struct mtk_vcp_of_cluster *vcp_cluster;
+	const struct mtk_vcp_ops *ops;
+	const struct mtk_vcp_platdata *platdata;
+};
+
+struct mtk_vcp_ops {
+	int (*vcp_register_feature)(struct mtk_vcp_device *vcp, enum feature_id id);
+	int (*vcp_deregister_feature)(struct mtk_vcp_device *vcp, enum feature_id id);
+	phys_addr_t (*vcp_get_mem_phys)(enum vcp_reserve_mem_id_t id);
+	dma_addr_t (*vcp_get_mem_iova)(enum vcp_reserve_mem_id_t id);
+	void __iomem *(*vcp_get_mem_virt)(enum vcp_reserve_mem_id_t id);
+	uint32_t (*vcp_get_mem_size)(enum vcp_reserve_mem_id_t id);
+	void __iomem *(*vcp_get_sram_virt)(struct mtk_vcp_device *vcp);
+};
+
+struct mtk_vcp_device *vcp_get(struct platform_device *pdev);
+void vcp_put(struct mtk_vcp_device *vcp);
+
+/*
+ * These inline functions are intended for user drivers that are loaded
+ * earlier than the VCP driver, or for built-in drivers that cannot access
+ * the symbols of VCP module.
+ */
+static inline struct mtk_vcp_device *mtk_vcp_get_by_phandle(phandle phandle)
+{
+	struct rproc *rproc = NULL;
+
+	rproc = rproc_get_by_phandle(phandle);
+	if (IS_ERR_OR_NULL(rproc))
+		return NULL;
+
+	return rproc->priv;
+}
+#endif
diff --git a/include/linux/soc/mediatek/mtk_sip_svc.h b/include/linux/soc/mediatek/mtk_sip_svc.h
index abe24a73ee19..d65a59752979 100644
--- a/include/linux/soc/mediatek/mtk_sip_svc.h
+++ b/include/linux/soc/mediatek/mtk_sip_svc.h
@@ -28,4 +28,7 @@
 /* IOMMU related SMC call */
 #define MTK_SIP_KERNEL_IOMMU_CONTROL	MTK_SIP_SMC_CMD(0x514)
 
+/* VCP */
+#define MTK_SIP_TINYSYS_VCP_CONTROL	MTK_SIP_SMC_CMD(0x52C)
+
 #endif
-- 
2.46.0



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v2 3/4] remoterpoc: mediatek: vcp: Add ipi-mbox communication
  2025-09-14 12:29 [PATCH v2 0/4] ASoC: mediatek: Add support of VCP on Mediatek mt8196 SoC Xiangzhi Tang
  2025-09-14 12:29 ` [PATCH v2 1/4] dt-bindings: remoteproc: Add VCP support for mt8196 Xiangzhi Tang
  2025-09-14 12:29 ` [PATCH v2 2/4] remoterpoc: mediatek: vcp: Add vcp remoteproc driver Xiangzhi Tang
@ 2025-09-14 12:29 ` Xiangzhi Tang
  2025-09-14 13:30   ` Krzysztof Kozlowski
  2025-09-14 12:29 ` [PATCH v2 4/4] remoterpoc: mediatek: vcp: Add vcp suspned and resume feature Xiangzhi Tang
  2025-09-14 13:16 ` [PATCH v2 0/4] ASoC: mediatek: Add support of VCP on Mediatek mt8196 SoC Krzysztof Kozlowski
  4 siblings, 1 reply; 12+ messages in thread
From: Xiangzhi Tang @ 2025-09-14 12:29 UTC (permalink / raw)
  To: Bjorn Andersson, Mathieu Poirier, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
	AngeloGioacchino Del Regno, Xiangzhi Tang
  Cc: linux-remoteproc, devicetree, linux-kernel, linux-arm-kernel,
	linux-mediatek, Jjian Zhou, Hailong Fan, Xiangzhi Tang

Add VCP ipi-mbox communication mechanism between AP CPU with VCP mcu

1.Add VCP ipi-mbox init driver.
2.Add vcp ready ipi register driver.
3.Add vcp ready notify work mechanism.

Signed-off-by: Xiangzhi Tang <xiangzhi.tang@mediatek.com>
---
 drivers/remoteproc/Kconfig                |   2 +
 drivers/remoteproc/mtk_vcp_common.c       | 163 ++++++++++++++++++++++
 drivers/remoteproc/mtk_vcp_common.h       |  30 ++++
 drivers/remoteproc/mtk_vcp_rproc.c        | 154 +++++++++++++++++++-
 drivers/remoteproc/mtk_vcp_rproc.h        |  18 +++
 include/linux/remoteproc/mtk_vcp_public.h |  62 ++++++++
 6 files changed, 428 insertions(+), 1 deletion(-)

diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index 1a9bb49a8a28..90fdc632ff2f 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -68,6 +68,8 @@ config MTK_VCP_RPROC
 	tristate "MediaTek VCP support"
 	depends on ARCH_MEDIATEK || COMPILE_TEST
 	depends on ARCH_DMA_ADDR_T_64BIT
+	select MTK_VCP_IPC
+	select MTK_VCP_MBOX
 	help
 	  Say y here to support MediaTek's Video Companion Processor (VCP) via
 	  the remote processor framework.
diff --git a/drivers/remoteproc/mtk_vcp_common.c b/drivers/remoteproc/mtk_vcp_common.c
index d53e311c4981..9767f5ff15a0 100644
--- a/drivers/remoteproc/mtk_vcp_common.c
+++ b/drivers/remoteproc/mtk_vcp_common.c
@@ -11,6 +11,7 @@
 #include <linux/fs.h>
 #include <linux/interrupt.h>
 #include <linux/iommu.h>
+#include <linux/iopoll.h>
 #include <linux/kernel.h>
 #include <linux/mutex.h>
 #include <linux/of.h>
@@ -23,6 +24,9 @@
 #include "mtk_vcp_common.h"
 #include "mtk_vcp_rproc.h"
 
+static BLOCKING_NOTIFIER_HEAD(mmup_notifier_list);
+static BLOCKING_NOTIFIER_HEAD(vcp_notifier_list);
+
 struct vcp_feature_tb feature_table[NUM_FEATURE_ID] = {
 	{
 		.feature    = RTOS_FEATURE_ID,
@@ -302,6 +306,23 @@ static bool is_vcp_ready_by_coreid(enum vcp_core_id core_id)
 	}
 }
 
+static enum vcp_core_id get_core_by_feature(enum feature_id id)
+{
+	for (u32 i = 0; i < NUM_FEATURE_ID; i++) {
+		if (feature_table[i].feature == id)
+			return feature_table[i].core_id;
+	}
+
+	return 0;
+}
+
+bool is_vcp_ready(enum feature_id id)
+{
+	enum vcp_core_id core_id = get_core_by_feature(id);
+
+	return is_vcp_ready_by_coreid(core_id);
+}
+
 u32 wait_core_hart_shutdown(struct mtk_vcp_device *vcp,
 			    enum vcp_core_id core_id)
 {
@@ -343,9 +364,114 @@ u32 wait_core_hart_shutdown(struct mtk_vcp_device *vcp,
 	return retry;
 }
 
+void vcp_A_register_notify(enum feature_id id,
+			   struct notifier_block *nb)
+{
+	enum vcp_core_id core_id = get_core_by_feature(id);
+
+	switch (core_id) {
+	case VCP_ID:
+		blocking_notifier_chain_register(&vcp_notifier_list, nb);
+		if (is_vcp_ready_by_coreid(VCP_ID))
+			nb->notifier_call(nb, VCP_EVENT_READY, NULL);
+		break;
+	case MMUP_ID:
+		blocking_notifier_chain_register(&mmup_notifier_list, nb);
+		if (is_vcp_ready_by_coreid(MMUP_ID))
+			nb->notifier_call(nb, VCP_EVENT_READY, NULL);
+		break;
+	default:
+		break;
+	}
+}
+
+void vcp_A_unregister_notify(enum feature_id id,
+			     struct notifier_block *nb)
+{
+	enum vcp_core_id core_id = get_core_by_feature(id);
+
+	switch (core_id) {
+	case VCP_ID:
+		blocking_notifier_chain_unregister(&vcp_notifier_list, nb);
+		break;
+	case MMUP_ID:
+		blocking_notifier_chain_unregister(&mmup_notifier_list, nb);
+		break;
+	default:
+		break;
+	}
+}
+
+void vcp_extern_notify(enum vcp_core_id core_id,
+		       enum VCP_NOTIFY_EVENT notify_status)
+{
+	switch (core_id) {
+	case VCP_ID:
+		blocking_notifier_call_chain(&vcp_notifier_list, notify_status, NULL);
+		break;
+	case MMUP_ID:
+		blocking_notifier_call_chain(&mmup_notifier_list, notify_status, NULL);
+		break;
+	default:
+		break;
+	}
+}
+
+static void vcp_A_notify_ws(struct work_struct *ws)
+{
+	struct vcp_work_struct *sws =
+		container_of(ws, struct vcp_work_struct, work);
+	struct mtk_vcp_device *vcp = platform_get_drvdata(to_platform_device(sws->dev));
+	enum vcp_core_id core_id = sws->flags;
+
+	if (core_id < VCP_CORE_TOTAL) {
+		mutex_lock(&vcp->vcp_cluster->vcp_ready_mutex);
+		vcp->vcp_cluster->vcp_ready[core_id] = true;
+		mutex_unlock(&vcp->vcp_cluster->vcp_ready_mutex);
+
+		vcp_extern_notify(core_id, VCP_EVENT_READY);
+
+		/*clear reset status and unlock wake lock*/
+		dev_info(sws->dev, "%s core id %u ready\n", __func__, core_id);
+	} else {
+		dev_warn(sws->dev, "%s wrong core id %u\n", __func__, core_id);
+	}
+}
+
+static void vcp_A_set_ready(struct mtk_vcp_device *vcp,
+			    enum vcp_core_id core_id)
+{
+	if (core_id < VCP_CORE_TOTAL) {
+		vcp->vcp_cluster->vcp_ready_notify_wk[core_id].flags = core_id;
+		queue_work(vcp->vcp_cluster->vcp_workqueue,
+			   &vcp->vcp_cluster->vcp_ready_notify_wk[core_id].work);
+	}
+}
+
+int vcp_A_ready_ipi_handler(u32 id, void *prdata, void *data, u32 len)
+{
+	struct mtk_vcp_device *vcp = (struct mtk_vcp_device *)prdata;
+
+	switch (id) {
+	case IPI_IN_VCP_READY_0:
+		if (!is_vcp_ready_by_coreid(VCP_ID))
+			vcp_A_set_ready(vcp, VCP_ID);
+		break;
+	case IPI_IN_VCP_READY_1:
+		if (!is_vcp_ready_by_coreid(MMUP_ID))
+			vcp_A_set_ready(vcp, MMUP_ID);
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
 static int reset_vcp(struct mtk_vcp_device *vcp)
 {
 	struct arm_smccc_res res;
+	bool mmup_status, vcp_status;
 
 	if (vcp->vcp_cluster->core_nums >= MMUP_ID) {
 		/* write vcp reserved memory address/size to GRP1/GRP2
@@ -359,6 +485,16 @@ static int reset_vcp(struct mtk_vcp_device *vcp)
 		arm_smccc_smc(MTK_SIP_TINYSYS_VCP_CONTROL,
 			      MTK_TINYSYS_MMUP_KERNEL_OP_RESET_RELEASE,
 			      1, 0, 0, 0, 0, 0, &res);
+
+		read_poll_timeout_atomic(is_vcp_ready_by_coreid,
+					 mmup_status, mmup_status,
+					 USEC_PER_MSEC,
+					 VCP_READY_TIMEOUT_MS * USEC_PER_MSEC,
+					 false, MMUP_ID);
+		if (!mmup_status) {
+			dev_err(vcp->dev, "MMUP_ID bootup timeout. Stop vcp booting\n");
+			return -EINVAL;
+		}
 	}
 
 	/* write vcp reserved memory address/size to GRP1/GRP2
@@ -372,6 +508,17 @@ static int reset_vcp(struct mtk_vcp_device *vcp)
 	arm_smccc_smc(MTK_SIP_TINYSYS_VCP_CONTROL,
 		      MTK_TINYSYS_VCP_KERNEL_OP_RESET_RELEASE,
 		      1, 0, 0, 0, 0, 0, &res);
+
+	read_poll_timeout_atomic(is_vcp_ready_by_coreid,
+				 vcp_status, vcp_status,
+				 USEC_PER_MSEC,
+				 VCP_READY_TIMEOUT_MS * USEC_PER_MSEC,
+				 false, VCP_ID);
+	if (!vcp_status) {
+		dev_err(vcp->dev, "VCP_ID bootup timeout. Stop vcp booting\n");
+		return -EINVAL;
+	}
+
 	return 0;
 }
 
@@ -458,6 +605,22 @@ int vcp_A_deregister_feature(struct mtk_vcp_device *vcp, enum feature_id id)
 	return ret;
 }
 
+int vcp_notify_work_init(struct mtk_vcp_device *vcp)
+{
+	vcp->vcp_cluster->vcp_workqueue = create_singlethread_workqueue("VCP_WQ");
+	if (!vcp->vcp_cluster->vcp_workqueue) {
+		dev_err(vcp->dev, "vcp_workqueue create fail\n");
+		return -EINVAL;
+	}
+
+	for (u32 core_id = 0; core_id < VCP_CORE_TOTAL; core_id++) {
+		vcp->vcp_cluster->vcp_ready_notify_wk[core_id].dev = vcp->dev;
+		INIT_WORK(&vcp->vcp_cluster->vcp_ready_notify_wk[core_id].work, vcp_A_notify_ws);
+	}
+
+	return 0;
+}
+
 static size_t load_part_binary(void __iomem *image_buf,
 			       const u8 *fw_src,
 			       size_t size,
diff --git a/drivers/remoteproc/mtk_vcp_common.h b/drivers/remoteproc/mtk_vcp_common.h
index 8340f0bd4fdc..4a4393b2ae1f 100644
--- a/drivers/remoteproc/mtk_vcp_common.h
+++ b/drivers/remoteproc/mtk_vcp_common.h
@@ -14,6 +14,8 @@
 
 /* vcp timeout definition */
 #define VCP_AWAKE_TIMEOUT 1000
+#define VCP_READY_TIMEOUT_MS 3000
+#define VCP_IPI_DEV_READY_TIMEOUT 1000
 #define USDELAY_RANGE_MIN 1000
 #define USDELAY_RANGE_MAX 2000
 
@@ -153,6 +155,21 @@ struct vcp_reserve_mblock {
 	size_t size;
 };
 
+/**
+ * struct vcp_work_struct - vcp notify work structure.
+ *
+ * @work: struct work_struct member
+ * @dev: struct device member
+ * @u32 flags: vcp notify work flag
+ * @id: vcp core id
+ */
+struct vcp_work_struct {
+	struct work_struct work;
+	struct device *dev;
+	u32 flags;
+	u32 id;
+};
+
 /**
  * struct vcp_region_info_st - config vcp image info sync to vcp bootloader.
  *
@@ -200,6 +217,19 @@ struct vcp_region_info_st {
 	u32 coredump_dram_offset;
 };
 
+/* vcp common ready signale APIs */
+int vcp_A_ready_ipi_handler(u32 id, void *prdata,
+			    void *data, u32 len);
+bool is_vcp_ready(enum feature_id id);
+
+/* vcp common notify extern APIs */
+int vcp_notify_work_init(struct mtk_vcp_device *vcp);
+void vcp_extern_notify(enum vcp_core_id core_id,
+		       enum VCP_NOTIFY_EVENT notify_status);
+void vcp_A_register_notify(enum feature_id id,
+			   struct notifier_block *nb);
+void vcp_A_unregister_notify(enum feature_id id,
+			     struct notifier_block *nb);
 /* vcp common reserved memory APIs */
 int vcp_reserve_memory_ioremap(struct mtk_vcp_device *vcp);
 phys_addr_t vcp_get_reserve_mem_phys(enum vcp_reserve_mem_id_t id);
diff --git a/drivers/remoteproc/mtk_vcp_rproc.c b/drivers/remoteproc/mtk_vcp_rproc.c
index bf4736ce6795..4aa0ed47abd7 100644
--- a/drivers/remoteproc/mtk_vcp_rproc.c
+++ b/drivers/remoteproc/mtk_vcp_rproc.c
@@ -5,6 +5,7 @@
 
 #include <linux/device.h>
 #include <linux/module.h>
+#include <linux/iopoll.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_platform.h>
@@ -60,10 +61,39 @@ void vcp_put(struct mtk_vcp_device *vcp)
 }
 EXPORT_SYMBOL_GPL(vcp_put);
 
+/**
+ * vcp_get_ipidev() - get a vcp ipi device struct to reference vcp ipi.
+ *
+ * @vcp: mtk_vcp_device structure from vcp_get().
+ *
+ **/
+struct mtk_ipi_device *vcp_get_ipidev(struct mtk_vcp_device *vcp)
+{
+	return vcp->ipi_dev;
+}
+EXPORT_SYMBOL_GPL(vcp_get_ipidev);
+
 static int mtk_vcp_start(struct rproc *rproc)
 {
 	struct mtk_vcp_device *vcp = (struct mtk_vcp_device *)rproc->priv;
 	struct arm_smccc_res res;
+	int ret;
+
+	ret = vcp->ipi_ops->ipi_register(vcp->ipi_dev, IPI_IN_VCP_READY_0,
+					 (void *)vcp_A_ready_ipi_handler,
+					 vcp, &vcp->vcp_cluster->msg_vcp_ready0);
+	if (ret) {
+		dev_err(vcp->dev, "Failed to register IPI_IN_VCP_READY_0\n");
+		goto vcp0_ready_ipi_unregister;
+	}
+
+	ret = vcp->ipi_ops->ipi_register(vcp->ipi_dev, IPI_IN_VCP_READY_1,
+					 (void *)vcp_A_ready_ipi_handler,
+					 vcp, &vcp->vcp_cluster->msg_vcp_ready1);
+	if (ret) {
+		dev_err(vcp->dev, "Failed to register IPI_IN_VCP_READY_1\n");
+		goto vcp1_ready_ipi_unregister;
+	}
 
 	/* core 0 */
 	arm_smccc_smc(MTK_SIP_TINYSYS_VCP_CONTROL,
@@ -82,7 +112,14 @@ static int mtk_vcp_start(struct rproc *rproc)
 		dev_info(vcp->dev, "bootup successfully\n");
 	}
 
-	return 0;
+	return ret;
+
+vcp1_ready_ipi_unregister:
+	vcp->ipi_ops->ipi_unregister(vcp->ipi_dev, IPI_IN_VCP_READY_1);
+vcp0_ready_ipi_unregister:
+	vcp->ipi_ops->ipi_unregister(vcp->ipi_dev, IPI_IN_VCP_READY_0);
+
+	return ret;
 }
 
 static int mtk_vcp_stop(struct rproc *rproc)
@@ -91,6 +128,9 @@ static int mtk_vcp_stop(struct rproc *rproc)
 
 	vcp_A_deregister_feature(vcp, RTOS_FEATURE_ID);
 
+	vcp_extern_notify(VCP_ID, VCP_EVENT_STOP);
+	vcp_extern_notify(MMUP_ID, VCP_EVENT_STOP);
+
 	return 0;
 }
 
@@ -100,6 +140,43 @@ static const struct rproc_ops mtk_vcp_ops = {
 	.stop		= mtk_vcp_stop,
 };
 
+static int vcp_ipi_mbox_init(struct mtk_vcp_device *vcp)
+{
+	struct mtk_vcp_ipc *vcp_ipc;
+	struct platform_device *pdev;
+	int ret;
+
+	pdev = platform_device_register_data(vcp->dev, "mtk-vcp-ipc",
+					     PLATFORM_DEVID_NONE,
+					     vcp->platdata->ipc_data,
+					     sizeof(struct mtk_mbox_table));
+	if (IS_ERR(pdev)) {
+		ret = PTR_ERR(pdev);
+		dev_err(vcp->dev, "failed to create mtk-vcp-ipc device\n");
+		return ret;
+	}
+
+	ret = read_poll_timeout_atomic(dev_get_drvdata,
+				       vcp_ipc, vcp_ipc,
+				       USEC_PER_MSEC,
+				       VCP_IPI_DEV_READY_TIMEOUT * USEC_PER_MSEC,
+				       false,
+				       &pdev->dev);
+	if (ret) {
+		ret = -EPROBE_DEFER;
+		dev_err(vcp->dev, "failed to get drvdata\n");
+		return ret;
+	}
+
+	ret = mtk_vcp_ipc_device_register(vcp->ipi_dev, VCP_IPI_COUNT, vcp_ipc);
+	if (ret) {
+		dev_err(vcp->dev, "ipi_dev_register failed, ret %d\n", ret);
+		return ret;
+	}
+
+	return ret;
+}
+
 static int vcp_multi_core_init(struct platform_device *pdev,
 			       struct mtk_vcp_of_cluster *vcp_cluster,
 			       enum vcp_core_id core_id)
@@ -150,12 +227,15 @@ static struct mtk_vcp_device *vcp_rproc_init(struct platform_device *pdev,
 	vcp->dev = dev;
 	vcp->ops = &vcp_of_data->ops;
 	vcp->platdata = &vcp_of_data->platdata;
+	vcp->ipi_ops = vcp_of_data->platdata.ipi_ops;
 	vcp->vcp_cluster = vcp_cluster;
+	vcp->ipi_dev = &vcp_cluster->vcp_ipidev;
 
 	rproc->auto_boot = vcp_of_data->platdata.auto_boot;
 	rproc->sysfs_read_only = vcp_of_data->platdata.sysfs_read_only;
 	mutex_init(&vcp->vcp_cluster->vcp_feature_mutex);
 	mutex_init(&vcp->vcp_cluster->vcp_pw_clk_mutex);
+	mutex_init(&vcp->vcp_cluster->vcp_ready_mutex);
 	platform_set_drvdata(pdev, vcp);
 
 	ret = vcp_reserve_memory_ioremap(vcp);
@@ -185,6 +265,16 @@ static struct mtk_vcp_device *vcp_rproc_init(struct platform_device *pdev,
 		return ERR_PTR(ret);
 	}
 
+	ret = vcp_ipi_mbox_init(vcp);
+	if (ret) {
+		dev_err(dev, "Failed to init vcp ipi-mbox\n");
+		return ERR_PTR(ret);
+	}
+
+	ret = vcp_notify_work_init(vcp);
+	if (ret)
+		dev_err(dev, "vcp_notify_work_init failed\n");
+
 	pm_runtime_get_sync(dev);
 
 	return vcp;
@@ -264,6 +354,8 @@ static void vcp_device_remove(struct platform_device *pdev)
 {
 	struct mtk_vcp_device *vcp = platform_get_drvdata(pdev);
 
+	flush_workqueue(vcp->vcp_cluster->vcp_workqueue);
+	destroy_workqueue(vcp->vcp_cluster->vcp_workqueue);
 	pm_runtime_disable(&pdev->dev);
 
 	rproc_del(vcp->rproc);
@@ -274,6 +366,12 @@ static void vcp_device_shutdown(struct platform_device *pdev)
 	struct mtk_vcp_device *vcp = platform_get_drvdata(pdev);
 	u32 ret;
 
+	vcp->vcp_cluster->vcp_ready[VCP_ID] = false;
+	vcp->vcp_cluster->vcp_ready[MMUP_ID] = false;
+
+	vcp_extern_notify(VCP_ID, VCP_EVENT_STOP);
+	vcp_extern_notify(MMUP_ID, VCP_EVENT_STOP);
+
 	writel(GIPC_VCP_HART0_SHUT, vcp->vcp_cluster->cfg_core + R_GIPC_IN_SET);
 	if (vcp->vcp_cluster->core_nums > VCP_ID) {
 		ret = wait_core_hart_shutdown(vcp, VCP_ID);
@@ -285,8 +383,60 @@ static void vcp_device_shutdown(struct platform_device *pdev)
 	}
 }
 
+static struct mtk_mbox_table ipc_table = {
+	.send_table = {
+		{ .msg_size = 18, .ipi_id =  0, .mbox_id = 0 },
+
+		{ .msg_size =  8, .ipi_id = 15, .mbox_id = 1 },
+		{ .msg_size = 18, .ipi_id = 16, .mbox_id = 1 },
+		{ .msg_size =  2, .ipi_id =  9, .mbox_id = 1 },
+
+		{ .msg_size = 18, .ipi_id = 11, .mbox_id = 2 },
+		{ .msg_size =  2, .ipi_id =  2, .mbox_id = 2 },
+		{ .msg_size =  3, .ipi_id =  3, .mbox_id = 2 },
+		{ .msg_size =  2, .ipi_id = 32, .mbox_id = 2 },
+
+		{ .msg_size =  2, .ipi_id = 33, .mbox_id = 3 },
+		{ .msg_size =  2, .ipi_id = 13, .mbox_id = 3 },
+		{ .msg_size =  2, .ipi_id = 35, .mbox_id = 3 },
+
+		{ .msg_size =  2, .ipi_id = 20, .mbox_id = 4 },
+		{ .msg_size =  3, .ipi_id = 21, .mbox_id = 4 },
+		{ .msg_size =  2, .ipi_id = 23, .mbox_id = 4 }
+	},
+	.recv_table = {
+		{ .recv_opt = 0, .msg_size = 18, .ipi_id =  1, .mbox_id = 0 },
+
+		{ .recv_opt = 1, .msg_size =  8, .ipi_id = 15, .mbox_id = 1 },
+		{ .recv_opt = 0, .msg_size = 18, .ipi_id = 17, .mbox_id = 1 },
+		{ .recv_opt = 0, .msg_size =  2, .ipi_id = 10, .mbox_id = 1 },
+
+		{ .recv_opt = 0, .msg_size = 18, .ipi_id = 12, .mbox_id = 2 },
+		{ .recv_opt = 0, .msg_size =  1, .ipi_id =  5, .mbox_id = 2 },
+		{ .recv_opt = 1, .msg_size =  1, .ipi_id =  2, .mbox_id = 2 },
+
+		{ .recv_opt = 0, .msg_size =  2, .ipi_id = 34, .mbox_id = 3 },
+		{ .recv_opt = 0, .msg_size =  2, .ipi_id = 14, .mbox_id = 3 },
+
+		{ .recv_opt = 0, .msg_size =  1, .ipi_id = 26, .mbox_id = 4 },
+		{ .recv_opt = 1, .msg_size =  1, .ipi_id = 20, .mbox_id = 4 }
+	},
+	.recv_count = 11,
+	.send_count = 14,
+};
+
+static struct mtk_vcp_ipi_ops mt8196_vcp_ipi_ops = {
+	.ipi_send = mtk_vcp_ipc_send,
+	.ipi_send_compl = mtk_vcp_ipc_send_compl,
+	.ipi_register = mtk_vcp_mbox_ipc_register,
+	.ipi_unregister = mtk_vcp_mbox_ipc_unregister,
+};
+
 static const struct mtk_vcp_of_data mt8196_of_data = {
 	.ops = {
+		.vcp_is_ready = is_vcp_ready,
+		.vcp_register_notify = vcp_A_register_notify,
+		.vcp_unregister_notify = vcp_A_unregister_notify,
 		.vcp_register_feature = vcp_A_register_feature,
 		.vcp_deregister_feature = vcp_A_deregister_feature,
 		.vcp_get_mem_phys = vcp_get_reserve_mem_phys,
@@ -299,6 +449,8 @@ static const struct mtk_vcp_of_data mt8196_of_data = {
 		.auto_boot = true,
 		.sysfs_read_only = true,
 		.rtos_static_iova = 0x180600000,
+		.ipc_data = &ipc_table,
+		.ipi_ops = &mt8196_vcp_ipi_ops,
 		.fw_name = "mediatek/mt8196/vcp.img",
 	},
 };
diff --git a/drivers/remoteproc/mtk_vcp_rproc.h b/drivers/remoteproc/mtk_vcp_rproc.h
index 6c7a99bf919b..e36612256b63 100644
--- a/drivers/remoteproc/mtk_vcp_rproc.h
+++ b/drivers/remoteproc/mtk_vcp_rproc.h
@@ -20,11 +20,18 @@
  * @core_nums: total core numbers get from dtb
  * @twohart: core weo hart support flag
  * @sram_offset: core sram memory layout
+ * @msg_vcp_ready0: core0 ready ipi msg data
+ * @msg_vcp_ready1: core1 ready ipi msg data
  * @pwclkcnt: power and clock config count data
+ * @vcp_ready: vcp core status flag
  * @share_mem_iova: shared memory iova base
  * @share_mem_size: shared memory size
  * @vcp_feature_mutex: vcp feature register mutex structure
  * @vcp_pw_clk_mutex: vcp feature lock pw_clk mutex structure
+ * @vcp_ready_mutex: vcp core ready mutex structure
+ * @vcp_ipidev: struct mtk_ipi_device
+ * @vcp_workqueue: ready workqueue_struct
+ * @vcp_ready_notify_wk: vcp_work_struct structure
  */
 struct mtk_vcp_of_cluster {
 	void __iomem *sram_base;
@@ -36,11 +43,18 @@ struct mtk_vcp_of_cluster {
 	u32 core_nums;
 	u32 twohart[VCP_CORE_TOTAL];
 	u32 sram_offset[VCP_CORE_TOTAL];
+	u32 msg_vcp_ready0;
+	u32 msg_vcp_ready1;
 	int pwclkcnt;
+	bool vcp_ready[VCP_CORE_TOTAL];
 	dma_addr_t share_mem_iova;
 	size_t share_mem_size;
 	struct mutex vcp_feature_mutex;
 	struct mutex vcp_pw_clk_mutex;
+	struct mutex vcp_ready_mutex;
+	struct mtk_ipi_device vcp_ipidev;
+	struct workqueue_struct *vcp_workqueue;
+	struct vcp_work_struct vcp_ready_notify_wk[VCP_CORE_TOTAL];
 };
 
 /**
@@ -49,12 +63,16 @@ struct mtk_vcp_of_cluster {
  * @auto_boot: rproc auto_boot flag
  * @sysfs_read_only: rproc sysfs_read_only flag
  * @rtos_static_iova: vcp dram binary static map iova
+ * @mtk_mbox_table: mtk_mbox_table structure
+ * @mtk_vcp_ipi_ops: vcp ipi api ops structure
  * @fw_name: vcp image name and path
  */
 struct mtk_vcp_platdata {
 	bool auto_boot;
 	bool sysfs_read_only;
 	dma_addr_t rtos_static_iova;
+	struct mtk_mbox_table *ipc_data;
+	struct mtk_vcp_ipi_ops *ipi_ops;
 	char *fw_name;
 };
 
diff --git a/include/linux/remoteproc/mtk_vcp_public.h b/include/linux/remoteproc/mtk_vcp_public.h
index b4de5e5d63d8..5bd562d1ae62 100644
--- a/include/linux/remoteproc/mtk_vcp_public.h
+++ b/include/linux/remoteproc/mtk_vcp_public.h
@@ -7,8 +7,19 @@
 #define __MTK_VCP_PUBLIC_H__
 
 #include <linux/platform_device.h>
+#include <linux/firmware/mediatek/mtk-vcp-ipc.h>
 #include <linux/remoteproc.h>
 
+#define VCP_SYNC_TIMEOUT_MS             (50)
+
+/* vcp notify event */
+enum VCP_NOTIFY_EVENT {
+	VCP_EVENT_READY = 0,
+	VCP_EVENT_STOP,
+	VCP_EVENT_SUSPEND,
+	VCP_EVENT_RESUME,
+};
+
 /* vcp reserve memory ID definition */
 enum vcp_reserve_mem_id_t {
 	VCP_RTOS_MEM_ID,
@@ -38,16 +49,66 @@ enum feature_id {
 	NUM_FEATURE_ID,
 };
 
+/* vcp ipi ID list */
+enum {
+	IPI_OUT_VDEC_1                 =  0,
+	IPI_IN_VDEC_1                  =  1,
+	IPI_OUT_C_SLEEP_0              =  2,
+	IPI_OUT_TEST_0                 =  3,
+	IPI_IN_VCP_READY_0             =  5,
+	IPI_OUT_MMDVFS_VCP             =  9,
+	IPI_IN_MMDVFS_VCP              = 10,
+	IPI_OUT_MMQOS                  = 11,
+	IPI_IN_MMQOS                   = 12,
+	IPI_OUT_MMDEBUG                = 13,
+	IPI_IN_MMDEBUG                 = 14,
+	IPI_OUT_C_VCP_HWVOTER_DEBUG    = 15,
+	IPI_OUT_VENC_0                 = 16,
+	IPI_IN_VENC_0                  = 17,
+	IPI_OUT_C_SLEEP_1              = 20,
+	IPI_OUT_TEST_1                 = 21,
+	IPI_OUT_LOGGER_CTRL_0          = 22,
+	IPI_OUT_VCPCTL_1               = 23,
+	IPI_IN_LOGGER_CTRL_0           = 25,
+	IPI_IN_VCP_READY_1             = 26,
+	IPI_OUT_LOGGER_CTRL_1          = 30,
+	IPI_IN_LOGGER_CTRL_1           = 31,
+	IPI_OUT_VCPCTL_0               = 32,
+	IPI_OUT_MMDVFS_MMUP            = 33,
+	IPI_IN_MMDVFS_MMUP             = 34,
+	IPI_OUT_VDISP                  = 35,
+	VCP_IPI_COUNT,
+	VCP_IPI_NS_SERVICE             = 0xff,
+	VCP_IPI_NS_SERVICE_COUNT       = 0x100,
+};
+
 struct mtk_vcp_device {
 	struct platform_device *pdev;
 	struct device *dev;
 	struct rproc *rproc;
+	struct mtk_ipi_device *ipi_dev;
+	struct rproc_subdev *rpmsg_subdev;
+	struct mtk_rpmsg_channel_info_mbox *mtk_rpchan;
 	struct mtk_vcp_of_cluster *vcp_cluster;
+	const struct mtk_vcp_ipi_ops *ipi_ops;
 	const struct mtk_vcp_ops *ops;
 	const struct mtk_vcp_platdata *platdata;
 };
 
+struct mtk_vcp_ipi_ops {
+	int (*ipi_send)(struct mtk_ipi_device *ipidev, u32 ipi_id,
+			void *data, u32 len);
+	int (*ipi_send_compl)(struct mtk_ipi_device *ipidev, u32 ipi_id,
+			      void *data, u32 len, u32 timeout_ms);
+	int (*ipi_register)(struct mtk_ipi_device *ipidev, int ipi_id,
+			    mbox_pin_cb_t cb, void *prdata, void *msg);
+	int (*ipi_unregister)(struct mtk_ipi_device *ipidev, int ipi_id);
+};
+
 struct mtk_vcp_ops {
+	bool (*vcp_is_ready)(enum feature_id id);
+	void (*vcp_register_notify)(enum feature_id id, struct notifier_block *nb);
+	void (*vcp_unregister_notify)(enum feature_id id, struct notifier_block *nb);
 	int (*vcp_register_feature)(struct mtk_vcp_device *vcp, enum feature_id id);
 	int (*vcp_deregister_feature)(struct mtk_vcp_device *vcp, enum feature_id id);
 	phys_addr_t (*vcp_get_mem_phys)(enum vcp_reserve_mem_id_t id);
@@ -59,6 +120,7 @@ struct mtk_vcp_ops {
 
 struct mtk_vcp_device *vcp_get(struct platform_device *pdev);
 void vcp_put(struct mtk_vcp_device *vcp);
+struct mtk_ipi_device *vcp_get_ipidev(struct mtk_vcp_device *vcp);
 
 /*
  * These inline functions are intended for user drivers that are loaded
-- 
2.46.0



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v2 4/4] remoterpoc: mediatek: vcp: Add vcp suspned and resume feature
  2025-09-14 12:29 [PATCH v2 0/4] ASoC: mediatek: Add support of VCP on Mediatek mt8196 SoC Xiangzhi Tang
                   ` (2 preceding siblings ...)
  2025-09-14 12:29 ` [PATCH v2 3/4] remoterpoc: mediatek: vcp: Add ipi-mbox communication Xiangzhi Tang
@ 2025-09-14 12:29 ` Xiangzhi Tang
  2025-09-14 13:16 ` [PATCH v2 0/4] ASoC: mediatek: Add support of VCP on Mediatek mt8196 SoC Krzysztof Kozlowski
  4 siblings, 0 replies; 12+ messages in thread
From: Xiangzhi Tang @ 2025-09-14 12:29 UTC (permalink / raw)
  To: Bjorn Andersson, Mathieu Poirier, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
	AngeloGioacchino Del Regno, Xiangzhi Tang
  Cc: linux-remoteproc, devicetree, linux-kernel, linux-arm-kernel,
	linux-mediatek, Jjian Zhou, Hailong Fan, Xiangzhi Tang

1.Add vcp suspend and resume callback
2.Add sleep lock ipi cmd flow for lock vcp status while
  feature using VCP

Signed-off-by: Xiangzhi Tang <xiangzhi.tang@mediatek.com>
---
 drivers/remoteproc/mtk_vcp_common.c       | 175 ++++++++++++++++++++++
 drivers/remoteproc/mtk_vcp_common.h       |  29 ++++
 drivers/remoteproc/mtk_vcp_rproc.c        |  66 ++++++++
 drivers/remoteproc/mtk_vcp_rproc.h        |   4 +
 include/linux/remoteproc/mtk_vcp_public.h |   1 +
 5 files changed, 275 insertions(+)

diff --git a/drivers/remoteproc/mtk_vcp_common.c b/drivers/remoteproc/mtk_vcp_common.c
index 9767f5ff15a0..0bd071f73b23 100644
--- a/drivers/remoteproc/mtk_vcp_common.c
+++ b/drivers/remoteproc/mtk_vcp_common.c
@@ -364,6 +364,127 @@ u32 wait_core_hart_shutdown(struct mtk_vcp_device *vcp,
 	return retry;
 }
 
+bool is_vcp_suspending(struct mtk_vcp_device *vcp)
+{
+	return vcp->vcp_cluster->is_suspending ? true : false;
+}
+
+void vcp_wait_core_stop(struct mtk_vcp_device *vcp, enum vcp_core_id core_id)
+{
+	u32 core_halt;
+	u32 core_axi;
+	u32 core_status;
+	u32 status;
+
+	/* make sure vcp is in idle state */
+	int timeout = SUSPEND_WAIT_TIMEOUT_MS;
+
+	while (--timeout) {
+		switch (core_id) {
+		case VCP_ID:
+			core_status = readl(vcp->vcp_cluster->cfg + R_CORE0_STATUS);
+			status = (vcp->vcp_cluster->twohart[VCP_ID] ?
+				 (B_CORE_GATED | B_HART0_HALT | B_HART1_HALT) :
+				 (B_CORE_GATED | B_HART0_HALT));
+			break;
+		case MMUP_ID:
+			core_status = readl(vcp->vcp_cluster->cfg + R_CORE1_STATUS);
+			status = (vcp->vcp_cluster->twohart[MMUP_ID] ?
+				 (B_CORE_GATED | B_HART0_HALT | B_HART1_HALT) :
+				 (B_CORE_GATED | B_HART0_HALT));
+			break;
+		case VCP_CORE_TOTAL:
+		default:
+			break;
+		}
+
+		core_halt = ((core_status & status) == status);
+		core_axi = core_status & (B_CORE_AXIS_BUSY);
+
+		if (core_halt && !core_axi) {
+			dev_err(vcp->dev, "[%s] core status 0x%x, GPIC 0x%x flag 0x%x\n",
+				core_id ? "MMUP_ID" : "VCP_ID", core_status,
+				readl(vcp->vcp_cluster->cfg_core + R_GIPC_IN_SET),
+				readl(vcp->vcp_cluster->cfg_sec + R_GPR3_SEC));
+			break;
+		}
+		usleep_range(USDELAY_RANGE_MIN, USDELAY_RANGE_MAX);
+	}
+
+	if (timeout == 0) {
+		dev_err(vcp->dev, "wait [%s] core stop timeout, current status 0x%x\n",
+			core_id ? "MMUP_ID" : "VCP_ID", core_status);
+	}
+}
+
+void vcp_wait_rdy_signal(struct mtk_vcp_device *vcp, bool rdy)
+{
+	u32 rdy_signal;
+	int ret;
+
+	if (!IS_ERR((void const *)vcp->vcp_cluster->vcp_rdy)) {
+		if (rdy)
+			ret = read_poll_timeout_atomic(readl, rdy_signal, rdy_signal & READY_BIT,
+						       USEC_PER_MSEC,
+						       VCP_SYNC_TIMEOUT_MS * USEC_PER_MSEC,
+						       false,
+						       vcp->vcp_cluster->vcp_rdy + VLP_AO_RSVD7);
+		else
+			ret = read_poll_timeout_atomic(readl, rdy_signal, !(rdy_signal & READY_BIT),
+						       USEC_PER_MSEC,
+						       VCP_SYNC_TIMEOUT_MS * USEC_PER_MSEC,
+						       false,
+						       vcp->vcp_cluster->vcp_rdy + VLP_AO_RSVD7);
+		if (ret < 0)
+			dev_err(vcp->dev, "wait vcp %s timeout 0x%x\n",
+				rdy ? "set rdy bit" : "clr rdy bit",
+				readl(vcp->vcp_cluster->vcp_rdy + VLP_AO_RSVD7));
+	} else {
+		dev_err(vcp->dev, "illegal vcp rdy signal\n");
+	}
+}
+
+void vcp_wait_suspend_resume(struct mtk_vcp_device *vcp, bool suspend)
+{
+	int timeout = SUSPEND_WAIT_TIMEOUT_MS;
+
+	if (suspend) {
+		writel(B_CORE0_SUSPEND, vcp->vcp_cluster->cfg_core + AP_R_GPR2);
+		writel(B_CORE1_SUSPEND, vcp->vcp_cluster->cfg_core + AP_R_GPR3);
+		writel(SUSPEND_IPI_MAGIC, vcp->vcp_cluster->cfg + VCP_C0_GPR0_SUSPEND_RESUME_FLAG);
+		writel(SUSPEND_IPI_MAGIC, vcp->vcp_cluster->cfg + VCP_C1_GPR0_SUSPEND_RESUME_FLAG);
+		writel(B_GIPC4_SETCLR_3, vcp->vcp_cluster->cfg_core + R_GIPC_IN_SET);
+	} else {
+		writel(B_CORE0_RESUME, vcp->vcp_cluster->cfg_core + AP_R_GPR2);
+		writel(B_CORE1_RESUME, vcp->vcp_cluster->cfg_core + AP_R_GPR3);
+		writel(RESUME_IPI_MAGIC, vcp->vcp_cluster->cfg + VCP_C0_GPR0_SUSPEND_RESUME_FLAG);
+		writel(RESUME_IPI_MAGIC, vcp->vcp_cluster->cfg + VCP_C1_GPR0_SUSPEND_RESUME_FLAG);
+		writel(B_GIPC4_SETCLR_3, vcp->vcp_cluster->cfg_core + R_GIPC_IN_SET);
+	}
+
+	while (--timeout) {
+		if (suspend &&
+		    (readl(vcp->vcp_cluster->cfg_sec + R_GPR3_SEC) & (VCP_AP_SUSPEND)) &&
+		    (readl(vcp->vcp_cluster->cfg_sec + R_GPR2_SEC) & (MMUP_AP_SUSPEND)))
+			break;
+		else if (!suspend &&
+			 !(readl(vcp->vcp_cluster->cfg_sec + R_GPR3_SEC) & (VCP_AP_SUSPEND)) &&
+			 !(readl(vcp->vcp_cluster->cfg_sec + R_GPR2_SEC) & (MMUP_AP_SUSPEND)))
+			break;
+		usleep_range(USDELAY_RANGE_MIN, USDELAY_RANGE_MAX);
+	}
+	if (timeout <= 0) {
+		dev_err(vcp->dev, "vcp %s timeout GPIC 0x%x 0x%x 0x%x 0x%x flag 0x%x 0x%x\n",
+			suspend ? "suspend" : "resume",
+			readl(vcp->vcp_cluster->cfg_core + R_GIPC_IN_SET),
+			readl(vcp->vcp_cluster->cfg_core + R_GIPC_IN_CLR),
+			readl(vcp->vcp_cluster->cfg_core + AP_R_GPR2),
+			readl(vcp->vcp_cluster->cfg_core + AP_R_GPR3),
+			readl(vcp->vcp_cluster->cfg_sec + R_GPR2_SEC),
+			readl(vcp->vcp_cluster->cfg_sec + R_GPR3_SEC));
+	}
+}
+
 void vcp_A_register_notify(enum feature_id id,
 			   struct notifier_block *nb)
 {
@@ -524,7 +645,23 @@ static int reset_vcp(struct mtk_vcp_device *vcp)
 
 static int vcp_enable_pm_clk(struct mtk_vcp_device *vcp, enum feature_id id)
 {
+	int ret;
+	bool suspend_status;
+	struct slp_ctrl_data ipi_data;
+
 	mutex_lock(&vcp->vcp_cluster->vcp_pw_clk_mutex);
+	read_poll_timeout_atomic(is_vcp_suspending,
+				 suspend_status, !suspend_status,
+				 USEC_PER_MSEC,
+				 SUSPEND_WAIT_TIMEOUT_MS * USEC_PER_MSEC,
+				 false, vcp);
+	if (suspend_status) {
+		dev_warn(vcp->dev, "%s blocked by vcp suspend, pwclkcnt(%d)\n",
+			 __func__,
+			 vcp->vcp_cluster->pwclkcnt);
+		return -ETIMEDOUT;
+	}
+
 	if (vcp->vcp_cluster->pwclkcnt == 0) {
 		if (!is_vcp_ready_by_coreid(VCP_CORE_TOTAL)) {
 			if (reset_vcp(vcp)) {
@@ -534,6 +671,17 @@ static int vcp_enable_pm_clk(struct mtk_vcp_device *vcp, enum feature_id id)
 		}
 	}
 	vcp->vcp_cluster->pwclkcnt++;
+	if (id != RTOS_FEATURE_ID) {
+		ipi_data.cmd = SLP_WAKE_LOCK;
+		ipi_data.feature = id;
+		ret = vcp->ipi_ops->ipi_send_compl(vcp->ipi_dev, IPI_OUT_C_SLEEP_0,
+					     &ipi_data, PIN_OUT_C_SIZE_SLEEP_0, 500);
+		if (ret < 0) {
+			dev_warn(vcp->dev, "%s ipc_send_compl failed. ret %d\n",
+				 __func__, ret);
+			return ret;
+		}
+	}
 	mutex_unlock(&vcp->vcp_cluster->vcp_pw_clk_mutex);
 
 	return 0;
@@ -541,7 +689,34 @@ static int vcp_enable_pm_clk(struct mtk_vcp_device *vcp, enum feature_id id)
 
 static int vcp_disable_pm_clk(struct mtk_vcp_device *vcp, enum feature_id id)
 {
+	int ret;
+	bool suspend_status;
+	struct slp_ctrl_data ipi_data;
+
 	mutex_lock(&vcp->vcp_cluster->vcp_pw_clk_mutex);
+	read_poll_timeout_atomic(is_vcp_suspending,
+				 suspend_status, !suspend_status,
+				 USEC_PER_MSEC,
+				 SUSPEND_WAIT_TIMEOUT_MS * USEC_PER_MSEC,
+				 false, vcp);
+	if (suspend_status) {
+		dev_warn(vcp->dev, "%s blocked by vcp suspend, pwclkcnt(%d)\n",
+			 __func__,
+			 vcp->vcp_cluster->pwclkcnt);
+		return -ETIMEDOUT;
+	}
+
+	if (id != RTOS_FEATURE_ID) {
+		ipi_data.cmd = SLP_WAKE_UNLOCK;
+		ipi_data.feature = id;
+		ret = vcp->ipi_ops->ipi_send_compl(vcp->ipi_dev, IPI_OUT_C_SLEEP_0,
+					 &ipi_data, PIN_OUT_C_SIZE_SLEEP_0, 500);
+		if (ret < 0) {
+			dev_err(vcp->dev, "%s ipc_send_compl failed. ret %d\n",
+				__func__, ret);
+			return ret;
+		}
+	}
 	vcp->vcp_cluster->pwclkcnt--;
 	if (vcp->vcp_cluster->pwclkcnt < 0) {
 		for (u32 i = 0; i < NUM_FEATURE_ID; i++)
diff --git a/drivers/remoteproc/mtk_vcp_common.h b/drivers/remoteproc/mtk_vcp_common.h
index 4a4393b2ae1f..42deda362b6c 100644
--- a/drivers/remoteproc/mtk_vcp_common.h
+++ b/drivers/remoteproc/mtk_vcp_common.h
@@ -18,9 +18,13 @@
 #define VCP_IPI_DEV_READY_TIMEOUT 1000
 #define USDELAY_RANGE_MIN 1000
 #define USDELAY_RANGE_MAX 2000
+#define SUSPEND_WAIT_TIMEOUT_MS 100
 
 /* vcp platform define */
 #define DMA_MAX_MASK_BIT 33
+#define RESUME_IPI_MAGIC 0x12345678
+#define SUSPEND_IPI_MAGIC 0x87654321
+#define PIN_OUT_C_SIZE_SLEEP_0 2
 
 /* vcp load image define */
 #define VCM_IMAGE_MAGIC             (0x58881688)
@@ -98,6 +102,15 @@ enum vcp_core_id {
 	VCP_CORE_TOTAL  = 2,
 };
 
+/* vcp sleep cmd flag sync with VCP FW */
+enum {
+	SLP_WAKE_LOCK = 0,
+	SLP_WAKE_UNLOCK,
+	SLP_STATUS_DBG,
+	SLP_SUSPEND,
+	SLP_RESUME,
+};
+
 /* vcp kernel smc server id */
 enum mtk_tinysys_vcp_kernel_op {
 	MTK_TINYSYS_VCP_KERNEL_OP_RESET_SET = 0,
@@ -155,6 +168,17 @@ struct vcp_reserve_mblock {
 	size_t size;
 };
 
+/**
+ * struct slp_ctrl_data - sleep ctrl data sync with AP and VCP
+ *
+ * @feature: Feature id
+ * @cmd: sleep cmd flag.
+ */
+struct slp_ctrl_data {
+	u32 feature;
+	u32 cmd;
+};
+
 /**
  * struct vcp_work_struct - vcp notify work structure.
  *
@@ -230,6 +254,8 @@ void vcp_A_register_notify(enum feature_id id,
 			   struct notifier_block *nb);
 void vcp_A_unregister_notify(enum feature_id id,
 			     struct notifier_block *nb);
+bool is_vcp_suspending(struct mtk_vcp_device *vcp);
+
 /* vcp common reserved memory APIs */
 int vcp_reserve_memory_ioremap(struct mtk_vcp_device *vcp);
 phys_addr_t vcp_get_reserve_mem_phys(enum vcp_reserve_mem_id_t id);
@@ -252,4 +278,7 @@ int vcp_A_deregister_feature(struct mtk_vcp_device *vcp,
 
 /* vcp common core hart shutdown API */
 u32 wait_core_hart_shutdown(struct mtk_vcp_device *vcp, enum vcp_core_id core_id);
+void vcp_wait_core_stop(struct mtk_vcp_device *vcp, enum vcp_core_id core_id);
+void vcp_wait_rdy_signal(struct mtk_vcp_device *vcp, bool rdy);
+void vcp_wait_suspend_resume(struct mtk_vcp_device *vcp, bool suspend);
 #endif
diff --git a/drivers/remoteproc/mtk_vcp_rproc.c b/drivers/remoteproc/mtk_vcp_rproc.c
index 4aa0ed47abd7..133518bedd76 100644
--- a/drivers/remoteproc/mtk_vcp_rproc.c
+++ b/drivers/remoteproc/mtk_vcp_rproc.c
@@ -11,6 +11,7 @@
 #include <linux/of_platform.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
+#include <linux/suspend.h>
 #include <linux/remoteproc.h>
 
 #include "mtk_vcp_common.h"
@@ -73,12 +74,68 @@ struct mtk_ipi_device *vcp_get_ipidev(struct mtk_vcp_device *vcp)
 }
 EXPORT_SYMBOL_GPL(vcp_get_ipidev);
 
+static int mtk_vcp_suspend(struct device *dev)
+{
+	struct mtk_vcp_device *vcp = platform_get_drvdata(to_platform_device(dev));
+
+	vcp_extern_notify(VCP_ID, VCP_EVENT_SUSPEND);
+	vcp_extern_notify(MMUP_ID, VCP_EVENT_SUSPEND);
+
+	if (!vcp->vcp_cluster->is_suspending &&
+	    vcp->vcp_cluster->pwclkcnt) {
+		vcp->vcp_cluster->is_suspending = true;
+		vcp->vcp_cluster->vcp_ready[VCP_ID] = false;
+		vcp->vcp_cluster->vcp_ready[MMUP_ID] = false;
+
+		flush_workqueue(vcp->vcp_cluster->vcp_workqueue);
+
+		vcp_wait_suspend_resume(vcp, true);
+		vcp_wait_core_stop(vcp, VCP_ID);
+		vcp_wait_core_stop(vcp, MMUP_ID);
+
+		pm_runtime_put_sync(dev);
+
+		/* wait vcp clr rdy bit */
+		vcp_wait_rdy_signal(vcp, false);
+	}
+	vcp->vcp_cluster->is_suspending = true;
+
+	return 0;
+}
+
+static int mtk_vcp_resume(struct device *dev)
+{
+	struct mtk_vcp_device *vcp = platform_get_drvdata(to_platform_device(dev));
+
+	if (vcp->vcp_cluster->is_suspending &&
+	    vcp->vcp_cluster->pwclkcnt) {
+		pm_runtime_get_sync(dev);
+
+		/* wait vcp set rdy bit */
+		vcp_wait_rdy_signal(vcp, true);
+		vcp_wait_suspend_resume(vcp, false);
+	}
+	vcp->vcp_cluster->is_suspending = false;
+
+	vcp_extern_notify(MMUP_ID, VCP_EVENT_RESUME);
+	vcp_extern_notify(VCP_ID, VCP_EVENT_RESUME);
+
+	return 0;
+}
+
 static int mtk_vcp_start(struct rproc *rproc)
 {
 	struct mtk_vcp_device *vcp = (struct mtk_vcp_device *)rproc->priv;
 	struct arm_smccc_res res;
 	int ret;
 
+	ret = vcp->ipi_ops->ipi_register(vcp->ipi_dev, IPI_OUT_C_SLEEP_0,
+					 NULL, NULL, &vcp->vcp_cluster->slp_ipi_ack_data);
+	if (ret) {
+		dev_err(vcp->dev, "Failed to register IPI_OUT_C_SLEEP_0\n");
+		goto slp_ipi_unregister;
+	}
+
 	ret = vcp->ipi_ops->ipi_register(vcp->ipi_dev, IPI_IN_VCP_READY_0,
 					 (void *)vcp_A_ready_ipi_handler,
 					 vcp, &vcp->vcp_cluster->msg_vcp_ready0);
@@ -118,6 +175,8 @@ static int mtk_vcp_start(struct rproc *rproc)
 	vcp->ipi_ops->ipi_unregister(vcp->ipi_dev, IPI_IN_VCP_READY_1);
 vcp0_ready_ipi_unregister:
 	vcp->ipi_ops->ipi_unregister(vcp->ipi_dev, IPI_IN_VCP_READY_0);
+slp_ipi_unregister:
+	vcp->ipi_ops->ipi_unregister(vcp->ipi_dev, IPI_OUT_C_SLEEP_0);
 
 	return ret;
 }
@@ -435,6 +494,7 @@ static struct mtk_vcp_ipi_ops mt8196_vcp_ipi_ops = {
 static const struct mtk_vcp_of_data mt8196_of_data = {
 	.ops = {
 		.vcp_is_ready = is_vcp_ready,
+		.vcp_is_suspending = is_vcp_suspending,
 		.vcp_register_notify = vcp_A_register_notify,
 		.vcp_unregister_notify = vcp_A_unregister_notify,
 		.vcp_register_feature = vcp_A_register_feature,
@@ -455,6 +515,11 @@ static const struct mtk_vcp_of_data mt8196_of_data = {
 	},
 };
 
+static const struct dev_pm_ops mtk_vcp_rproc_pm_ops = {
+	.suspend_noirq = mtk_vcp_suspend,
+	.resume_noirq = mtk_vcp_resume,
+};
+
 static const struct of_device_id mtk_vcp_of_match[] = {
 	{ .compatible = "mediatek,mt8196-vcp", .data = &mt8196_of_data},
 	{}
@@ -468,6 +533,7 @@ static struct platform_driver mtk_vcp_device = {
 	.driver = {
 		.name = "mtk-vcp",
 		.of_match_table = mtk_vcp_of_match,
+		.pm = pm_ptr(&mtk_vcp_rproc_pm_ops),
 	},
 };
 
diff --git a/drivers/remoteproc/mtk_vcp_rproc.h b/drivers/remoteproc/mtk_vcp_rproc.h
index e36612256b63..3713977e4171 100644
--- a/drivers/remoteproc/mtk_vcp_rproc.h
+++ b/drivers/remoteproc/mtk_vcp_rproc.h
@@ -22,7 +22,9 @@
  * @sram_offset: core sram memory layout
  * @msg_vcp_ready0: core0 ready ipi msg data
  * @msg_vcp_ready1: core1 ready ipi msg data
+ * @slp_ipi_ack_data: sleep ipi msg data
  * @pwclkcnt: power and clock config count data
+ * @is_suspending: suspend status flag
  * @vcp_ready: vcp core status flag
  * @share_mem_iova: shared memory iova base
  * @share_mem_size: shared memory size
@@ -45,7 +47,9 @@ struct mtk_vcp_of_cluster {
 	u32 sram_offset[VCP_CORE_TOTAL];
 	u32 msg_vcp_ready0;
 	u32 msg_vcp_ready1;
+	u32 slp_ipi_ack_data;
 	int pwclkcnt;
+	bool is_suspending;
 	bool vcp_ready[VCP_CORE_TOTAL];
 	dma_addr_t share_mem_iova;
 	size_t share_mem_size;
diff --git a/include/linux/remoteproc/mtk_vcp_public.h b/include/linux/remoteproc/mtk_vcp_public.h
index 5bd562d1ae62..5a859a3bc1eb 100644
--- a/include/linux/remoteproc/mtk_vcp_public.h
+++ b/include/linux/remoteproc/mtk_vcp_public.h
@@ -107,6 +107,7 @@ struct mtk_vcp_ipi_ops {
 
 struct mtk_vcp_ops {
 	bool (*vcp_is_ready)(enum feature_id id);
+	bool (*vcp_is_suspending)(struct mtk_vcp_device *vcp);
 	void (*vcp_register_notify)(enum feature_id id, struct notifier_block *nb);
 	void (*vcp_unregister_notify)(enum feature_id id, struct notifier_block *nb);
 	int (*vcp_register_feature)(struct mtk_vcp_device *vcp, enum feature_id id);
-- 
2.46.0



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH v2 0/4] ASoC: mediatek: Add support of VCP on Mediatek mt8196 SoC.
  2025-09-14 12:29 [PATCH v2 0/4] ASoC: mediatek: Add support of VCP on Mediatek mt8196 SoC Xiangzhi Tang
                   ` (3 preceding siblings ...)
  2025-09-14 12:29 ` [PATCH v2 4/4] remoterpoc: mediatek: vcp: Add vcp suspned and resume feature Xiangzhi Tang
@ 2025-09-14 13:16 ` Krzysztof Kozlowski
  4 siblings, 0 replies; 12+ messages in thread
From: Krzysztof Kozlowski @ 2025-09-14 13:16 UTC (permalink / raw)
  To: Xiangzhi Tang, Bjorn Andersson, Mathieu Poirier, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
	AngeloGioacchino Del Regno
  Cc: linux-remoteproc, devicetree, linux-kernel, linux-arm-kernel,
	linux-mediatek, Jjian Zhou, Hailong Fan

On 14/09/2025 14:29, Xiangzhi Tang wrote:
> Add support MediaTek's Video Companion Processor(VCP) host driver to
> control the MediaTek VCP Risc-V coprocessor.
> 
>> This series is based on linux-next, tag: next-20250912.
>>
>> Changes in v2:
>> - Refactor: split vcp driver patch to mult diff
>> - Fix reviewer's comments

What exactly changed? That's too vague.

>> This series patches dependent on:
>> [1]
>> https://patchwork.kernel.org/project/linux-mediatek/patch/20250623120154.109429-2-angelogioacchino.delregno@collabora.com/

Why? This received clear feedback that needs changes, so why would you
now send something based on obsolete patchset?



Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v2 1/4] dt-bindings: remoteproc: Add VCP support for mt8196
  2025-09-14 12:29 ` [PATCH v2 1/4] dt-bindings: remoteproc: Add VCP support for mt8196 Xiangzhi Tang
@ 2025-09-14 13:18   ` Krzysztof Kozlowski
  2025-09-14 21:57   ` Rob Herring (Arm)
  2025-09-15  7:48   ` AngeloGioacchino Del Regno
  2 siblings, 0 replies; 12+ messages in thread
From: Krzysztof Kozlowski @ 2025-09-14 13:18 UTC (permalink / raw)
  To: Xiangzhi Tang, Bjorn Andersson, Mathieu Poirier, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
	AngeloGioacchino Del Regno
  Cc: linux-remoteproc, devicetree, linux-kernel, linux-arm-kernel,
	linux-mediatek, Jjian Zhou, Hailong Fan

On 14/09/2025 14:29, Xiangzhi Tang wrote:
> Add the new binding document for MediaTek Video Companion
> Processor(VCP) on MediaTek mt8196.
> 
> Signed-off-by: Xiangzhi Tang <xiangzhi.tang@mediatek.com>
> ---
>  .../remoteproc/mediatek,mt8196-vcp.yaml       | 165 ++++++++++++++++++
>  1 file changed, 165 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/remoteproc/mediatek,mt8196-vcp.yaml
> 
> diff --git a/Documentation/devicetree/bindings/remoteproc/mediatek,mt8196-vcp.yaml b/Documentation/devicetree/bindings/remoteproc/mediatek,mt8196-vcp.yaml
> new file mode 100644
> index 000000000000..71a55943843b
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/remoteproc/mediatek,mt8196-vcp.yaml
> @@ -0,0 +1,165 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/remoteproc/mediatek,mt8196-vcp.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: MediaTek Video Companion Processor (VCP)
> +
> +maintainers:
> +  - Xiangzhi Tang <Xiangzhi.Tang@mediatek.com>
> +
> +description:
> +  The MediaTek VCP enables the SoC control the MediaTek Video Companion Risc-V coprocessor.

You keep ignoring comments.

> +
> +properties:
> +  compatible:
> +    enum:
> +      - mediatek,mt8196-vcp
> +
> +  reg:
> +    items:
> +      - description: sram base
> +      - description: cfg group IO
> +      - description: cfg core group IO
> +      - description: cfg sec group IO
> +      - description: vcp rdy group IO
> +
> +  reg-names:
> +    items:
> +      - const: sram

Why would sram be the first, main address of this device? Not the
address space responsible for configuring it?

> +      - const: cfg
> +      - const: cfg_core
> +      - const: cfg_sec
> +      - const: vcp_vlp_ao_rsvd7
> +
> +  interrupts:
> +    maxItems: 1
> +
> +  mboxes:
> +    maxItems: 5
> +
> +  mbox-names:
> +    maxItems: 5

Not much improved, more ignored comments and since the changelog is so
vague I am afraid you just skimmed through the comments and you just
ignored many of them.

Implement entire feedback and provide detailed changelog.

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v2 2/4] remoterpoc: mediatek: vcp: Add vcp remoteproc driver
  2025-09-14 12:29 ` [PATCH v2 2/4] remoterpoc: mediatek: vcp: Add vcp remoteproc driver Xiangzhi Tang
@ 2025-09-14 13:28   ` Krzysztof Kozlowski
  2025-09-15  9:14   ` AngeloGioacchino Del Regno
  1 sibling, 0 replies; 12+ messages in thread
From: Krzysztof Kozlowski @ 2025-09-14 13:28 UTC (permalink / raw)
  To: Xiangzhi Tang, Krzysztof Kozlowski, Matthias Brugger,
	AngeloGioacchino Del Regno, irving.ch.lin,
	Vince-WL Liu (劉文龍),
	Jh Hsu (許希孜),
	Project_Global_Chrome_Upstream_Group,
	Sirius Wang (王皓昱)
  Cc: Rob Herring, Mathieu Poirier, Bjorn Andersson, linux-remoteproc,
	Conor Dooley, devicetree, linux-kernel, linux-arm-kernel,
	linux-mediatek, Jjian Zhou, Hailong Fan

On 14/09/2025 14:29, Xiangzhi Tang wrote:
> +
> +static const struct rproc_ops mtk_vcp_ops = {
> +	.load		= mtk_vcp_load,
> +	.start		= mtk_vcp_start,
> +	.stop		= mtk_vcp_stop,
> +};
> +
> +static int vcp_multi_core_init(struct platform_device *pdev,
> +			       struct mtk_vcp_of_cluster *vcp_cluster,
> +			       enum vcp_core_id core_id)
> +{
> +	int ret;
> +
> +	ret = of_property_read_u32(pdev->dev.of_node, "mtk,vcp-core-twohart",
> +				   &vcp_cluster->twohart[core_id]);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to twohart property\n");
> +		return ret;
> +	}
> +	ret = of_property_read_u32(pdev->dev.of_node, "mtk,core-sram-offset",

Undocumented ABI, if you ever bothered to test your DTS you would see
clear warning.

> +				   &vcp_cluster->sram_offset[core_id]);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to sram-offset property\n");
> +		return ret;
> +	}
> +
> +	return ret;
> +}
> +
> +static struct mtk_vcp_device *vcp_rproc_init(struct platform_device *pdev,
> +					     struct mtk_vcp_of_cluster *vcp_cluster)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *np = dev_of_node(dev);
> +	struct device_node *child;
> +	struct platform_device *cpdev;
> +	struct mtk_vcp_device *vcp;
> +	struct rproc *rproc;
> +	const struct mtk_vcp_of_data *vcp_of_data;
> +	u32 core_id;
> +	int ret;
> +
> +	vcp_of_data = of_device_get_match_data(dev);
> +	rproc = devm_rproc_alloc(dev, np->name, &mtk_vcp_ops,
> +				 vcp_of_data->platdata.fw_name,
> +				 sizeof(struct mtk_vcp_device));
> +	if (!rproc) {
> +		dev_err(dev, "unable to allocate remoteproc\n");

No, you never do this.

> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	vcp  = rproc->priv;
> +	vcp->rproc = rproc;
> +	vcp->pdev = pdev;
> +	vcp->dev = dev;
> +	vcp->ops = &vcp_of_data->ops;
> +	vcp->platdata = &vcp_of_data->platdata;
> +	vcp->vcp_cluster = vcp_cluster;
> +
> +	rproc->auto_boot = vcp_of_data->platdata.auto_boot;
> +	rproc->sysfs_read_only = vcp_of_data->platdata.sysfs_read_only;
> +	mutex_init(&vcp->vcp_cluster->vcp_feature_mutex);
> +	mutex_init(&vcp->vcp_cluster->vcp_pw_clk_mutex);
> +	platform_set_drvdata(pdev, vcp);
> +
> +	ret = vcp_reserve_memory_ioremap(vcp);
> +	if (ret) {
> +		dev_err(dev, "vcp_reserve_memory_ioremap failed ret = %d\n", ret);
> +		return ERR_PTR(ret);
> +	}
> +
> +	core_id = 0;
> +	for_each_available_child_of_node(np, child) {
> +		if (of_device_is_compatible(child, "mediatek,vcp-core")) {
> +			cpdev = of_find_device_by_node(child);
> +			if (!cpdev) {
> +				ret = -ENODEV;
> +				dev_err(dev, "Not found platform device for core\n");

1. That's probe path, so why are you using this old style? The syntax is
return dev_err_probe and I am dissapointed that your internal review did
not ask for that.

2. You just leak here everywhere device.

This patchset has trivial errors, which should be spotted easily by
internal review. Plus your other patch ignored EXITING feedback, so what
is even point of posting this if you are just going to ignore us?

Please confirm that you received extensive internal review before
posting this?

Mediatek posts so many patches, I complained so many times about poor
quality, things a bit improved two years ago and for a year the quality
deteriorated and Mediatek posts poor code again. It's huge company with
huge resources, so I do not understand why trivial bugs like this cannot
be found inside first, to offload the community reviewers.

NAK

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v2 3/4] remoterpoc: mediatek: vcp: Add ipi-mbox communication
  2025-09-14 12:29 ` [PATCH v2 3/4] remoterpoc: mediatek: vcp: Add ipi-mbox communication Xiangzhi Tang
@ 2025-09-14 13:30   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 12+ messages in thread
From: Krzysztof Kozlowski @ 2025-09-14 13:30 UTC (permalink / raw)
  To: Xiangzhi Tang, Bjorn Andersson, Mathieu Poirier, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
	AngeloGioacchino Del Regno
  Cc: linux-remoteproc, devicetree, linux-kernel, linux-arm-kernel,
	linux-mediatek, Jjian Zhou, Hailong Fan

On 14/09/2025 14:29, Xiangzhi Tang wrote:
>  
> +/**
> + * vcp_get_ipidev() - get a vcp ipi device struct to reference vcp ipi.
> + *
> + * @vcp: mtk_vcp_device structure from vcp_get().
> + *
> + **/
> +struct mtk_ipi_device *vcp_get_ipidev(struct mtk_vcp_device *vcp)
> +{
> +	return vcp->ipi_dev;
> +}
> +EXPORT_SYMBOL_GPL(vcp_get_ipidev);

NAK, there is no user of this.

I did not check the rest but I assume the same pattern in other places.

> +
>  static int mtk_vcp_start(struct rproc *rproc)
>  {
>  	struct mtk_vcp_device *vcp = (struct mtk_vcp_device *)rproc->priv;
>  	struct arm_smccc_res res;
> +	int ret;
> +
> +	ret = vcp->ipi_ops->ipi_register(vcp->ipi_dev, IPI_IN_VCP_READY_0,
> +					 (void *)vcp_A_ready_ipi_handler,
> +					 vcp, &vcp->vcp_cluster->msg_vcp_ready0);
> +	if (ret) {
> +		dev_err(vcp->dev, "Failed to register IPI_IN_VCP_READY_0\n");
> +		goto vcp0_ready_ipi_unregister;
> +	}
> +
> +	ret = vcp->ipi_ops->ipi_register(vcp->ipi_dev, IPI_IN_VCP_READY_1,
> +					 (void *)vcp_A_ready_ipi_handler,
> +					 vcp, &vcp->vcp_cluster->msg_vcp_ready1);
> +	if (ret) {
> +		dev_err(vcp->dev, "Failed to register IPI_IN_VCP_READY_1\n");
> +		goto vcp1_ready_ipi_unregister;
> +	}
>  
>  	/* core 0 */
>  	arm_smccc_smc(MTK_SIP_TINYSYS_VCP_CONTROL,
> @@ -82,7 +112,14 @@ static int mtk_vcp_start(struct rproc *rproc)
>  		dev_info(vcp->dev, "bootup successfully\n");
>  	}
>  
> -	return 0;
> +	return ret;

This make sno sense, why are you doing this?


Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v2 1/4] dt-bindings: remoteproc: Add VCP support for mt8196
  2025-09-14 12:29 ` [PATCH v2 1/4] dt-bindings: remoteproc: Add VCP support for mt8196 Xiangzhi Tang
  2025-09-14 13:18   ` Krzysztof Kozlowski
@ 2025-09-14 21:57   ` Rob Herring (Arm)
  2025-09-15  7:48   ` AngeloGioacchino Del Regno
  2 siblings, 0 replies; 12+ messages in thread
From: Rob Herring (Arm) @ 2025-09-14 21:57 UTC (permalink / raw)
  To: Xiangzhi Tang
  Cc: Matthias Brugger, devicetree, linux-remoteproc,
	Krzysztof Kozlowski, Jjian Zhou, Xiangzhi Tang, linux-mediatek,
	Conor Dooley, AngeloGioacchino Del Regno, Hailong Fan,
	linux-kernel, linux-arm-kernel, Bjorn Andersson, Mathieu Poirier


On Sun, 14 Sep 2025 20:29:24 +0800, Xiangzhi Tang wrote:
> Add the new binding document for MediaTek Video Companion
> Processor(VCP) on MediaTek mt8196.
> 
> Signed-off-by: Xiangzhi Tang <xiangzhi.tang@mediatek.com>
> ---
>  .../remoteproc/mediatek,mt8196-vcp.yaml       | 165 ++++++++++++++++++
>  1 file changed, 165 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/remoteproc/mediatek,mt8196-vcp.yaml
> 

My bot found errors running 'make dt_binding_check' on your patch:

yamllint warnings/errors:

dtschema/dtc warnings/errors:
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/remoteproc/mediatek,mt8196-vcp.yaml: patternProperties:^vcp@[a-f0-9]+$:properties:mtk,vcp-core-twohart: 'anyOf' conditional failed, one must be fixed:
	'description' is a dependency of '$ref'
	'/schemas/types.yaml#/definitions/uint32' does not match '^#/(definitions|\\$defs)/'
		hint: A vendor property can have a $ref to a a $defs schema
	hint: Vendor specific properties must have a type and description unless they have a defined, common suffix.
	from schema $id: http://devicetree.org/meta-schemas/vendor-props.yaml#
Documentation/devicetree/bindings/remoteproc/mediatek,mt8196-vcp.example.dts:26:18: fatal error: dt-bindings/power/mt8196-power.h: No such file or directory
   26 |         #include <dt-bindings/power/mt8196-power.h>
      |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
make[2]: *** [scripts/Makefile.dtbs:132: Documentation/devicetree/bindings/remoteproc/mediatek,mt8196-vcp.example.dtb] Error 1
make[2]: *** Waiting for unfinished jobs....
make[1]: *** [/builds/robherring/dt-review-ci/linux/Makefile:1525: dt_binding_check] Error 2
make: *** [Makefile:248: __sub-make] Error 2

doc reference errors (make refcheckdocs):

See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/20250914122943.10412-2-xiangzhi.tang@mediatek.com

The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v2 1/4] dt-bindings: remoteproc: Add VCP support for mt8196
  2025-09-14 12:29 ` [PATCH v2 1/4] dt-bindings: remoteproc: Add VCP support for mt8196 Xiangzhi Tang
  2025-09-14 13:18   ` Krzysztof Kozlowski
  2025-09-14 21:57   ` Rob Herring (Arm)
@ 2025-09-15  7:48   ` AngeloGioacchino Del Regno
  2 siblings, 0 replies; 12+ messages in thread
From: AngeloGioacchino Del Regno @ 2025-09-15  7:48 UTC (permalink / raw)
  To: Xiangzhi Tang, Bjorn Andersson, Mathieu Poirier, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Matthias Brugger
  Cc: linux-remoteproc, devicetree, linux-kernel, linux-arm-kernel,
	linux-mediatek, Jjian Zhou, Hailong Fan

Il 14/09/25 14:29, Xiangzhi Tang ha scritto:
> Add the new binding document for MediaTek Video Companion
> Processor(VCP) on MediaTek mt8196.
> 
> Signed-off-by: Xiangzhi Tang <xiangzhi.tang@mediatek.com>
> ---
>   .../remoteproc/mediatek,mt8196-vcp.yaml       | 165 ++++++++++++++++++
>   1 file changed, 165 insertions(+)
>   create mode 100644 Documentation/devicetree/bindings/remoteproc/mediatek,mt8196-vcp.yaml
> 
> diff --git a/Documentation/devicetree/bindings/remoteproc/mediatek,mt8196-vcp.yaml b/Documentation/devicetree/bindings/remoteproc/mediatek,mt8196-vcp.yaml
> new file mode 100644
> index 000000000000..71a55943843b
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/remoteproc/mediatek,mt8196-vcp.yaml
> @@ -0,0 +1,165 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/remoteproc/mediatek,mt8196-vcp.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: MediaTek Video Companion Processor (VCP)
> +
> +maintainers:
> +  - Xiangzhi Tang <Xiangzhi.Tang@mediatek.com>
> +
> +description:
> +  The MediaTek VCP enables the SoC control the MediaTek Video Companion Risc-V coprocessor.
> +
> +properties:
> +  compatible:
> +    enum:
> +      - mediatek,mt8196-vcp
> +
> +  reg:
> +    items:
> +      - description: sram base
> +      - description: cfg group IO
> +      - description: cfg core group IO
> +      - description: cfg sec group IO
> +      - description: vcp rdy group IO
> +
> +  reg-names:
> +    items:
> +      - const: sram
> +      - const: cfg
> +      - const: cfg_core

sram, cfg-global, cfg-core, cfg-sec, vcp-vlp-ao-ready

> +      - const: cfg_sec
> +      - const: vcp_vlp_ao_rsvd7
> +
> +  interrupts:
> +    maxItems: 1
> +
> +  mboxes:
> +    maxItems: 5
> +
> +  mbox-names:
> +    maxItems: 5
> +
> +  power-domains:
> +    maxItems: 1
> +
> +  iommus:
> +    description:
> +      Using MediaTek iommu to apply larb ports for Multimedia Memory
> +      Management Unit and address translation
> +      Documentation/devicetree/bindings/iommu/arm,smmu-v3.yaml
> +    maxItems: 1
> +
> +  memory-region:
> +    maxItems: 1
> +
> +patternProperties:
> +  "^vcp@[a-f0-9]+$":
> +    type: object
> +    description:
> +      The MediaTek VCP integrated to SoC might be a multi-core version.

"may feature one or multiple cores"

> +      The other cores are represented as child nodes of the boot core.
> +      There are some integration differences for the IP like the usage of
> +      address translator for translating SoC bus addresses into address
> +      space for the processor.
> +
> +      The SRAM are shared by all cores, each VCP core only using a piece

s/piece/portion of/g

> +      SRAM memory. The power of SRAM should be enabled before booting VCP cores.

How do you enable the SRAM power?

Is there a regulator, a power domain, both, or what?

> +      The size of SRAM are varied on differnt SoCs.
> +
> +      The VCP cores has differences on different SoCs to support for
> +      Hart.
> +
> +    properties:
> +      compatible:
> +        enum:
> +          - mediatek,vcp-core
> +
> +      reg:
> +        description: The base address and size of SRAM.
> +        maxItems: 1
> +
> +      reg-names:
> +        const: sram
> +
> +      mtk,vcp-core-twohart:

1. s/mtk,/mediatek,/g
2. there's no description, what does "twohart" mean?

> +        enum: [0, 1]
> +        $ref: /schemas/types.yaml#/definitions/uint32
> +
> +      mtk,vcp-sram-offset:
> +        description:
> +          Allocated SRAM memory for each VCP core used.

vcp-sram-offset is the same as the iostart in `reg`, so you don't need this
property at all.

> +        $ref: /schemas/types.yaml#/definitions/uint32
> +
> +    required:
> +      - compatible
> +      - reg
> +      - reg-names
> +      - mtk,vcp-core-twohart
> +      - mtk,vcp-sram-offset
> +
> +    additionalProperties: false
> +
> +required:
> +  - compatible
> +  - reg
> +  - reg-names
> +  - interrupts
> +  - mboxes
> +  - mbox-names
> +  - power-domains
> +  - iommus
> +  - memory-region
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> +    #include <dt-bindings/interrupt-controller/irq.h>
> +    #include <dt-bindings/power/mt8196-power.h>
> +
> +    vcp: vcp@31800000 {
> +        compatible = "mediatek,mt8196-vcp";
> +        reg = <0x31800000 0x60000>,
> +              <0x31a04000 0xa000>,
> +              <0x31bd0000 0x1000>,
> +              <0x31a70020 0x100>,
> +              <0x1c00091c 0x4>;
> +        reg-names = "sram",
> +                    "cfg",
> +                    "cfg_core",
> +                    "cfg_sec",
> +                    "vcp_vlp_ao_rsvd7";
> +
> +        interrupts = <GIC_SPI 787 IRQ_TYPE_LEVEL_HIGH 0>;
> +
> +        mboxes = <&vcp_mailbox0>,
> +                 <&vcp_mailbox1>,
> +                 <&vcp_mailbox2>,
> +                 <&vcp_mailbox3>,
> +                 <&vcp_mailbox4>;
> +        mbox-names = "mbox0", "mbox1", "mbox2", "mbox3", "mbox4";

Is there any descriptive name that can be used for mbox-names?

As in, what is mbox0 used for? what is mbox1 used for? etc.

> +
> +        power-domains = <&scpsys MT8196_POWER_DOMAIN_MM_PROC_DORMANT>;
> +        iommus = <&mm_smmu 160>;
> +        memory-region = <&vcp_resv_mem>;
> +
> +        vcp@0 {
> +            compatible = "mediatek,vcp-core";
> +            reg = <0x0 0x31000>;
> +            reg-names = "sram";
> +            mtk,vcp-core-twohart = <1>;

Is the first core always "twohart"?

If it is, there's no need to even have this property, as you can add that to
the driver, either in form of platform data (if this changes per-SoC) or just
hardcoded.

Regards,
Angelo

> +            mtk,vcp-sram-offset = <0x0>;
> +        };
> +
> +        vcp@31000 {
> +            compatible = "mediatek,vcp-core";
> +            reg = <0x31000 0x60000>;
> +            reg-names = "sram";
> +            mtk,vcp-core-twohart = <0>;
> +            mtk,vcp-sram-offset = <0x31000>;
> +        };
> +    };



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v2 2/4] remoterpoc: mediatek: vcp: Add vcp remoteproc driver
  2025-09-14 12:29 ` [PATCH v2 2/4] remoterpoc: mediatek: vcp: Add vcp remoteproc driver Xiangzhi Tang
  2025-09-14 13:28   ` Krzysztof Kozlowski
@ 2025-09-15  9:14   ` AngeloGioacchino Del Regno
  1 sibling, 0 replies; 12+ messages in thread
From: AngeloGioacchino Del Regno @ 2025-09-15  9:14 UTC (permalink / raw)
  To: Xiangzhi Tang, Bjorn Andersson, Mathieu Poirier, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Matthias Brugger
  Cc: linux-remoteproc, devicetree, linux-kernel, linux-arm-kernel,
	linux-mediatek, Jjian Zhou, Hailong Fan

Il 14/09/25 14:29, Xiangzhi Tang ha scritto:
> Add host driver to control the mediatek Risc-V coprocessor
> 
> 1.Support rproc mechanism to load vcm firmware from filesystem
> 2.Support SMC services to request ATF to setting vcp boot sequence
> 
> Signed-off-by: Xiangzhi Tang <xiangzhi.tang@mediatek.com>
> ---
>   drivers/remoteproc/Kconfig                |  10 +
>   drivers/remoteproc/Makefile               |   3 +
>   drivers/remoteproc/mtk_vcp_common.c       | 677 ++++++++++++++++++++++
>   drivers/remoteproc/mtk_vcp_common.h       | 225 +++++++
>   drivers/remoteproc/mtk_vcp_rproc.c        | 326 +++++++++++
>   drivers/remoteproc/mtk_vcp_rproc.h        |  71 +++
>   include/linux/remoteproc/mtk_vcp_public.h |  78 +++
>   include/linux/soc/mediatek/mtk_sip_svc.h  |   3 +
>   8 files changed, 1393 insertions(+)
>   create mode 100644 drivers/remoteproc/mtk_vcp_common.c
>   create mode 100644 drivers/remoteproc/mtk_vcp_common.h
>   create mode 100644 drivers/remoteproc/mtk_vcp_rproc.c
>   create mode 100644 drivers/remoteproc/mtk_vcp_rproc.h
>   create mode 100644 include/linux/remoteproc/mtk_vcp_public.h
> 
> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
> index 48a0d3a69ed0..1a9bb49a8a28 100644
> --- a/drivers/remoteproc/Kconfig
> +++ b/drivers/remoteproc/Kconfig
> @@ -64,6 +64,16 @@ config MTK_SCP
>   
>   	  It's safe to say N here.
>   
> +config MTK_VCP_RPROC
> +	tristate "MediaTek VCP support"
> +	depends on ARCH_MEDIATEK || COMPILE_TEST
> +	depends on ARCH_DMA_ADDR_T_64BIT
> +	help
> +	  Say y here to support MediaTek's Video Companion Processor (VCP) via
> +	  the remote processor framework.
> +
> +	  It's safe to say N here.
> +
>   config OMAP_REMOTEPROC
>   	tristate "OMAP remoteproc support"
>   	depends on ARCH_OMAP4 || SOC_OMAP5 || SOC_DRA7XX
> diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
> index 1c7598b8475d..ad48d85c5019 100644
> --- a/drivers/remoteproc/Makefile
> +++ b/drivers/remoteproc/Makefile
> @@ -15,6 +15,9 @@ obj-$(CONFIG_IMX_REMOTEPROC)		+= imx_rproc.o
>   obj-$(CONFIG_IMX_DSP_REMOTEPROC)	+= imx_dsp_rproc.o
>   obj-$(CONFIG_INGENIC_VPU_RPROC)		+= ingenic_rproc.o
>   obj-$(CONFIG_MTK_SCP)			+= mtk_scp.o mtk_scp_ipi.o
> +obj-$(CONFIG_MTK_VCP_RPROC)		+= mtk_vcp.o
> +mtk_vcp-$(CONFIG_MTK_VCP_RPROC)		+= mtk_vcp_rproc.o
> +mtk_vcp-$(CONFIG_MTK_VCP_RPROC)		+= mtk_vcp_common.o
>   obj-$(CONFIG_OMAP_REMOTEPROC)		+= omap_remoteproc.o
>   obj-$(CONFIG_WKUP_M3_RPROC)		+= wkup_m3_rproc.o
>   obj-$(CONFIG_DA8XX_REMOTEPROC)		+= da8xx_remoteproc.o
> diff --git a/drivers/remoteproc/mtk_vcp_common.c b/drivers/remoteproc/mtk_vcp_common.c
> new file mode 100644
> index 000000000000..d53e311c4981
> --- /dev/null
> +++ b/drivers/remoteproc/mtk_vcp_common.c
> @@ -0,0 +1,677 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2025 MediaTek Corporation. All rights reserved.
> + */
> +
> +#include <linux/device.h>
> +#include <linux/delay.h>
> +#include <linux/dma-buf.h>
> +#include <linux/dma-heap.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/fs.h>
> +#include <linux/interrupt.h>
> +#include <linux/iommu.h>
> +#include <linux/kernel.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_fdt.h>
> +#include <linux/of_reserved_mem.h>
> +#include <linux/slab.h>
> +#include <uapi/linux/dma-heap.h>
> +
> +#include "mtk_vcp_common.h"
> +#include "mtk_vcp_rproc.h"
> +
> +struct vcp_feature_tb feature_table[NUM_FEATURE_ID] = {

I really don't like seeing those two global variables. This will backfire one day,
especially because this is multicore hardware.

> +	{
> +		.feature    = RTOS_FEATURE_ID,
> +		.core_id    = VCP_CORE_TOTAL,
> +	},
> +	{
> +		.feature    = VDEC_FEATURE_ID,
> +		.core_id    = VCP_ID,
> +	},
> +	{
> +		.feature    = VENC_FEATURE_ID,
> +		.core_id    = VCP_ID,
> +	},
> +	{
> +		.feature    = MMDVFS_MMUP_FEATURE_ID,
> +		.core_id    = MMUP_ID,
> +	},
> +	{
> +		.feature    = MMDVFS_VCP_FEATURE_ID,
> +		.core_id    = VCP_ID,
> +	},
> +	{
> +		.feature    = MMDEBUG_FEATURE_ID,
> +		.core_id    = MMUP_ID,
> +	},
> +	{
> +		.feature    = VMM_FEATURE_ID,
> +		.core_id    = MMUP_ID,
> +	},
> +	{
> +		.feature    = VDISP_FEATURE_ID,
> +		.core_id    = MMUP_ID,
> +	},
> +	{
> +		.feature    = MMQOS_FEATURE_ID,
> +		.core_id    = VCP_ID,
> +	},
> +};
> +
> +static struct vcp_reserve_mblock vcp_reserve_mblock[] = {

(same here)

> +	{
> +		.num = VCP_RTOS_MEM_ID,
> +		.start_phys = 0x0,
> +		.start_iova = 0x0,
> +		.start_virt = 0x0,
> +		.size = 0x0,
> +	},
> +	{
> +		.num = VDEC_MEM_ID,
> +		.start_phys = 0x0,
> +		.start_iova = 0x0,
> +		.start_virt = 0x0,
> +		.size = 0x0,
> +	},
> +	{
> +		.num = VENC_MEM_ID,
> +		.start_phys = 0x0,
> +		.start_iova = 0x0,
> +		.start_virt = 0x0,
> +		.size = 0x0,
> +	},
> +	{
> +		.num = MMDVFS_VCP_MEM_ID,
> +		.start_phys = 0x0,
> +		.start_iova = 0x0,
> +		.start_virt = 0x0,
> +		.size = 0x0,
> +	},
> +	{
> +		.num = MMDVFS_MMUP_MEM_ID,
> +		.start_phys = 0x0,
> +		.start_iova = 0x0,
> +		.start_virt = 0x0,
> +		.size = 0x0,
> +	},
> +	{
> +		.num = MMQOS_MEM_ID,
> +		.start_phys = 0x0,
> +		.start_iova = 0x0,
> +		.start_virt = 0x0,
> +		.size = 0x0,
> +	},
> +};
> +
> +phys_addr_t vcp_get_reserve_mem_phys(enum vcp_reserve_mem_id_t id)

Functions that you're exporting *shall* be documented.

That's also because I'm not sure that this is the best way of doing whatever you're
trying to do here (and besides, I'm not sure what this function is used for).

> +{
> +	if (id >= 0 && id < NUMS_MEM_ID)
> +		return vcp_reserve_mblock[id].start_phys;
> +
> +	return 0;
> +}
> +
> +dma_addr_t vcp_get_reserve_mem_iova(enum vcp_reserve_mem_id_t id)
> +{
> +	if (id >= 0 && id < NUMS_MEM_ID)
> +		return vcp_reserve_mblock[id].start_iova;
> +
> +	return 0;
> +}
> +
> +void __iomem *vcp_get_reserve_mem_virt(enum vcp_reserve_mem_id_t id)
> +{
> +	if (id >= 0 && id < NUMS_MEM_ID)
> +		return vcp_reserve_mblock[id].start_virt;
> +
> +	return NULL;
> +}
> +
> +u32 vcp_get_reserve_mem_size(enum vcp_reserve_mem_id_t id)
> +{
> +	if (id >= 0 && id < NUMS_MEM_ID)
> +		return vcp_reserve_mblock[id].size;
> +
> +	return 0;
> +}
> +
> +void __iomem *vcp_get_internal_sram_virt(struct mtk_vcp_device *vcp)
> +{
> +	return vcp->vcp_cluster->sram_base;
> +}
> +
> +int vcp_reserve_memory_ioremap(struct mtk_vcp_device *vcp)
> +{
> +#define MEMORY_TBL_ELEM_NUM (2)
> +	u32 num = (u32)(sizeof(vcp_reserve_mblock)
> +			/ sizeof(vcp_reserve_mblock[0]));
> +	enum vcp_reserve_mem_id_t id;
> +	u32 vcp_mem_num;
> +	u32 i, m_idx, m_size;
> +	u32 offset;
> +	struct device_node *rmem_node;
> +	struct resource res;
> +	struct iommu_domain *domain;
> +	void __iomem *share_memory_virt;
> +	phys_addr_t mblock_start_phys;
> +	dma_addr_t share_memory_iova;
> +	size_t share_memory_size;
> +	int ret;
> +
> +	if (num != NUMS_MEM_ID) {
> +		dev_err(vcp->dev, "actual memory num(%u) is not match mem ID table (%u)\n",
> +			num, NUMS_MEM_ID);
> +		WARN_ON(1);
> +		return -EINVAL;
> +	}
> +
> +	rmem_node = of_parse_phandle(vcp->dev->of_node, "memory-region", 0);
> +	if (!rmem_node) {
> +		dev_err(vcp->dev, "No reserved memory region found.\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = of_address_to_resource(rmem_node, 0, &res);
> +	if (ret) {
> +		dev_err(vcp->dev, "failed to parse reserved memory: %d\n", ret);
> +		return ret;
> +	}
> +
> +	mblock_start_phys = (phys_addr_t)res.start;
> +
> +	/* Set reserved memory table */
> +	vcp_mem_num = of_property_count_u32_elems(vcp->dev->of_node, "vcp-mem-tbl")

First of all, vendor properties must have a vendor prefix (mediatek,vcp-mem-tbl).

But then... is this memory table static, or is it dynamic?

If it's static -> read it from the firmware!
If it's dynamic -> dynamically calculate it in the driver perhaps?

> +		      / MEMORY_TBL_ELEM_NUM;
> +	if (vcp_mem_num <= 0) {
> +		dev_warn(vcp->dev, "vcp-mem-tbl not found\n");
> +		vcp_mem_num = 0;
> +	}
> +
> +	for (i = 0; i < vcp_mem_num; i++) {
> +		ret = of_property_read_u32_index(vcp->dev->of_node, "vcp-mem-tbl",
> +						 i * MEMORY_TBL_ELEM_NUM, &m_idx);
> +		if (ret) {
> +			dev_err(vcp->dev, "cannot get memory index(%d)\n", i);
> +			return -EINVAL;
> +		}
> +
> +		ret = of_property_read_u32_index(vcp->dev->of_node, "vcp-mem-tbl",
> +						 (i * MEMORY_TBL_ELEM_NUM) + 1, &m_size);
> +		if (ret) {
> +			dev_err(vcp->dev, "Cannot get memory size(%d)(%d)\n", i, m_idx);
> +			return -EINVAL;
> +		}
> +
> +		if (m_idx >= NUMS_MEM_ID) {
> +			dev_warn(vcp->dev, "skip unexpected index, %d\n", m_idx);
> +			continue;
> +		}
> +
> +		vcp_reserve_mblock[m_idx].size = m_size;
> +	}
> +
> +	vcp_reserve_mblock[VCP_RTOS_MEM_ID].start_phys = mblock_start_phys;
> +	vcp_reserve_mblock[VCP_RTOS_MEM_ID].start_virt = devm_ioremap(vcp->dev,
> +				vcp_reserve_mblock[VCP_RTOS_MEM_ID].start_phys,
> +				vcp_reserve_mblock[VCP_RTOS_MEM_ID].size);
> +	domain = iommu_get_domain_for_dev(vcp->dev);
> +	ret = iommu_map(domain, vcp->platdata->rtos_static_iova,
> +			vcp_reserve_mblock[VCP_RTOS_MEM_ID].start_phys,
> +			vcp_reserve_mblock[VCP_RTOS_MEM_ID].size,
> +			IOMMU_READ | IOMMU_WRITE | IOMMU_PRIV, GFP_KERNEL);
> +	if (ret) {
> +		dev_err(vcp->dev, "%s iommu map fail, ret:%d.\n", __func__, ret);
> +		return ret;
> +	}
> +	vcp_reserve_mblock[VCP_RTOS_MEM_ID].start_iova = vcp->platdata->rtos_static_iova;
> +
> +	share_memory_size = 0;
> +	for (id = VDEC_MEM_ID; id < NUMS_MEM_ID; id++) {
> +		if (vcp_reserve_mblock[id].size == 0)
> +			continue;
> +		share_memory_size += vcp_reserve_mblock[id].size;
> +	}
> +
> +	ret = dma_set_mask_and_coherent(vcp->dev, DMA_BIT_MASK(DMA_MAX_MASK_BIT));
> +	if (ret) {
> +		dev_err(vcp->dev, "64-bit DMA enable failed\n");
> +		return ret;
> +	}
> +
> +	if (!vcp->dev->dma_parms) {
> +		vcp->dev->dma_parms = devm_kzalloc(vcp->dev,
> +						   sizeof(*vcp->dev->dma_parms),
> +						   GFP_KERNEL);
> +		if (vcp->dev->dma_parms) {
> +			dma_set_max_seg_size(vcp->dev, (u32)DMA_BIT_MASK(33));
> +		} else {
> +			dev_err(vcp->dev, "Failed to set DMA parms\n");
> +			return -EINVAL;
> +		}
> +	}
> +	share_memory_virt = dma_alloc_coherent(vcp->dev,
> +					       share_memory_size,
> +					       &share_memory_iova,
> +					       GFP_KERNEL);
> +	if (!share_memory_virt)
> +		return -ENOMEM;
> +	offset = 0;
> +	for (id = VDEC_MEM_ID; id < NUMS_MEM_ID; id++)  {
> +		if (vcp_reserve_mblock[id].size == 0)
> +			continue;
> +
> +		vcp_reserve_mblock[id].start_phys = vcp_reserve_mblock[VCP_RTOS_MEM_ID].start_phys +
> +						    vcp_reserve_mblock[VCP_RTOS_MEM_ID].size +
> +						    offset;
> +		vcp_reserve_mblock[id].start_iova = share_memory_iova + offset;
> +		vcp_reserve_mblock[id].start_virt = share_memory_virt + offset;
> +		offset += (u32)vcp_reserve_mblock[id].size;
> +	}
> +
> +	vcp->vcp_cluster->share_mem_iova = share_memory_iova;
> +	vcp->vcp_cluster->share_mem_size = share_memory_size;
> +
> +	return 0;
> +}
> +
> +static bool is_vcp_ready_by_coreid(enum vcp_core_id core_id)

static bool vcp_is_core_ready(enum vcp_core_id core_id)

this name looks better imo.

> +{
> +	struct device *dev;
> +	struct mtk_vcp_device *vcp;
> +
> +	dev = feature_table[RTOS_FEATURE_ID].priv;

I'd have some comments for this, but the most important one is that the
feature_table should really not be a global variable - so it's useless
for me to add comments here, because this function will probably change
quite a bit after that.

> +	if (!dev)
> +		return false;
> +	vcp = platform_get_drvdata(to_platform_device(dev));
> +	if (!vcp)
> +		return false;
> +
> +	switch (core_id) {
> +	case VCP_ID:
> +		return vcp->vcp_cluster->vcp_ready[VCP_ID];
> +	case MMUP_ID:
> +		return vcp->vcp_cluster->vcp_ready[MMUP_ID];
> +	case VCP_CORE_TOTAL:
> +	default:
> +		return vcp->vcp_cluster->vcp_ready[VCP_ID] &
> +		       vcp->vcp_cluster->vcp_ready[MMUP_ID];
> +	}
> +}
> +
> +u32 wait_core_hart_shutdown(struct mtk_vcp_device *vcp,
> +			    enum vcp_core_id core_id)
> +{
> +	u32 retry;
> +	bool twohart_support;
> +	u32 core_hart0;
> +	u32 core_hart1;

Not sure why you're not using readl_poll_timeout() or readx_poll_timeout().

You really should.

> +
> +	twohart_support = vcp->vcp_cluster->twohart[core_id];
> +
> +	for (retry = VCP_AWAKE_TIMEOUT; retry > 0; retry--) {
> +		switch (core_id) {
> +		case VCP_ID:
> +			core_hart0 = readl(vcp->vcp_cluster->cfg + VCP_C0_GPR5_H0_REBOOT);
> +			if (twohart_support)
> +				core_hart1 = readl(vcp->vcp_cluster->cfg + VCP_C0_GPR6_H1_REBOOT);
> +			break;
> +		case MMUP_ID:
> +			core_hart0 = readl(vcp->vcp_cluster->cfg + VCP_C1_GPR5_H0_REBOOT);
> +			if (twohart_support)
> +				core_hart1 = readl(vcp->vcp_cluster->cfg + VCP_C1_GPR6_H1_REBOOT);
> +			break;
> +		case VCP_CORE_TOTAL:
> +		default:
> +			break;
> +		}
> +
> +		if (twohart_support) {
> +			if (core_hart0 == CORE_RDY_TO_REBOOT &&
> +			    core_hart1 == CORE_RDY_TO_REBOOT)
> +				break;
> +		} else {
> +			if (core_hart0 == CORE_RDY_TO_REBOOT)
> +				break;
> +		}
> +		usleep_range(USDELAY_RANGE_MIN, USDELAY_RANGE_MAX);
> +	}
> +
> +	return retry;
> +}
> +

static void vcp_set_carveouts(struct mtk_vcp_of_cluster *vcp_cluster,
			      u32 gpr1_offset, u32 gpr2_offset)
{
	/* Write VCP reserved memory address/size to GPR1/GPR2 to setup VCP MPU */
	writel((u32)VCP_PACK_IOVA(vcp_cluster->share_mem_iova), vcp_cluster->cfg + 
gpr1_offset);
	writel((u32)vcp_cluster->share_mem_size, vcp_cluster->cfg + gpr2_offset);
}

static int reset_vcp(struct mtk_vcp_device *vcp)
{
	if (vcp->cluster->core_nums >= MMUP_ID) {
		vcp_set_carveouts(vcp->cluster, VCP_C1_GPR1_DRAM_RESV_ADDR,
				  VCP_C2_GPR2_DRAM_RESV_SIZE);
		arm_smccc_smc(.....)
	}

	vcp_set_carveouts(....)
	arm_smccc_smc(....)

	return 0;
}

looks better, but that's only for readability - not everyone likes that anyway
so you're free to do it like that or just cleanup the commits in your own version
of reset_vcp().

> +static int reset_vcp(struct mtk_vcp_device *vcp)
> +{
> +	struct arm_smccc_res res;
> +
> +	if (vcp->vcp_cluster->core_nums >= MMUP_ID) {
> +		/* write vcp reserved memory address/size to GRP1/GRP2
> +		 * to let vcp setup MPU
> +		 */
> +		writel((u32)VCP_PACK_IOVA(vcp->vcp_cluster->share_mem_iova),
> +		       vcp->vcp_cluster->cfg + VCP_C1_GPR1_DRAM_RESV_ADDR);
> +		writel((u32)vcp->vcp_cluster->share_mem_size,
> +		       vcp->vcp_cluster->cfg + VCP_C1_GPR2_DRAM_RESV_SIZE);
> +
> +		arm_smccc_smc(MTK_SIP_TINYSYS_VCP_CONTROL,
> +			      MTK_TINYSYS_MMUP_KERNEL_OP_RESET_RELEASE,
> +			      1, 0, 0, 0, 0, 0, &res);
> +	}
> +
> +	/* write vcp reserved memory address/size to GRP1/GRP2
> +	 * to let vcp setup MPU
> +	 */
> +	writel((u32)VCP_PACK_IOVA(vcp->vcp_cluster->share_mem_iova),
> +	       vcp->vcp_cluster->cfg + VCP_C0_GPR1_DRAM_RESV_ADDR);
> +	writel((u32)vcp->vcp_cluster->share_mem_size,
> +	       vcp->vcp_cluster->cfg + VCP_C0_GPR2_DRAM_RESV_SIZE);
> +
> +	arm_smccc_smc(MTK_SIP_TINYSYS_VCP_CONTROL,
> +		      MTK_TINYSYS_VCP_KERNEL_OP_RESET_RELEASE,
> +		      1, 0, 0, 0, 0, 0, &res);
> +	return 0;
> +}
> +
> +static int vcp_enable_pm_clk(struct mtk_vcp_device *vcp, enum feature_id id)
> +{
> +	mutex_lock(&vcp->vcp_cluster->vcp_pw_clk_mutex);
> +	if (vcp->vcp_cluster->pwclkcnt == 0) {
> +		if (!is_vcp_ready_by_coreid(VCP_CORE_TOTAL)) {
> +			if (reset_vcp(vcp)) {
> +				mutex_unlock(&vcp->vcp_cluster->vcp_pw_clk_mutex);
> +				return -EINVAL;
> +			}
> +		}
> +	}
> +	vcp->vcp_cluster->pwclkcnt++;
> +	mutex_unlock(&vcp->vcp_cluster->vcp_pw_clk_mutex);
> +
> +	return 0;
> +}
> +
> +static int vcp_disable_pm_clk(struct mtk_vcp_device *vcp, enum feature_id id)
> +{
> +	mutex_lock(&vcp->vcp_cluster->vcp_pw_clk_mutex);
> +	vcp->vcp_cluster->pwclkcnt--;
> +	if (vcp->vcp_cluster->pwclkcnt < 0) {
> +		for (u32 i = 0; i < NUM_FEATURE_ID; i++)
> +			dev_warn(vcp->dev, "%s Check feature id %d enable cnt %d\n",
> +				 __func__, feature_table[i].feature, feature_table[i].enable);
> +		vcp->vcp_cluster->pwclkcnt = 0;
> +		return -EINVAL;
> +	}
> +	mutex_unlock(&vcp->vcp_cluster->vcp_pw_clk_mutex);
> +
> +	return 0;
> +}
> +
> +int vcp_A_register_feature(struct mtk_vcp_device *vcp, enum feature_id id)
> +{
> +	int ret;
> +
> +	if (id >= NUM_FEATURE_ID) {
> +		dev_err(vcp->dev, "%s unsupported feature id %d\n",
> +			__func__, id);
> +		return -EINVAL;
> +	}
> +
> +	mutex_lock(&vcp->vcp_cluster->vcp_feature_mutex);
> +	for (u32 i = 0; i < NUM_FEATURE_ID; i++) {
> +		if (feature_table[i].feature == id)
> +			feature_table[i].enable++;
> +		if (id == RTOS_FEATURE_ID)
> +			feature_table[i].priv = vcp->dev;
> +	}
> +	ret = vcp_enable_pm_clk(vcp, id);
> +	mutex_unlock(&vcp->vcp_cluster->vcp_feature_mutex);
> +
> +	return ret;
> +}
> +
> +int vcp_A_deregister_feature(struct mtk_vcp_device *vcp, enum feature_id id)
> +{
> +	int ret;
> +
> +	if (id >= NUM_FEATURE_ID) {
> +		dev_warn(vcp->dev, "%s unsupported feature id %d\n",
> +			 __func__, id);
> +		return -EINVAL;
> +	}
> +	mutex_lock(&vcp->vcp_cluster->vcp_feature_mutex);
> +	for (u32 i = 0; i < NUM_FEATURE_ID; i++) {

if (id != feature_table[i].feature)
	continue;

if (feature_table[i].enable == 0) {
	dev_warn .... etc etc
}

feature_table[i].enable--;

> +		if (feature_table[i].feature == id) {
> +			if (feature_table[i].enable == 0) {
> +				dev_warn(vcp->dev, "%s unbalanced feature id %d enable cnt %d\n",
> +					 __func__, id, feature_table[i].enable);
> +				mutex_unlock(&vcp->vcp_cluster->vcp_feature_mutex);
> +				return -EINVAL;
> +			}
> +			feature_table[i].enable--;
> +		}
> +	}
> +	ret = vcp_disable_pm_clk(vcp, id);
> +	mutex_unlock(&vcp->vcp_cluster->vcp_feature_mutex);
> +
> +	return ret;
> +}
> +
> +static size_t load_part_binary(void __iomem *image_buf,
> +			       const u8 *fw_src,
> +			       size_t size,
> +			       const char *part_bin_name)
> +{
> +	const u8 *fw_ptr = fw_src;
> +	u32 offset;
> +	u32 align_size;
> +	const struct mkimg_hdr *img_hdr_info;
> +
> +	if (!fw_src || !image_buf || size < VCP_IMAGE_HEADER_SIZE)
> +		return 0;
> +
> +	offset = 0;
> +	while (offset < size) {
> +		img_hdr_info = (const struct mkimg_hdr *)(fw_ptr + offset);
> +		align_size = round_up(img_hdr_info->dsz, ALIGN_16);
> +		offset += VCP_IMAGE_HEADER_SIZE;
> +		if (img_hdr_info->magic != VCM_IMAGE_MAGIC ||
> +		    strncmp(img_hdr_info->name, part_bin_name, VCM_IMAGE_NAME_MAXSZ - 1)) {
> +			offset += align_size;
> +		} else {
> +			memcpy_toio(image_buf, fw_ptr + offset, img_hdr_info->dsz);
> +			offset += align_size;
> +			return img_hdr_info->dsz;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int load_vcp_bin(const u8 *fw_src,

load_vcp_bin() and load_mmup_bin() have got a lot of code in common, I think you
can reduce the amount of code a bit here by writing a function that commonizes
the loading process.

I can see a pattern, at least - and looks like being the same one between the two.

> +			size_t size,
> +			void __iomem *img_buf_va,
> +			phys_addr_t img_buf_pa,
> +			dma_addr_t img_buf_iova,
> +			struct mtk_vcp_device *vcp)
> +{
> +	u32 fw_size;
> +	u32 dram_img_size;
> +	u32 dram_backup_img_offset;
> +	struct vcp_region_info_st vcp_region_info = {};
> +	struct arm_smccc_res res;
> +
> +	fw_size = load_part_binary(vcp->vcp_cluster->sram_base +
> +				   vcp->vcp_cluster->sram_offset[VCP_ID],
> +				   fw_src, size, VCP_HFRP_PART_NAME);
> +	if (!fw_size) {
> +		dev_err(vcp->dev, "load %s failed\n", VCP_HFRP_PART_NAME);
> +		return -EINVAL;
> +	}
> +
> +	dram_img_size = load_part_binary(img_buf_va + VCP_DRAM_IMG_OFFSET,
> +					 fw_src, size, VCP_HFRP_DRAM_PART_NAME);
> +	if (!dram_img_size) {
> +		dev_err(vcp->dev, "load %s failed\n", VCP_HFRP_DRAM_PART_NAME);
> +		return -EINVAL;
> +	}
> +
> +	vcp_region_info.struct_size = sizeof(struct vcp_region_info_st);
> +
> +	dram_backup_img_offset = VCP_DRAM_IMG_OFFSET + round_up(dram_img_size, ALIGN_1024);
> +
> +	vcp_region_info.ap_dram_start = VCP_PACK_IOVA(img_buf_iova + VCP_DRAM_IMG_OFFSET);
> +	vcp_region_info.ap_dram_backup_start = VCP_PACK_IOVA(img_buf_iova + dram_backup_img_offset);
> +	vcp_region_info.ap_dram_size = dram_img_size;
> +
> +	vcp_region_info.l2tcm_offset = vcp->vcp_cluster->sram_offset[MMUP_ID];
> +
> +	memcpy_toio(vcp->vcp_cluster->sram_base +
> +		    vcp->vcp_cluster->sram_offset[VCP_ID] + REGION_OFFSET,
> +		    &vcp_region_info, sizeof(vcp_region_info));
> +
> +	arm_smccc_smc(MTK_SIP_TINYSYS_VCP_CONTROL,
> +		      MTK_TINYSYS_MMUP_KERNEL_OP_SET_L2TCM_OFFSET,
> +		      vcp->vcp_cluster->sram_offset[MMUP_ID],
> +		      0, 0, 0, 0, 0, &res);
> +
> +	return 0;
> +}
> +
> +static int load_mmup_bin(const u8 *fw_src,
> +			 size_t size,
> +			 void __iomem *img_buf_va,
> +			 phys_addr_t img_buf_pa,
> +			 dma_addr_t img_buf_iova,
> +			 struct mtk_vcp_device *vcp)
> +{
> +	u32 fw_size;
> +	u32 dram_img_size;
> +	u32 dram_backup_img_offset;
> +	struct vcp_region_info_st vcp_region_info = {};
> +	struct arm_smccc_res res;
> +
> +	fw_size = load_part_binary(vcp->vcp_cluster->sram_base +
> +				   vcp->vcp_cluster->sram_offset[MMUP_ID],
> +				   fw_src, size, VCP_MMUP_PART_NAME);
> +	if (!fw_size) {
> +		dev_err(vcp->dev, "load %s failed\n", VCP_MMUP_PART_NAME);
> +		return -EINVAL;
> +	}
> +
> +	dram_img_size = load_part_binary(img_buf_va + MMUP_DRAM_IMG_OFFSET, fw_src, size,
> +					 VCP_MMUP_DRAM_PART_NAME);
> +	if (!dram_img_size) {
> +		dev_err(vcp->dev, "load %s failed\n", VCP_MMUP_DRAM_PART_NAME);
> +		return -EINVAL;
> +	}
> +
> +	vcp_region_info.struct_size = sizeof(struct vcp_region_info_st);
> +
> +	dram_backup_img_offset = MMUP_DRAM_IMG_OFFSET + round_up(dram_img_size, ALIGN_1024);
> +	vcp_region_info.ap_dram_start = VCP_PACK_IOVA(img_buf_iova + MMUP_DRAM_IMG_OFFSET);
> +	vcp_region_info.ap_dram_backup_start = VCP_PACK_IOVA(img_buf_iova + dram_backup_img_offset);
> +	vcp_region_info.ap_dram_size = dram_img_size;
> +
> +	memcpy_toio(vcp->vcp_cluster->sram_base +
> +		    vcp->vcp_cluster->sram_offset[MMUP_ID] + REGION_OFFSET,
> +		    &vcp_region_info, sizeof(vcp_region_info));
> +
> +	arm_smccc_smc(MTK_SIP_TINYSYS_VCP_CONTROL,
> +		      MTK_TINYSYS_MMUP_KERNEL_OP_SET_FW_SIZE,
> +		      fw_size, 0, 0, 0, 0, 0, &res);
> +
> +	return 0;
> +}
> +
> +int mtk_vcp_load(struct rproc *rproc, const struct firmware *fw)
> +{
> +	struct arm_smccc_res res;
> +	struct mtk_vcp_device *vcp = (struct mtk_vcp_device *)rproc->priv;
> +	dma_addr_t img_buf_iova;
> +	phys_addr_t img_buf_phys;
> +	void __iomem *img_buf_va;
> +	int ret;
> +
> +	if (!vcp) {
> +		dev_err(vcp->dev, "vcp device is no exist!\n");
> +		return -EINVAL;
> +	}
> +
> +	if (fw->size < VCP_IMAGE_HEADER_SIZE ||
> +	    fw->size > vcp->ops->vcp_get_mem_size(VCP_RTOS_MEM_ID)) {
> +		dev_err(vcp->dev, "firmware is oversize/undersize\n");
> +		return -EINVAL;
> +	}
> +
> +	writel(0x1, vcp->vcp_cluster->cfg_core + VCP_R_CORE0_SW_RSTN_SET);
> +	writel(0x1, vcp->vcp_cluster->cfg_core + VCP_R_CORE1_SW_RSTN_SET);
> +
> +	memset_io(vcp->vcp_cluster->sram_base, 0, vcp->vcp_cluster->sram_size);
> +
> +	img_buf_iova = vcp->ops->vcp_get_mem_iova(VCP_RTOS_MEM_ID);
> +	img_buf_phys = vcp->ops->vcp_get_mem_phys(VCP_RTOS_MEM_ID);
> +	img_buf_va = vcp->ops->vcp_get_mem_virt(VCP_RTOS_MEM_ID);
> +
> +	arm_smccc_smc(MTK_SIP_TINYSYS_VCP_CONTROL,
> +		      MTK_TINYSYS_VCP_KERNEL_OP_COLD_BOOT_VCP,
> +		      0, 0, 0, 0, 0, 0, &res);
> +
> +	ret = load_vcp_bin(fw->data, fw->size,
> +			   img_buf_va, img_buf_phys,
> +			   img_buf_iova, vcp);
> +	if (ret) {
> +		dev_err(vcp->dev, "load vcp bin failed\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = load_mmup_bin(fw->data, fw->size,
> +			    img_buf_va, img_buf_phys,
> +			    img_buf_iova, vcp);
> +	if (ret) {
> +		dev_err(vcp->dev, "load mmup bin failed\n");
> +		return -EINVAL;
> +	}
> +
> +	return ret;
> +}
> +
> +static irqreturn_t vcp_irq_handler(int irq, void *priv)
> +{
> +	u32 reg0, reg1;
> +	struct mtk_vcp_device *vcp = priv;
> +
> +	disable_irq_nosync(irq);

You're disabling the irq here, but I don't see where you re-enable it?

> +
> +	reg0 = readl(vcp->vcp_cluster->cfg_core + R_CORE0_WDT_IRQ);
> +	reg1 = vcp->vcp_cluster->core_nums > VCP_ID ?
> +	       readl(vcp->vcp_cluster->cfg_core + R_CORE1_WDT_IRQ) : 0;
> +
> +	if (reg0)
> +		writel(B_WDT_IRQ, vcp->vcp_cluster->cfg_core + R_CORE0_WDT_IRQ);
> +	if (reg1)
> +		writel(B_WDT_IRQ, vcp->vcp_cluster->cfg_core + R_CORE1_WDT_IRQ);
> +
> +	if (reg0 || reg1)
> +		return IRQ_HANDLED;
> +
> +	return IRQ_NONE;
> +}
> +
> +int vcp_wdt_irq_init(struct mtk_vcp_device *vcp)
> +{
> +	int ret;
> +
> +	ret = devm_request_irq(vcp->dev, platform_get_irq(vcp->pdev, 0),
> +			       vcp_irq_handler, IRQF_ONESHOT,
> +			       vcp->pdev->name, vcp);
> +	if (ret)
> +		dev_err(vcp->dev, "failed to request wdt irq\n");
> +
> +	return ret;
> +}
> +
> +MODULE_AUTHOR("Xiangzhi Tang <xiangzhi.tang@mediatek.com>");
> +MODULE_DESCRIPTION("MTK VCP Controller");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/remoteproc/mtk_vcp_common.h b/drivers/remoteproc/mtk_vcp_common.h
> new file mode 100644
> index 000000000000..8340f0bd4fdc
> --- /dev/null
> +++ b/drivers/remoteproc/mtk_vcp_common.h
> @@ -0,0 +1,225 @@
> +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
> +/*
> + * Copyright (c) 2025 MediaTek Inc.
> + */
> +
> +#ifndef __MTK_VCP_COMMON_H
> +#define __MTK_VCP_COMMON_H
> +
> +#include <linux/arm-smccc.h>
> +#include <linux/bitops.h>
> +#include <linux/firmware.h>
> +#include <linux/soc/mediatek/mtk_sip_svc.h>
> +#include <linux/remoteproc/mtk_vcp_public.h>
> +
> +/* vcp timeout definition */
> +#define VCP_AWAKE_TIMEOUT 1000
> +#define USDELAY_RANGE_MIN 1000
> +#define USDELAY_RANGE_MAX 2000
> +
> +/* vcp platform define */
> +#define DMA_MAX_MASK_BIT 33
> +
> +/* vcp load image define */
> +#define VCM_IMAGE_MAGIC             (0x58881688)

You don't need to enclose those in ().

> +#define VCM_IMAGE_NAME_MAXSZ        (32)
> +#define VCP_IMAGE_HEADER_SIZE       (0x200)
> +
> +#define VCP_DRAM_IMG_OFFSET         (0x200000)
> +#define MMUP_DRAM_IMG_OFFSET        (0x1200000)
> +
> +#define REGION_OFFSET               (0x4)
> +#define ALIGN_1024                  (1024)
> +#define ALIGN_16                    (16)
> +#define VCP_HFRP_PART_NAME          "tinysys-vcp-RV55_A"
> +#define VCP_MMUP_PART_NAME          "tinysys-mmup-RV33_A"
> +#define VCP_HFRP_DRAM_PART_NAME     "tinysys-vcp-RV55_A_dram"
> +#define VCP_MMUP_DRAM_PART_NAME     "tinysys-mmup-RV33_A_dram"
> +
> +/* vcp memory iove pack convert define */
> +#define VCP_PACK_IOVA(addr)     ((uint32_t)((addr) | (((uint64_t)(addr) >> 32) & 0xF)))



> +#define VCP_UNPACK_IOVA(addr)   \
> +	((uint64_t)(addr & 0xFFFFFFF0) | (((uint64_t)(addr) & 0xF) << 32))
> +
> +/* vcp register define */
> +#define VCP_R_CORE0_SW_RSTN_SET         (0x0004)
> +#define VCP_R_CORE1_SW_RSTN_SET         (0x000C)

lower case hex please.

> +#define R_GIPC_IN_SET                   (0x0028)
> +#define R_GIPC_IN_CLR                   (0x002C)
> +#define GIPC_MMUP_SHUT                  (BIT(10))
> +#define GIPC_VCP_HART0_SHUT             (BIT(14))
> +#define B_GIPC4_SETCLR_3                (BIT(19))
> +#define R_CORE0_WDT_IRQ                 (0x0050)
> +#define R_CORE1_WDT_IRQ                 (0x0054)
> +#define B_WDT_IRQ                       (BIT(0))
> +#define AP_R_GPR2                       (0x0068)
> +#define B_CORE0_SUSPEND                 (BIT(0))
> +#define B_CORE0_RESUME                  (BIT(1))
> +#define AP_R_GPR3                       (0x006C)
> +#define B_CORE1_SUSPEND                 (BIT(0))
> +#define B_CORE1_RESUME                  (BIT(1))
> +
> +#define R_CORE0_STATUS                  (0x6070)
> +#define B_CORE_GATED                    (BIT(0))
> +#define B_HART0_HALT                    (BIT(1))
> +#define B_HART1_HALT                    (BIT(2))
> +#define B_CORE_AXIS_BUSY                (BIT(4))
> +#define R_CORE1_STATUS                  (0x9070)
> +#define VCP_C0_GPR0_SUSPEND_RESUME_FLAG (0x6040)
> +#define VCP_C0_GPR1_DRAM_RESV_ADDR      (0x6044)
> +#define VCP_C0_GPR2_DRAM_RESV_SIZE      (0x6048)
> +#define VCP_C0_GPR3_DRAM_RESV_LOGGER    (0x604C)
> +#define VCP_C0_GPR5_H0_REBOOT           (0x6054)
> +#define CORE_RDY_TO_REBOOT              (0x0034)
> +#define VCP_C0_GPR6_H1_REBOOT           (0x6058)
> +#define VCP_C1_GPR0_SUSPEND_RESUME_FLAG (0x9040)
> +#define VCP_C1_GPR1_DRAM_RESV_ADDR      (0x9044)
> +#define VCP_C1_GPR2_DRAM_RESV_SIZE      (0x9048)
> +#define VCP_C1_GPR3_DRAM_RESV_LOGGER    (0x904C)
> +#define VCP_C1_GPR5_H0_REBOOT           (0x9054)
> +#define VCP_C1_GPR6_H1_REBOOT           (0x9058)
> +
> +/* sec GPR */
> +#define R_GPR2_SEC                      (0x0008)
> +#define MMUP_AP_SUSPEND                 (BIT(0))
> +#define R_GPR3_SEC                      (0x000C)
> +#define VCP_AP_SUSPEND                  (BIT(0))
> +
> +/* vcp rdy */
> +#define VLP_AO_RSVD7                    (0x0000)
> +#define READY_BIT                       (BIT(1))
> +
> +/* vcp Core ID definition */

This comment doesn't add any valuable information to this enum: either write
something that adds it, or just remove the comment.

> +enum vcp_core_id {
> +	VCP_ID          = 0,
> +	MMUP_ID         = 1,
> +	VCP_CORE_TOTAL  = 2,
> +};
> +
> +/* vcp kernel smc server id */
> +enum mtk_tinysys_vcp_kernel_op {
> +	MTK_TINYSYS_VCP_KERNEL_OP_RESET_SET = 0,
> +	MTK_TINYSYS_VCP_KERNEL_OP_RESET_RELEASE,
> +	MTK_TINYSYS_VCP_KERNEL_OP_COLD_BOOT_VCP,
> +	MTK_TINYSYS_MMUP_KERNEL_OP_RESET_SET,
> +	MTK_TINYSYS_MMUP_KERNEL_OP_RESET_RELEASE,
> +	MTK_TINYSYS_MMUP_KERNEL_OP_SET_L2TCM_OFFSET,
> +	MTK_TINYSYS_MMUP_KERNEL_OP_SET_FW_SIZE,
> +	MTK_TINYSYS_MMUP_KERNEL_OP_COLD_BOOT_MMUP,
> +	MTK_TINYSYS_VCP_KERNEL_OP_NUM,
> +};
> +
> +/**
> + * struct mkimg_hdr - mtk image header format.
> + *
> + * @magic: mtk vcp image magic id
> + * @dsz: mtk vcp image part binary size
> + * @name: mtk vcp image part binary parttion name
> + */
> +struct mkimg_hdr {

struct mtk_vcp_img_header {

> +	u32 magic;
> +	u32 dsz;
> +	char name[VCM_IMAGE_NAME_MAXSZ];
> +};
> +
> +/**
> + * struct vcp_feature_tb - feature table structure definition.
> + *
> + * @feature: feature id
> + * @core_id: feature using vcp core id
> + * @enable: whether the feature is enabled or not

priv must also be documented.

> + */
> +struct vcp_feature_tb {
> +	enum vcp_core_id core_id;
> +	u32 feature;
> +	u32 enable;
> +	void *priv;
> +};
> +
> +/**
> + * struct vcp_reserve_mblock - vcp reserved memory structure.
> + *
> + * @vcp_reserve_mem_id_t: reserved memory id
> + * @start_phys: reserved memory phy addr
> + * @start_iova: reserved memory dma map addr
> + * @start_virt: reserved memory CPU virt addr
> + * @size: reserved memory size
> + */
> +struct vcp_reserve_mblock {
> +	enum vcp_reserve_mem_id_t num;
> +	phys_addr_t start_phys;
> +	dma_addr_t start_iova;
> +	void __iomem *start_virt;
> +	size_t size;
> +};
> +
> +/**
> + * struct vcp_region_info_st - config vcp image info sync to vcp bootloader.
> + *
> + * @ap_loader_start: (optional) - config vcp bootloader to copy loader start addr
> + * @ap_loader_size: (optional) - config vcp bootloader to copy loader size
> + * @ap_firmware_start: (optional) - config vcp bootloader to copy firmware start addr
> + * @ap_firmware_size: (optional) - config vcp bootloader to copy firmware size
> + * @ap_dram_start: config vcp run dram binary start addr
> + * @ap_dram_size: config vcp run dram binary size
> + * @ap_dram_backup_start: config vcp backup dram binary start addr
> + * @struct_size: vcp image region info structure size
> + * @l2tcm_offset: vcp two core using l2sram layout
> + * @TaskContext_ptr: (optional) - vcp task context ptr for debug
> + * @vcpctl:  (optional) - vcp control info
> + * @regdump_start: (optional) - regdump start addr for debug
> + * @regdump_size: (optional) - regdump size for debug
> + * @ap_params_start: (optional) - params start addr
> + * @sramlog_buf_offset: (optional) - sramlog_buf_offset for debug
> + * @sramlog_end_idx_offset: (optional) - sramlog_end_idx_offset for debug
> + * @sramlog_buf_maxlen: (optional) - sramlog_buf_maxlen for debug
> + * @ap_loader_start_pa: (optional) - config vcp bootloader for loader start pa
> + * @coredump_offset: (optional) - coredump_offset offset for debug
> + * @coredump_dram_offset: (optional) - coredump_dram_offset offset for debug
> + */
> +struct vcp_region_info_st {
> +	u32 ap_loader_start;
> +	u32 ap_loader_size;
> +	u32 ap_firmware_start;
> +	u32 ap_firmware_size;
> +	u32 ap_dram_start;
> +	u32 ap_dram_size;
> +	u32 ap_dram_backup_start;
> +	u32 struct_size;
> +	u32 l2tcm_offset;
> +	u32 TaskContext_ptr;

Lowercase please.

> +	u32 vcpctl;
> +	u32 regdump_start;
> +	u32 regdump_size;
> +	u32 ap_params_start;
> +	u32 sramlog_buf_offset;
> +	u32 sramlog_end_idx_offset;
> +	u32 sramlog_buf_maxlen;
> +	u32 ap_loader_start_pa;
> +	u32 coredump_offset;
> +	u32 coredump_dram_offset;
> +};
> +
> +/* vcp common reserved memory APIs */
> +int vcp_reserve_memory_ioremap(struct mtk_vcp_device *vcp);
> +phys_addr_t vcp_get_reserve_mem_phys(enum vcp_reserve_mem_id_t id);
> +dma_addr_t vcp_get_reserve_mem_iova(enum vcp_reserve_mem_id_t id);
> +void __iomem *vcp_get_reserve_mem_virt(enum vcp_reserve_mem_id_t id);
> +void __iomem *vcp_get_internal_sram_virt(struct mtk_vcp_device *vcp);
> +u32 vcp_get_reserve_mem_size(enum vcp_reserve_mem_id_t id);
> +
> +/* vcp common load image API */
> +int mtk_vcp_load(struct rproc *rproc, const struct firmware *fw);
> +
> +/* vcp common wdt irq init API */
> +int vcp_wdt_irq_init(struct mtk_vcp_device *vcp);
> +
> +/* vcp common feature register/deregister APIs */
> +int vcp_A_register_feature(struct mtk_vcp_device *vcp,
> +			   enum feature_id id);
> +int vcp_A_deregister_feature(struct mtk_vcp_device *vcp,
> +			     enum feature_id id);
> +
> +/* vcp common core hart shutdown API */
> +u32 wait_core_hart_shutdown(struct mtk_vcp_device *vcp, enum vcp_core_id core_id);
> +#endif
> diff --git a/drivers/remoteproc/mtk_vcp_rproc.c b/drivers/remoteproc/mtk_vcp_rproc.c
> new file mode 100644
> index 000000000000..bf4736ce6795
> --- /dev/null
> +++ b/drivers/remoteproc/mtk_vcp_rproc.c
> @@ -0,0 +1,326 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2025 MediaTek Corporation. All rights reserved.
> + */
> +
> +#include <linux/device.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/remoteproc.h>
> +
> +#include "mtk_vcp_common.h"
> +#include "mtk_vcp_rproc.h"
> +#include "remoteproc_internal.h"
> +
> +/**
> + * vcp_get() - get a reference to VCP.
> + *
> + * @pdev: the platform device of the module requesting VCP platform
> + *        device for using VCP API.
> + *
> + * Return: Return NULL if failed.  otherwise reference to VCP.
> + **/
> +struct mtk_vcp_device *vcp_get(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *vcp_node;
> +	struct platform_device *vcp_pdev;
> +
> +	vcp_node = of_parse_phandle(dev->of_node, "mediatek,vcp", 0);
> +	if (!vcp_node) {
> +		dev_err(dev, "can't get VCP node\n");
> +		return NULL;
> +	}
> +
> +	vcp_pdev = of_find_device_by_node(vcp_node);
> +	of_node_put(vcp_node);
> +
> +	if (WARN_ON(!vcp_pdev)) {
> +		dev_err(dev, "VCP pdev failed\n");
> +		return NULL;
> +	}
> +
> +	return platform_get_drvdata(vcp_pdev);
> +}
> +EXPORT_SYMBOL_GPL(vcp_get);
> +
> +/**
> + * vcp_put() - "free" the VCP
> + *
> + * @vcp: mtk_vcp_device structure from vcp_get().
> + *
> + **/
> +void vcp_put(struct mtk_vcp_device *vcp)
> +{
> +	put_device(vcp->dev);
> +}
> +EXPORT_SYMBOL_GPL(vcp_put);
> +
> +static int mtk_vcp_start(struct rproc *rproc)
> +{
> +	struct mtk_vcp_device *vcp = (struct mtk_vcp_device *)rproc->priv;
> +	struct arm_smccc_res res;
> +
> +	/* core 0 */
> +	arm_smccc_smc(MTK_SIP_TINYSYS_VCP_CONTROL,
> +		      MTK_TINYSYS_VCP_KERNEL_OP_RESET_SET,
> +		      1, 0, 0, 0, 0, 0, &res);
> +
> +	/* core 1 */
> +	arm_smccc_smc(MTK_SIP_TINYSYS_VCP_CONTROL,
> +		      MTK_TINYSYS_MMUP_KERNEL_OP_RESET_SET,
> +		      1, 0, 0, 0, 0, 0, &res);
> +
> +	if (vcp_A_register_feature(vcp, RTOS_FEATURE_ID) < 0) {
> +		dev_err(vcp->dev, "bootup fail\n");

If there's an error, you should really return an error here.

> +		vcp_A_deregister_feature(vcp, RTOS_FEATURE_ID);
> +	} else {
> +		dev_info(vcp->dev, "bootup successfully\n");
> +	}
> +
> +	return 0;
> +}
> +
> +static int mtk_vcp_stop(struct rproc *rproc)
> +{
> +	struct mtk_vcp_device *vcp = (struct mtk_vcp_device *)rproc->priv;
> +
> +	vcp_A_deregister_feature(vcp, RTOS_FEATURE_ID);
> +
> +	return 0;
> +}
> +
> +static const struct rproc_ops mtk_vcp_ops = {
> +	.load		= mtk_vcp_load,
> +	.start		= mtk_vcp_start,
> +	.stop		= mtk_vcp_stop,
> +};
> +
> +static int vcp_multi_core_init(struct platform_device *pdev,
> +			       struct mtk_vcp_of_cluster *vcp_cluster,
> +			       enum vcp_core_id core_id)
> +{
> +	int ret;
> +
> +	ret = of_property_read_u32(pdev->dev.of_node, "mtk,vcp-core-twohart",

Apparently, twohart is present:
  1. If the HW is multi-core; and
  2. If this is the first core of the multi-core cluster.

This means that you don't need a devicetree property for that; you can either
hardcode this logic, or you can use per-SoC platform data.

> +				   &vcp_cluster->twohart[core_id]);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to twohart property\n");
> +		return ret;
> +	}
> +	ret = of_property_read_u32(pdev->dev.of_node, "mtk,core-sram-offset",
> +				   &vcp_cluster->sram_offset[core_id]);

As I said in the bindings review, you don't need this property.

> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to sram-offset property\n");
> +		return ret;
> +	}
> +
> +	return ret;
> +}
> +
> +static struct mtk_vcp_device *vcp_rproc_init(struct platform_device *pdev,
> +					     struct mtk_vcp_of_cluster *vcp_cluster)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *np = dev_of_node(dev);
> +	struct device_node *child;
> +	struct platform_device *cpdev;
> +	struct mtk_vcp_device *vcp;
> +	struct rproc *rproc;
> +	const struct mtk_vcp_of_data *vcp_of_data;
> +	u32 core_id;
> +	int ret;
> +
> +	vcp_of_data = of_device_get_match_data(dev);
> +	rproc = devm_rproc_alloc(dev, np->name, &mtk_vcp_ops,
> +				 vcp_of_data->platdata.fw_name,
> +				 sizeof(struct mtk_vcp_device));
> +	if (!rproc) {
> +		dev_err(dev, "unable to allocate remoteproc\n");
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	vcp  = rproc->priv;
> +	vcp->rproc = rproc;
> +	vcp->pdev = pdev;
> +	vcp->dev = dev;
> +	vcp->ops = &vcp_of_data->ops;
> +	vcp->platdata = &vcp_of_data->platdata;
> +	vcp->vcp_cluster = vcp_cluster;
> +
> +	rproc->auto_boot = vcp_of_data->platdata.auto_boot;
> +	rproc->sysfs_read_only = vcp_of_data->platdata.sysfs_read_only;
> +	mutex_init(&vcp->vcp_cluster->vcp_feature_mutex);
> +	mutex_init(&vcp->vcp_cluster->vcp_pw_clk_mutex);
> +	platform_set_drvdata(pdev, vcp);
> +
> +	ret = vcp_reserve_memory_ioremap(vcp);
> +	if (ret) {
> +		dev_err(dev, "vcp_reserve_memory_ioremap failed ret = %d\n", ret);
> +		return ERR_PTR(ret);
> +	}
> +
> +	core_id = 0;
> +	for_each_available_child_of_node(np, child) {
> +		if (of_device_is_compatible(child, "mediatek,vcp-core")) {
> +			cpdev = of_find_device_by_node(child);
> +			if (!cpdev) {
> +				ret = -ENODEV;
> +				dev_err(dev, "Not found platform device for core\n");
> +				return ERR_PTR(ret);
> +			}
> +			ret = vcp_multi_core_init(cpdev, vcp_cluster, core_id);
> +			core_id++;
> +		}
> +	}
> +	vcp->vcp_cluster->core_nums = core_id;
> +
> +	ret = vcp_wdt_irq_init(vcp);
> +	if (ret) {
> +		dev_err(dev, "vcp_wdt_irq_init failed\n");
> +		return ERR_PTR(ret);
> +	}
> +
> +	pm_runtime_get_sync(dev);
> +
> +	return vcp;
> +}
> +
> +static int vcp_cluster_init(struct platform_device *pdev,
> +			    struct mtk_vcp_of_cluster *vcp_cluster)
> +{
> +	struct mtk_vcp_device *vcp;
> +	int ret;
> +
> +	vcp = vcp_rproc_init(pdev, vcp_cluster);
> +	if (IS_ERR(vcp))
> +		return PTR_ERR(vcp);
> +
> +	ret = rproc_add(vcp->rproc);
> +	if (ret) {
> +		dev_err(vcp->dev, "Failed to add rproc\n");
> +		rproc_del(vcp->rproc);
> +	}
> +
> +	return ret;
> +}
> +
> +static int vcp_device_probe(struct platform_device *pdev)
> +{
> +	struct resource *res;
> +	struct device *dev = &pdev->dev;
> +	struct mtk_vcp_of_cluster *vcp_cluster;
> +	int ret;
> +
> +	pm_runtime_enable(dev);

devm_pm_runtime_enable()

> +
> +	vcp_cluster = devm_kzalloc(dev, sizeof(*vcp_cluster), GFP_KERNEL);
> +	if (!vcp_cluster)
> +		return -ENOMEM;
> +
> +	vcp_cluster->cfg = devm_platform_ioremap_resource_byname(pdev, "cfg");

You can apply the resource index-vs-name restriction in the devicetree bindings,
which is something that you have already done, even.

So, since you have that in bindings, you can do...

enum vcp_resources {
	VCP_CFG,
	VCP_CFG_SEC,
	....
	VCP_RESOURCES_MAX
};

enum vcp_resources i;

for (i = 0; i < VCP_RESOURCES_MAX; i++) {
	vcp_cluster->mmio[i] = devm_platform_ioremap_resource(pdev, i);
	if (IS_ERR(vcp_cluster->mmio[i]))
		return dev_err_probe ....
}

If you really want to keep probing them by name (it's slower, but it's okay), you
could add them to an array and still do the same.



> +	if (IS_ERR(vcp_cluster->cfg))
> +		return dev_err_probe(dev, PTR_ERR(vcp_cluster->cfg),
> +				     "Failed to parse and map cfg memory\n");
> +
> +	vcp_cluster->cfg_sec = devm_platform_ioremap_resource_byname(pdev, "cfg_sec");
> +	if (IS_ERR(vcp_cluster->cfg_sec))
> +		return dev_err_probe(dev, PTR_ERR(vcp_cluster->cfg_sec),
> +				     "Failed to parse and map cfg_sec memory\n");
> +
> +	vcp_cluster->cfg_core = devm_platform_ioremap_resource_byname(pdev, "cfg_core");
> +	if (IS_ERR(vcp_cluster->cfg_core))
> +		return dev_err_probe(dev, PTR_ERR(vcp_cluster->cfg_core),
> +				     "Failed to parse and map cfg_core memory\n");
> +
> +	vcp_cluster->vcp_rdy = devm_platform_ioremap_resource_byname(pdev, "vcp_vlp_ao_rsvd7");
> +	if (IS_ERR(vcp_cluster->vcp_rdy))
> +		return dev_err_probe(dev, PTR_ERR(vcp_cluster->vcp_rdy),
> +				     "Failed to parse and map vcp_rdy memory\n");
> +
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram");
> +	vcp_cluster->sram_base = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(vcp_cluster->sram_base))
> +		return dev_err_probe(dev, PTR_ERR(vcp_cluster->sram_base),
> +				     "Failed to parse and map sram memory\n");
> +	vcp_cluster->sram_size = (u32)resource_size(res);
> +
> +	ret = devm_of_platform_populate(dev);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "Failed to populate platform devices\n");
> +
> +	ret = vcp_cluster_init(pdev, vcp_cluster);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static void vcp_device_remove(struct platform_device *pdev)
> +{
> +	struct mtk_vcp_device *vcp = platform_get_drvdata(pdev);
> +
> +	pm_runtime_disable(&pdev->dev);
> +
> +	rproc_del(vcp->rproc);
> +}
> +
> +static void vcp_device_shutdown(struct platform_device *pdev)
> +{
> +	struct mtk_vcp_device *vcp = platform_get_drvdata(pdev);
> +	u32 ret;
> +
> +	writel(GIPC_VCP_HART0_SHUT, vcp->vcp_cluster->cfg_core + R_GIPC_IN_SET);

if (vcp->cluster->core_nums <= VCP_ID)
	return;

(change wait_core_hart_shutdown to return error number for error, or 0 for success)

ret = wait_core_hart_shutdown(...)
if (ret) {
	dev_err(...)
	return;
}

writel(GIPC_MMUP_SHUT ....)


> +	if (vcp->vcp_cluster->core_nums > VCP_ID) {
> +		ret = wait_core_hart_shutdown(vcp, VCP_ID);
> +		if (!ret)
> +			dev_err(&pdev->dev,
> +				"wait VCP_ID core hart shutdown timeout\n");
> +		else
> +			writel(GIPC_MMUP_SHUT, vcp->vcp_cluster->cfg_core + R_GIPC_IN_SET);
> +	}
> +}
> +
> +static const struct mtk_vcp_of_data mt8196_of_data = {
> +	.ops = {
> +		.vcp_register_feature = vcp_A_register_feature,
> +		.vcp_deregister_feature = vcp_A_deregister_feature,

please, lowercase 'A'.

> +		.vcp_get_mem_phys = vcp_get_reserve_mem_phys,
> +		.vcp_get_mem_iova = vcp_get_reserve_mem_iova,
> +		.vcp_get_mem_virt = vcp_get_reserve_mem_virt,
> +		.vcp_get_mem_size = vcp_get_reserve_mem_size,
> +		.vcp_get_sram_virt = vcp_get_internal_sram_virt,
> +	},
> +	.platdata = {
> +		.auto_boot = true,
> +		.sysfs_read_only = true,
> +		.rtos_static_iova = 0x180600000,

Can this parameter be read from the VCP firmware file instead?
It's static....

> +		.fw_name = "mediatek/mt8196/vcp.img",
> +	},
> +};
> +
> +static const struct of_device_id mtk_vcp_of_match[] = {
> +	{ .compatible = "mediatek,mt8196-vcp", .data = &mt8196_of_data},
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, mtk_vcp_of_match);
> +
> +static struct platform_driver mtk_vcp_device = {
> +	.probe = vcp_device_probe,
> +	.remove_new = vcp_device_remove,
> +	.shutdown = vcp_device_shutdown,
> +	.driver = {
> +		.name = "mtk-vcp",
> +		.of_match_table = mtk_vcp_of_match,
> +	},
> +};
> +
> +module_platform_driver(mtk_vcp_device);
> +
> +MODULE_AUTHOR("Xiangzhi Tang <xiangzhi.tang@mediatek.com>");
> +MODULE_DESCRIPTION("MTK VCP Controller");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/remoteproc/mtk_vcp_rproc.h b/drivers/remoteproc/mtk_vcp_rproc.h
> new file mode 100644
> index 000000000000..6c7a99bf919b
> --- /dev/null
> +++ b/drivers/remoteproc/mtk_vcp_rproc.h
> @@ -0,0 +1,71 @@
> +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
> +/*
> + * Copyright (c) 2025 MediaTek Inc.
> + */
> +
> +#ifndef __MTK_VCP_RPROC_H__
> +#define __MTK_VCP_RPROC_H__
> +
> +#include <linux/remoteproc/mtk_vcp_public.h>
> +
> +/*

Oops, you missed a * in /**

> + * struct mtk_vcp_of_cluster - vcp cluster priv data.
> + *
> + * @sram_base: sram_base get from dtb
> + * @cfg: cfg register get from dtb
> + * @cfg_sec: cfg_sec register get from dtb
> + * @cfg_core: cfg_core register get from dtb
> + * @vcp_rdy: vlp vcp_rdy register get from dtb
> + * @sram_size: total sram size get from dtb
> + * @core_nums: total core numbers get from dtb
> + * @twohart: core weo hart support flag
> + * @sram_offset: core sram memory layout
> + * @pwclkcnt: power and clock config count data
> + * @share_mem_iova: shared memory iova base
> + * @share_mem_size: shared memory size
> + * @vcp_feature_mutex: vcp feature register mutex structure
> + * @vcp_pw_clk_mutex: vcp feature lock pw_clk mutex structure
> + */
> +struct mtk_vcp_of_cluster {
> +	void __iomem *sram_base;
> +	void __iomem *cfg;
> +	void __iomem *cfg_sec;
> +	void __iomem *cfg_core;
> +	void __iomem *vcp_rdy;
> +	u32 sram_size;
> +	u32 core_nums;
> +	u32 twohart[VCP_CORE_TOTAL];
> +	u32 sram_offset[VCP_CORE_TOTAL];
> +	int pwclkcnt;
> +	dma_addr_t share_mem_iova;
> +	size_t share_mem_size;
> +	struct mutex vcp_feature_mutex;
> +	struct mutex vcp_pw_clk_mutex;
> +};
> +
> +/**
> + * struct mtk_vcp_platdata - vcp platform priv data.
> + *
> + * @auto_boot: rproc auto_boot flag
> + * @sysfs_read_only: rproc sysfs_read_only flag
> + * @rtos_static_iova: vcp dram binary static map iova
> + * @fw_name: vcp image name and path
> + */
> +struct mtk_vcp_platdata {
> +	bool auto_boot;
> +	bool sysfs_read_only;
> +	dma_addr_t rtos_static_iova;
> +	char *fw_name;
> +};
> +
> +/**
> + * struct mtk_vcp_of_data - const vcp device data.
> + *
> + * @mtk_vcp_ops: mtk_vcp_ops structure
> + * @mtk_vcp_platdata: mtk_vcp_platdata structure
> + */
> +struct mtk_vcp_of_data {
> +	const struct mtk_vcp_ops ops;
> +	const struct mtk_vcp_platdata platdata;
> +};
> +#endif
> diff --git a/include/linux/remoteproc/mtk_vcp_public.h b/include/linux/remoteproc/mtk_vcp_public.h
> new file mode 100644
> index 000000000000..b4de5e5d63d8
> --- /dev/null
> +++ b/include/linux/remoteproc/mtk_vcp_public.h
> @@ -0,0 +1,78 @@
> +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
> +/*
> + * Copyright (c) 2025 MediaTek Inc.
> + */
> +
> +#ifndef __MTK_VCP_PUBLIC_H__
> +#define __MTK_VCP_PUBLIC_H__
> +
> +#include <linux/platform_device.h>
> +#include <linux/remoteproc.h>
> +
> +/* vcp reserve memory ID definition */
> +enum vcp_reserve_mem_id_t {
> +	VCP_RTOS_MEM_ID,
> +	VDEC_MEM_ID,
> +	VENC_MEM_ID,
> +	MMDVFS_VCP_MEM_ID,
> +	MMDVFS_MMUP_MEM_ID,
> +	MMQOS_MEM_ID,
> +	NUMS_MEM_ID,
> +};
> +
> +/* vcp feature ID list */
> +enum feature_id {

enum mtk_vcp_feature_id ?

> +	RTOS_FEATURE_ID,

FEATURE_ID_RTOS,
FEATURE_ID_VDEC,
     .....


Regards,
Angelo




^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2025-09-15  9:14 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-14 12:29 [PATCH v2 0/4] ASoC: mediatek: Add support of VCP on Mediatek mt8196 SoC Xiangzhi Tang
2025-09-14 12:29 ` [PATCH v2 1/4] dt-bindings: remoteproc: Add VCP support for mt8196 Xiangzhi Tang
2025-09-14 13:18   ` Krzysztof Kozlowski
2025-09-14 21:57   ` Rob Herring (Arm)
2025-09-15  7:48   ` AngeloGioacchino Del Regno
2025-09-14 12:29 ` [PATCH v2 2/4] remoterpoc: mediatek: vcp: Add vcp remoteproc driver Xiangzhi Tang
2025-09-14 13:28   ` Krzysztof Kozlowski
2025-09-15  9:14   ` AngeloGioacchino Del Regno
2025-09-14 12:29 ` [PATCH v2 3/4] remoterpoc: mediatek: vcp: Add ipi-mbox communication Xiangzhi Tang
2025-09-14 13:30   ` Krzysztof Kozlowski
2025-09-14 12:29 ` [PATCH v2 4/4] remoterpoc: mediatek: vcp: Add vcp suspned and resume feature Xiangzhi Tang
2025-09-14 13:16 ` [PATCH v2 0/4] ASoC: mediatek: Add support of VCP on Mediatek mt8196 SoC Krzysztof Kozlowski

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).