* [PATCH v3 0/6] ASoC: mediatek: Add VCP driver on Mediatek MT8196 SoC
@ 2026-03-20 3:18 Xiangzhi Tang
2026-03-20 3:18 ` [PATCH v3 1/6] dt-bindings: remoteproc: Add VCP support for mt8196 Xiangzhi Tang
` (5 more replies)
0 siblings, 6 replies; 11+ messages in thread
From: Xiangzhi Tang @ 2026-03-20 3:18 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, Project_Global_Chrome_Upstream_Group, Hailong Fan,
Huayu Zong, 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-20260319.
>
> Changes in v3:
> - Fix probe path driver to using dev_err_probe error printf
> - Eliminate the use of global variables
> - Using readl_poll_timeout instead of "For" poll and "while" poll
> - Add VCP IPC layer driver
> - 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 (6):
dt-bindings: remoteproc: Add VCP support for mt8196
remoteproc: Mediatek: Add VCP remoteproc driver
firmware: mediatek: Add VCP IPC protocol interfaces driver
remoteproc: mediatek: Add VCP ipi-mbox init driver
remoteproc: mediatek: Add VCP ipi communication sync mechanism
remoterpoc: mediatek: vcp: Add vcp suspned and resume feature
.../remoteproc/mediatek,mt8196-vcp.yaml | 161 ++++
drivers/firmware/Kconfig | 9 +
drivers/firmware/Makefile | 1 +
drivers/firmware/mtk-vcp-ipc.c | 481 ++++++++++
drivers/remoteproc/Kconfig | 12 +
drivers/remoteproc/Makefile | 3 +
drivers/remoteproc/mtk_vcp_common.c | 854 ++++++++++++++++++
drivers/remoteproc/mtk_vcp_common.h | 279 ++++++
drivers/remoteproc/mtk_vcp_rproc.c | 540 +++++++++++
drivers/remoteproc/mtk_vcp_rproc.h | 95 ++
include/linux/firmware/mediatek/mtk-vcp-ipc.h | 151 ++++
include/linux/remoteproc/mtk_vcp_public.h | 146 +++
include/linux/soc/mediatek/mtk_sip_svc.h | 2 +
13 files changed, 2734 insertions(+)
create mode 100644 Documentation/devicetree/bindings/remoteproc/mediatek,mt8196-vcp.yaml
create mode 100644 drivers/firmware/mtk-vcp-ipc.c
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/firmware/mediatek/mtk-vcp-ipc.h
create mode 100644 include/linux/remoteproc/mtk_vcp_public.h
--
2.46.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v3 1/6] dt-bindings: remoteproc: Add VCP support for mt8196
2026-03-20 3:18 [PATCH v3 0/6] ASoC: mediatek: Add VCP driver on Mediatek MT8196 SoC Xiangzhi Tang
@ 2026-03-20 3:18 ` Xiangzhi Tang
2026-03-20 5:25 ` Rob Herring (Arm)
2026-03-20 9:01 ` Krzysztof Kozlowski
2026-03-20 3:18 ` [PATCH v3 2/6] remoteproc: Mediatek: Add VCP remoteproc driver Xiangzhi Tang
` (4 subsequent siblings)
5 siblings, 2 replies; 11+ messages in thread
From: Xiangzhi Tang @ 2026-03-20 3:18 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, Project_Global_Chrome_Upstream_Group, Hailong Fan,
Huayu Zong, 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 | 161 ++++++++++++++++++
1 file changed, 161 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..7ec1ec69537a
--- /dev/null
+++ b/Documentation/devicetree/bindings/remoteproc/mediatek,mt8196-vcp.yaml
@@ -0,0 +1,161 @@
+# 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
+
+ reg-names:
+ items:
+ - const: sram
+ - const: cfg
+ - const: cfg_core
+ - const: cfg_sec
+
+ 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>,
+ reg-names = "sram",
+ "cfg",
+ "cfg_core",
+ "cfg_sec";
+
+ 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] 11+ messages in thread
* [PATCH v3 2/6] remoteproc: Mediatek: Add VCP remoteproc driver
2026-03-20 3:18 [PATCH v3 0/6] ASoC: mediatek: Add VCP driver on Mediatek MT8196 SoC Xiangzhi Tang
2026-03-20 3:18 ` [PATCH v3 1/6] dt-bindings: remoteproc: Add VCP support for mt8196 Xiangzhi Tang
@ 2026-03-20 3:18 ` Xiangzhi Tang
2026-03-20 9:15 ` Krzysztof Kozlowski
2026-03-20 3:18 ` [PATCH v3 3/6] firmware: mediatek: Add VCP IPC protocol interfaces driver Xiangzhi Tang
` (3 subsequent siblings)
5 siblings, 1 reply; 11+ messages in thread
From: Xiangzhi Tang @ 2026-03-20 3:18 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, Project_Global_Chrome_Upstream_Group, Hailong Fan,
Huayu Zong, Xiangzhi Tang
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 | 461 ++++++++++++++++++++++
drivers/remoteproc/mtk_vcp_common.h | 217 ++++++++++
drivers/remoteproc/mtk_vcp_rproc.c | 325 +++++++++++++++
drivers/remoteproc/mtk_vcp_rproc.h | 69 ++++
include/linux/remoteproc/mtk_vcp_public.h | 78 ++++
include/linux/soc/mediatek/mtk_sip_svc.h | 2 +
8 files changed, 1165 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 ee54436fea5a..93827f6fd3c5 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -66,6 +66,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..97ea8099912d
--- /dev/null
+++ b/drivers/remoteproc/mtk_vcp_common.c
@@ -0,0 +1,461 @@
+// 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/iopoll.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"
+
+phys_addr_t vcp_get_reserve_mem_phys(struct mtk_vcp_device *vcp,
+ enum vcp_reserve_mem_id id)
+{
+ if (id >= 0 && id < NUMS_MEM_ID)
+ return vcp->vcp_cluster->vcp_memory_tb[id].phys;
+
+ return 0;
+}
+
+dma_addr_t vcp_get_reserve_mem_iova(struct mtk_vcp_device *vcp,
+ enum vcp_reserve_mem_id id)
+{
+ if (id >= 0 && id < NUMS_MEM_ID)
+ return vcp->vcp_cluster->vcp_memory_tb[id].iova;
+
+ return 0;
+}
+
+void __iomem *vcp_get_reserve_mem_virt(struct mtk_vcp_device *vcp,
+ enum vcp_reserve_mem_id id)
+{
+ if (id >= 0 && id < NUMS_MEM_ID)
+ return vcp->vcp_cluster->vcp_memory_tb[id].virt;
+
+ return NULL;
+}
+
+size_t vcp_get_reserve_mem_size(struct mtk_vcp_device *vcp,
+ enum vcp_reserve_mem_id id)
+{
+ if (id >= 0 && id < NUMS_MEM_ID)
+ return vcp->vcp_cluster->vcp_memory_tb[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_init(struct mtk_vcp_device *vcp)
+{
+ struct device_node *rmem_node;
+ struct resource res;
+ struct iommu_domain *domain;
+ void __iomem *share_memory_virt;
+ void __iomem *rtos_memory_virt;
+ phys_addr_t mblock_phys;
+ phys_addr_t share_memory_phys;
+ dma_addr_t share_memory_iova;
+ size_t mblock_size;
+ size_t share_memory_size;
+ enum vcp_reserve_mem_id id;
+ u32 offset;
+ int ret;
+
+ rmem_node = of_parse_phandle(vcp->dev->of_node, "memory-region", 0);
+ if (!rmem_node)
+ return dev_err_probe(vcp->dev, -ENODEV, "No reserved memory-region found.\n");
+
+ ret = of_address_to_resource(rmem_node, 0, &res);
+ if (ret)
+ return dev_err_probe(vcp->dev, ret, "failed to parse reserved memory\n");
+
+ mblock_phys = (phys_addr_t)res.start;
+ mblock_size = (size_t)resource_size(&res);
+
+ offset = 0;
+ for (id = 0; id < NUMS_MEM_ID; id++) {
+ vcp->vcp_cluster->vcp_memory_tb[id].phys = mblock_phys + offset;
+ vcp->vcp_cluster->vcp_memory_tb[id].size = vcp->platdata->memory_tb[id].size;
+ offset += vcp->vcp_cluster->vcp_memory_tb[id].size;
+ }
+ if (offset > mblock_size)
+ return dev_err_probe(vcp->dev, -EINVAL, "No enough reserved memory to alloc");
+
+ share_memory_size = offset - vcp->vcp_cluster->vcp_memory_tb[VCP_RTOS_MEM_ID].size;
+ share_memory_phys = vcp->vcp_cluster->vcp_memory_tb[VCP_RTOS_MEM_ID].phys +
+ vcp->vcp_cluster->vcp_memory_tb[VCP_RTOS_MEM_ID].size;
+
+ rtos_memory_virt = devm_ioremap(vcp->dev,
+ vcp->vcp_cluster->vcp_memory_tb[VCP_RTOS_MEM_ID].phys,
+ vcp->vcp_cluster->vcp_memory_tb[VCP_RTOS_MEM_ID].size);
+ domain = iommu_get_domain_for_dev(vcp->dev);
+ ret = iommu_map(domain, vcp->platdata->rtos_static_iova,
+ vcp->vcp_cluster->vcp_memory_tb[VCP_RTOS_MEM_ID].phys,
+ vcp->vcp_cluster->vcp_memory_tb[VCP_RTOS_MEM_ID].size,
+ IOMMU_READ | IOMMU_WRITE | IOMMU_PRIV, GFP_KERNEL);
+ if (ret)
+ return dev_err_probe(vcp->dev, ret, "iommu map failed\n");
+
+ vcp->vcp_cluster->vcp_memory_tb[VCP_RTOS_MEM_ID].virt = rtos_memory_virt;
+ vcp->vcp_cluster->vcp_memory_tb[VCP_RTOS_MEM_ID].iova = vcp->platdata->rtos_static_iova;
+
+ ret = dma_set_mask_and_coherent(vcp->dev, DMA_BIT_MASK(DMA_MAX_MASK_BIT));
+ if (ret)
+ return dev_err_probe(vcp->dev, ret, "64-bit DMA enable failed\n");
+
+ 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
+ return dev_err_probe(vcp->dev, -EINVAL, "Failed to set DMA parms\n");
+ }
+ share_memory_virt = dma_alloc_coherent(vcp->dev,
+ share_memory_size,
+ &share_memory_iova,
+ GFP_KERNEL);
+ if (!share_memory_virt)
+ return dev_err_probe(vcp->dev, -EINVAL, "dma_alloc_coherent failed\n");
+
+ offset = 0;
+ for (id = VCP_RTOS_MEM_ID + 1; id < NUMS_MEM_ID; id++) {
+ vcp->vcp_cluster->vcp_memory_tb[id].phys = share_memory_phys + offset;
+ vcp->vcp_cluster->vcp_memory_tb[id].iova = share_memory_iova + offset;
+ vcp->vcp_cluster->vcp_memory_tb[id].virt = share_memory_virt + offset;
+ offset += (u32)vcp->vcp_cluster->vcp_memory_tb[id].size;
+ }
+
+ vcp->vcp_cluster->share_mem_iova = share_memory_iova;
+ vcp->vcp_cluster->share_mem_size = share_memory_size;
+
+ return 0;
+}
+
+int wait_core_hart_shutdown(struct mtk_vcp_device *vcp,
+ enum vcp_core_id core_id)
+{
+ u32 hart0;
+ u32 hart1;
+ bool twohart_support;
+ int ret;
+
+ twohart_support = vcp->vcp_cluster->twohart[core_id];
+
+ switch (core_id) {
+ case VCP_ID:
+ ret = readl_poll_timeout(vcp->vcp_cluster->cfg + VCP_C0_GPR5_H0_REBOOT,
+ hart0, (hart0 & CORE_RDY_TO_REBOOT),
+ USEC_PER_MSEC,
+ CORE_HART_SHUTDOWN_TIMEOUT_MS * USEC_PER_MSEC);
+ if (ret) {
+ dev_err(vcp->dev, "VCP_ID hart0 shutdown timeout\n");
+ return ret;
+ }
+
+ if (twohart_support) {
+ ret = readl_poll_timeout(vcp->vcp_cluster->cfg + VCP_C0_GPR6_H1_REBOOT,
+ hart1, (hart1 & CORE_RDY_TO_REBOOT),
+ USEC_PER_MSEC,
+ CORE_HART_SHUTDOWN_TIMEOUT_MS * USEC_PER_MSEC);
+ if (ret) {
+ dev_err(vcp->dev, "VCP_ID hart1 shutdown timeout\n");
+ return ret;
+ }
+ }
+ break;
+ case MMUP_ID:
+ ret = readl_poll_timeout(vcp->vcp_cluster->cfg + VCP_C1_GPR5_H0_REBOOT,
+ hart0, (hart0 & CORE_RDY_TO_REBOOT),
+ USEC_PER_MSEC,
+ CORE_HART_SHUTDOWN_TIMEOUT_MS * USEC_PER_MSEC);
+ if (ret) {
+ dev_err(vcp->dev, "MMUP_ID hart0 shutdown timeout\n");
+ return ret;
+ }
+
+ if (twohart_support) {
+ ret = readl_poll_timeout(vcp->vcp_cluster->cfg + VCP_C1_GPR6_H1_REBOOT,
+ hart1, (hart1 & CORE_RDY_TO_REBOOT),
+ USEC_PER_MSEC,
+ CORE_HART_SHUTDOWN_TIMEOUT_MS * USEC_PER_MSEC);
+ if (ret) {
+ dev_err(vcp->dev, "MMUP_ID hart1 shutdown timeout\n");
+ return ret;
+ }
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err(vcp->dev, "%s(), No Support core id\n", __func__);
+ break;
+ }
+
+ return ret;
+}
+
+int reset_vcp(struct mtk_vcp_device *vcp)
+{
+ struct arm_smccc_res res;
+
+ if (vcp->vcp_cluster->core_nums > MMUP_ID) {
+ 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);
+ }
+
+ 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 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 mtk_vcp_img_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 mtk_vcp_img_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, 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, VCP_RTOS_MEM_ID);
+ img_buf_phys = vcp->ops->vcp_get_mem_phys(vcp, VCP_RTOS_MEM_ID);
+ img_buf_va = vcp->ops->vcp_get_mem_virt(vcp, 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_probe(vcp->dev, ret, "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..d048757c955a
--- /dev/null
+++ b/drivers/remoteproc/mtk_vcp_common.h
@@ -0,0 +1,217 @@
+/* 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 CORE_HART_SHUTDOWN_TIMEOUT_MS 10
+
+/* VCP platform definition */
+#define DMA_MAX_MASK_BIT 33
+
+/* VCP load image definition */
+#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 definition */
+#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 cfg_core register offset definition */
+#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)
+
+/* VCP cfg register offset definition */
+#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 (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 (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)
+
+/* VCP cfg_sec register offset definition */
+#define R_GPR2_SEC (0x0008)
+#define MMUP_AP_SUSPEND BIT(0)
+#define R_GPR3_SEC (0x000C)
+#define VCP_AP_SUSPEND BIT(0)
+
+enum vcp_core_id {
+ VCP_ID = 0,
+ MMUP_ID,
+ VCP_CORE_TOTAL,
+};
+
+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 mtk_vcp_img_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 mtk_vcp_img_hdr {
+ u32 magic;
+ u32 dsz;
+ char name[VCM_IMAGE_NAME_MAXSZ];
+};
+
+/**
+ * struct mtk_vcp_feature_table - feature table structure definition.
+ *
+ * @feature_id: feature id
+ * @core_id: feature using vcp core id
+ */
+struct mtk_vcp_feature_table {
+ enum vcp_feature_id feature_id;
+ enum vcp_core_id core_id;
+};
+
+/**
+ * struct mtk_vcp_reserved_mem_table - memory table structure definition.
+ *
+ * @memory_id: memory_id id
+ * @size: predistribution memory size
+ */
+struct mtk_vcp_reserved_mem_table {
+ enum vcp_reserve_mem_id memory_id;
+ size_t size;
+};
+
+/**
+ * struct vcp_reserve_mblock - vcp reserved memory structure.
+ *
+ * @vcp_reserve_mem_id: reserved memory id
+ * @phys: reserved memory phy addr
+ * @iova: reserved memory dma map addr
+ * @virt: reserved memory CPU virt addr
+ * @size: reserved memory size
+ */
+struct vcp_reserve_mblock {
+ enum vcp_reserve_mem_id num;
+ phys_addr_t phys;
+ dma_addr_t iova;
+ void __iomem *virt;
+ size_t size;
+};
+
+/**
+ * struct vcp_region_info_st - config vcp image info sync to vcp bootloader.
+ *
+ * @ap_loader_start: config vcp bootloader to copy loader start addr
+ * @ap_loader_size: config vcp bootloader to copy loader size
+ * @ap_firmware_start: config vcp bootloader to copy firmware start addr
+ * @ap_firmware_size: 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: vcp task context ptr for debug
+ * @vcpctl: - vcp control info
+ * @regdump_start: regdump start addr for debug
+ * @regdump_size: regdump size for debug
+ * @ap_params_start: params start addr
+ * @sramlog_buf_offset: sramlog_buf_offset for debug
+ * @sramlog_end_idx_offset: sramlog_end_idx_offset for debug
+ * @sramlog_buf_maxlen: sramlog_buf_maxlen for debug
+ * @ap_loader_start_pa: config vcp bootloader for loader start pa
+ * @coredump_offset: coredump_offset offset for debug
+ * @coredump_dram_offset: 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;
+};
+
+int vcp_reserve_memory_init(struct mtk_vcp_device *vcp);
+phys_addr_t vcp_get_reserve_mem_phys(struct mtk_vcp_device *vcp, enum vcp_reserve_mem_id id);
+dma_addr_t vcp_get_reserve_mem_iova(struct mtk_vcp_device *vcp, enum vcp_reserve_mem_id id);
+size_t vcp_get_reserve_mem_size(struct mtk_vcp_device *vcp, enum vcp_reserve_mem_id id);
+void __iomem *vcp_get_reserve_mem_virt(struct mtk_vcp_device *vcp, enum vcp_reserve_mem_id id);
+void __iomem *vcp_get_internal_sram_virt(struct mtk_vcp_device *vcp);
+
+int reset_vcp(struct mtk_vcp_device *vcp);
+int mtk_vcp_load(struct rproc *rproc, const struct firmware *fw);
+
+int vcp_wdt_irq_init(struct mtk_vcp_device *vcp);
+
+int 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..bcb5f3786cf7
--- /dev/null
+++ b/drivers/remoteproc/mtk_vcp_rproc.c
@@ -0,0 +1,325 @@
+// 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);
+
+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);
+
+ ret = reset_vcp(vcp);
+ if (ret)
+ dev_err(vcp->dev, "bootup fail\n");
+ else
+ dev_info(vcp->dev, "bootup successfully\n");
+
+ return ret;
+}
+
+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)
+ return dev_err_probe(&pdev->dev, ret, "get twohart property failed\n");
+
+ ret = of_property_read_u32(pdev->dev.of_node, "mtk,core-sram-offset",
+ &vcp_cluster->sram_offset[core_id]);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "get sram-offset property failed\n");
+
+ 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)
+ return ERR_PTR(dev_err_probe(dev, -ENOMEM, "allocate remoteproc failed\n"));
+
+ 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;
+ platform_set_drvdata(pdev, vcp);
+
+ ret = vcp_reserve_memory_init(vcp);
+ if (ret)
+ return ERR_PTR(dev_err_probe(dev, ret, "reserve memory failed\n"));
+
+ 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) {
+ of_node_put(child);
+ return ERR_PTR(dev_err_probe(dev, -ENODEV, "Not child node\n"));
+ }
+ 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)
+ return ERR_PTR(dev_err_probe(dev, ret, "vcp_wdt_irq_init failed\n"));
+
+ 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 dev_err_probe(vcp->dev, PTR_ERR(vcp), "vcp_rproc_init failed\n");
+
+ ret = rproc_add(vcp->rproc);
+ if (ret) {
+ rproc_del(vcp->rproc);
+ return dev_err_probe(vcp->dev, ret, "Failed to add rproc\n");
+ }
+
+ 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 dev_err_probe(dev, -ENOMEM, "allocate resource failed\n");
+
+ 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");
+
+ 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);
+ int ret;
+
+ writel(GIPC_VCP_HART0_SHUT, vcp->vcp_cluster->cfg_core + R_GIPC_IN_SET);
+ ret = wait_core_hart_shutdown(vcp, VCP_ID);
+ if (ret)
+ dev_err(&pdev->dev, "wait VCP_ID core hart shutdown timeout\n");
+
+ if (vcp->vcp_cluster->core_nums > MMUP_ID) {
+ writel(GIPC_MMUP_SHUT, vcp->vcp_cluster->cfg_core + R_GIPC_IN_SET);
+ ret = wait_core_hart_shutdown(vcp, MMUP_ID);
+ if (ret)
+ dev_err(&pdev->dev, "wait MMUP_ID core hart shutdown timeout\n");
+ }
+}
+
+static struct mtk_vcp_feature_table mt8196_feature_tb[NUM_FEATURE_ID] = {
+ { .feature_id = RTOS_FEATURE_ID, .core_id = VCP_CORE_TOTAL },
+ { .feature_id = VDEC_FEATURE_ID, .core_id = VCP_ID },
+ { .feature_id = VENC_FEATURE_ID, .core_id = VCP_ID },
+ { .feature_id = MMDVFS_MMUP_FEATURE_ID, .core_id = MMUP_ID },
+ { .feature_id = MMDVFS_VCP_FEATURE_ID, .core_id = VCP_ID },
+ { .feature_id = MMDEBUG_FEATURE_ID, .core_id = MMUP_ID },
+ { .feature_id = VMM_FEATURE_ID, .core_id = MMUP_ID },
+ { .feature_id = VDISP_FEATURE_ID, .core_id = MMUP_ID },
+ { .feature_id = MMQOS_FEATURE_ID, .core_id = VCP_ID },
+};
+
+static struct mtk_vcp_reserved_mem_table mt8196_memory_tb[NUMS_MEM_ID] = {
+ { .memory_id = VCP_RTOS_MEM_ID, .size = 0x1a00000 },
+ { .memory_id = VDEC_MEM_ID, .size = 0x30000 },
+ { .memory_id = VENC_MEM_ID, .size = 0x12000 },
+ { .memory_id = MMDVFS_VCP_MEM_ID, .size = 0x1000 },
+ { .memory_id = MMDVFS_MMUP_MEM_ID, .size = 0x1000 },
+ { .memory_id = MMQOS_MEM_ID, .size = 0x1000 },
+};
+
+static const struct mtk_vcp_of_data mt8196_of_data = {
+ .ops = {
+ .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,
+ .feature_tb = mt8196_feature_tb,
+ .memory_tb = mt8196_memory_tb,
+ .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..11ce0f6d562b
--- /dev/null
+++ b/drivers/remoteproc/mtk_vcp_rproc.h
@@ -0,0 +1,69 @@
+/* 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
+ * @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
+ * @share_mem_iova: shared memory iova base
+ * @share_mem_size: shared memory size
+ * @vcp_memory_tb: vcp memory allocated table
+ */
+struct mtk_vcp_of_cluster {
+ void __iomem *sram_base;
+ void __iomem *cfg;
+ void __iomem *cfg_sec;
+ void __iomem *cfg_core;
+ u32 sram_size;
+ u32 core_nums;
+ u32 twohart[VCP_CORE_TOTAL];
+ u32 sram_offset[VCP_CORE_TOTAL];
+ dma_addr_t share_mem_iova;
+ size_t share_mem_size;
+ struct vcp_reserve_mblock vcp_memory_tb[NUMS_MEM_ID];
+};
+
+/**
+ * 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
+ * @feature_tb: vcp feature table structure
+ * @memory_tb: vcp memory table 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_vcp_feature_table *feature_tb;
+ struct mtk_vcp_reserved_mem_table *memory_tb;
+ 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..3bbc2055f9f8
--- /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>
+
+enum vcp_reserve_mem_id {
+ VCP_RTOS_MEM_ID,
+ VDEC_MEM_ID,
+ VENC_MEM_ID,
+ MMDVFS_VCP_MEM_ID,
+ MMDVFS_MMUP_MEM_ID,
+ MMQOS_MEM_ID,
+ NUMS_MEM_ID,
+};
+
+enum vcp_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 {
+ phys_addr_t (*vcp_get_mem_phys)(struct mtk_vcp_device *vcp,
+ enum vcp_reserve_mem_id id);
+ dma_addr_t (*vcp_get_mem_iova)(struct mtk_vcp_device *vcp,
+ enum vcp_reserve_mem_id id);
+ void __iomem *(*vcp_get_mem_virt)(struct mtk_vcp_device *vcp,
+ enum vcp_reserve_mem_id id);
+ size_t (*vcp_get_mem_size)(struct mtk_vcp_device *vcp,
+ enum vcp_reserve_mem_id 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..e59fadc8cbee 100644
--- a/include/linux/soc/mediatek/mtk_sip_svc.h
+++ b/include/linux/soc/mediatek/mtk_sip_svc.h
@@ -28,4 +28,6 @@
/* IOMMU related SMC call */
#define MTK_SIP_KERNEL_IOMMU_CONTROL MTK_SIP_SMC_CMD(0x514)
+#define MTK_SIP_TINYSYS_VCP_CONTROL MTK_SIP_SMC_CMD(0x52C)
+
#endif
--
2.46.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v3 3/6] firmware: mediatek: Add VCP IPC protocol interfaces driver
2026-03-20 3:18 [PATCH v3 0/6] ASoC: mediatek: Add VCP driver on Mediatek MT8196 SoC Xiangzhi Tang
2026-03-20 3:18 ` [PATCH v3 1/6] dt-bindings: remoteproc: Add VCP support for mt8196 Xiangzhi Tang
2026-03-20 3:18 ` [PATCH v3 2/6] remoteproc: Mediatek: Add VCP remoteproc driver Xiangzhi Tang
@ 2026-03-20 3:18 ` Xiangzhi Tang
2026-08-13 22:10 ` sashiko-bot
2026-03-20 3:18 ` [PATCH v3 4/6] remoteproc: mediatek: Add VCP ipi-mbox init driver Xiangzhi Tang
` (2 subsequent siblings)
5 siblings, 1 reply; 11+ messages in thread
From: Xiangzhi Tang @ 2026-03-20 3:18 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, Project_Global_Chrome_Upstream_Group, Hailong Fan,
Huayu Zong, Xiangzhi Tang, Jjian Zhou
Some of mediatek processors contain the Risc-V coprocessor,
The communication between Host CPU and vcp firmware is
taking place using a shared memory area for message passing.
VCP IPC protocol offers (send/recv) interfaces using
mediatek-mailbox APIs.
Signed-off-by: Jjian Zhou <jjian.zhou@mediatek.com>
Signed-off-by: Xiangzhi Tang <xiangzhi.tang@mediatek.com>
---
drivers/firmware/Kconfig | 9 +
drivers/firmware/Makefile | 1 +
drivers/firmware/mtk-vcp-ipc.c | 481 ++++++++++++++++++
include/linux/firmware/mediatek/mtk-vcp-ipc.h | 151 ++++++
4 files changed, 642 insertions(+)
create mode 100644 drivers/firmware/mtk-vcp-ipc.c
create mode 100644 include/linux/firmware/mediatek/mtk-vcp-ipc.h
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index bbd2155d8483..80f63b733820 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -178,6 +178,15 @@ config MTK_ADSP_IPC
ADSP exists on some mtk processors.
Client might use shared memory to exchange information with ADSP.
+config MTK_VCP_IPC
+ tristate "MTK VCP IPC Protocol driver"
+ depends on MTK_VCP_MBOX
+ help
+ Say yes here to add support for the MediaTek VCP IPC
+ between host AP (Linux) and the firmware running on VCP.
+ VCP exists on some mtk processors.
+ Client might use shared memory to exchange information with VCP.
+
config SYSFB
bool
select BOOT_VESA_SUPPORT
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index 4ddec2820c96..d6b6197cb54c 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_ISCSI_IBFT_FIND) += iscsi_ibft_find.o
obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o
obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o
obj-$(CONFIG_MTK_ADSP_IPC) += mtk-adsp-ipc.o
+obj-$(CONFIG_MTK_VCP_IPC) += mtk-vcp-ipc.o
obj-$(CONFIG_RASPBERRYPI_FIRMWARE) += raspberrypi.o
obj-$(CONFIG_FW_CFG_SYSFS) += qemu_fw_cfg.o
obj-$(CONFIG_SYSFB) += sysfb.o
diff --git a/drivers/firmware/mtk-vcp-ipc.c b/drivers/firmware/mtk-vcp-ipc.c
new file mode 100644
index 000000000000..c25615cf1def
--- /dev/null
+++ b/drivers/firmware/mtk-vcp-ipc.c
@@ -0,0 +1,481 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Copyright (c) 2024 MediaTek Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/firmware/mediatek/mtk-vcp-ipc.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/sched/clock.h>
+#include <linux/time64.h>
+#include <linux/vmalloc.h>
+
+/**
+ * struct mtk_ipi_chan_table - channel table that belong to mtk_ipi_device
+ * @mbox: the mbox channel number
+ * @mbox_pin_cb: callback function
+ * @holder: keep 1 if there are ipi waiters (to wait the reply)
+ * @ipi_record: timestamp of each ipi transmission stage
+ * @pin_buf: buffer point
+ * @prdata: private data
+ * @recv_opt: recv option, 0:receive ,1: response
+ * @notify: completion notify process
+ * @send_ofs: message offset in the slots of a mbox
+ * @send_index: bit offset in the mbox
+ * @msg_zie: slot size of the ipi message
+ *
+ * All of these data should be initialized by mtk_ipi_device_register()
+ */
+struct mtk_ipi_chan_table {
+ u32 mbox;
+ mbox_pin_cb_t mbox_pin_cb;
+ atomic_t holder;
+ void *pin_buf;
+ void *prdata;
+ u32 recv_opt;
+ struct completion notify;
+ /* define a mutex for remote response */
+ struct mutex mutex_send;
+ u32 send_ofs;
+ u32 send_index;
+ u32 msg_size;
+};
+
+/**
+ * mbox information
+ *
+ * @mbdev: mbox device
+ * @mbox_id: mbox id
+ * @slot: how many slots that mbox used
+ * @opt: option for tx mode, 0:mbox, 1:share memory 2:queue
+ * @base: mbox base address
+ * @mbox_client: mbox client
+ * @mbox_chan: mbox channel
+ */
+struct mtk_mbox_info {
+ struct mtk_vcp_ipc *vcp_ipc;
+ u32 mbox_id;
+ u32 slot;
+ u32 opt;
+ /* lock of mbox */
+ spinlock_t mbox_lock;
+ struct mbox_client cl;
+ struct mbox_chan *ch;
+ struct mtk_ipi_info ipi_info;
+};
+
+static const char * const mbox_names[VCP_MBOX_NUM] = {
+ "mbox0", "mbox1", "mbox2", "mbox3", "mbox4"
+};
+
+/**
+ * mtk_vcp_ipc_recv - recv callback used by MTK VCP mailbox
+ *
+ * @c: mbox client
+ * @msg: message received
+ *
+ * Users of VCP IPC will need to provide handle_reply and handle_request
+ * callbacks.
+ */
+static void mtk_vcp_ipc_recv(struct mbox_client *c, void *msg)
+{
+ struct mtk_mbox_info *minfo = container_of(c, struct mtk_mbox_info, cl);
+ struct mtk_vcp_ipc *vcp_ipc = minfo->vcp_ipc;
+ struct mtk_ipi_info *ipi_info = msg;
+ struct mtk_ipi_device *ipidev = vcp_ipc->ipi_priv;
+ struct mtk_ipi_chan_table *table;
+ struct mtk_mbox_recv_table *mbox_recv;
+ u32 id;
+
+ /* execute all receive pin handler */
+ for (id = 0; id < vcp_ipc->mbdev->recv_count; id++) {
+ mbox_recv = &vcp_ipc->mbdev->recv_table[id];
+ if (mbox_recv->mbox_id != minfo->mbox_id)
+ continue;
+
+ if (!(BIT(mbox_recv->pin_index) & ipi_info->irq_status))
+ continue;
+
+ table = &ipidev->table[mbox_recv->ipi_id];
+ if (!table->pin_buf) {
+ dev_err(vcp_ipc->dev, "IPI%d buf is null.\n",
+ mbox_recv->ipi_id);
+ continue;
+ }
+
+ memcpy(table->pin_buf,
+ ipi_info->msg + mbox_recv->offset * MBOX_SLOT_SIZE,
+ mbox_recv->msg_size * MBOX_SLOT_SIZE);
+
+ if (!mbox_recv->recv_opt && table->mbox_pin_cb)
+ table->mbox_pin_cb(mbox_recv->ipi_id,
+ table->prdata,
+ table->pin_buf,
+ mbox_recv->msg_size * MBOX_SLOT_SIZE);
+
+ /* notify task */
+ if (table->recv_opt == MBOX_RECV_MESSAGE ||
+ atomic_read(&table->holder))
+ complete(&table->notify);
+ }
+}
+
+/*
+ * mtk_vcp_ipc_send - send ipc command to MTK VCP
+ *
+ * @ipidev: VCP struct mtk_ipi_device handle
+ * @id: id of the feature IPI
+ * @data: message address
+ * @len: message length
+ *
+ * Return: Zero for success from mbox_send_message
+ * negative value for error
+ */
+int mtk_vcp_ipc_send(struct mtk_ipi_device *ipidev, u32 id, void *data, u32 len)
+{
+ struct device *dev;
+ struct mtk_mbox_info *minfo;
+ struct mtk_ipi_chan_table *table;
+ struct mtk_vcp_ipc *vcp_ipc;
+ int ret;
+
+ if (!ipidev || !ipidev->ipi_inited || !data)
+ return IPI_UNAVAILABLE;
+ vcp_ipc = ipidev->vcp_ipc;
+ if (!vcp_ipc)
+ return IPI_UNAVAILABLE;
+
+ table = ipidev->table;
+ dev = ipidev->vcp_ipc->dev;
+ minfo = &ipidev->vcp_ipc->info_table[table[id].mbox];
+ if (!minfo) {
+ dev_err(dev, "%s IPI%d minfo is invalid.\n", ipidev->name, id);
+ return IPI_UNAVAILABLE;
+ }
+
+ if (len > table[id].msg_size)
+ return IPI_MSG_TOO_BIG;
+ else if (!len)
+ len = table[id].msg_size;
+
+ mutex_lock(&table[id].mutex_send);
+
+ minfo->ipi_info.msg = data;
+ minfo->ipi_info.len = len;
+ minfo->ipi_info.id = id;
+ minfo->ipi_info.index = table[id].send_index;
+ minfo->ipi_info.slot_ofs = table[id].send_ofs * MBOX_SLOT_SIZE;
+
+ ret = mbox_send_message(minfo->ch, &minfo->ipi_info);
+ mutex_unlock(&table[id].mutex_send);
+ if (ret < 0) {
+ dev_err(dev, "%s IPI%d send failed.\n", ipidev->name, id);
+ return IPI_MBOX_ERR;
+ }
+
+ return IPI_ACTION_DONE;
+}
+EXPORT_SYMBOL(mtk_vcp_ipc_send);
+
+/*
+ * mtk_vcp_ipc_send_compl - send ipc command to MTK VCP
+ *
+ * @ipidev: VCP struct mtk_ipi_device handle
+ * @id: id of the feature IPI
+ * @data: message address
+ * @len: message length
+ * @timeout_ms:
+ *
+ * Return: Zero for success from mbox_send_message
+ * negative value for error
+ */
+int mtk_vcp_ipc_send_compl(struct mtk_ipi_device *ipidev, u32 id,
+ void *data, u32 len, u32 timeout_ms)
+{
+ struct device *dev;
+ struct mtk_mbox_info *minfo;
+ struct mtk_ipi_chan_table *table;
+ struct mtk_vcp_ipc *vcp_ipc;
+ int ret;
+
+ if (!ipidev || !ipidev->ipi_inited || !data)
+ return IPI_UNAVAILABLE;
+ vcp_ipc = ipidev->vcp_ipc;
+ if (!vcp_ipc)
+ return IPI_UNAVAILABLE;
+
+ table = ipidev->table;
+ dev = ipidev->vcp_ipc->dev;
+ minfo = &ipidev->vcp_ipc->info_table[table[id].mbox];
+ if (!minfo) {
+ dev_err(dev, "%s IPI%d minfo is invalid.\n", ipidev->name, id);
+ return IPI_UNAVAILABLE;
+ }
+
+ if (len > table[id].msg_size)
+ return IPI_MSG_TOO_BIG;
+ else if (!len)
+ len = table[id].msg_size;
+
+ mutex_lock(&table[id].mutex_send);
+
+ minfo->ipi_info.msg = data;
+ minfo->ipi_info.len = len;
+ minfo->ipi_info.id = id;
+ minfo->ipi_info.index = table[id].send_index;
+ minfo->ipi_info.slot_ofs = table[id].send_ofs * MBOX_SLOT_SIZE;
+
+ atomic_inc(&table[id].holder);
+
+ ret = mbox_send_message(minfo->ch, &minfo->ipi_info);
+ if (ret < 0) {
+ atomic_set(&table[id].holder, 0);
+ mutex_unlock(&table[id].mutex_send);
+ dev_err(dev, "%s IPI%d send failed.\n", ipidev->name, id);
+ return IPI_MBOX_ERR;
+ }
+
+ /* wait for completion */
+ ret = wait_for_completion_timeout(&table[id].notify,
+ msecs_to_jiffies(timeout_ms));
+ atomic_set(&table[id].holder, 0);
+ if (ret > 0)
+ ret = IPI_ACTION_DONE;
+
+ mutex_unlock(&table[id].mutex_send);
+
+ return ret;
+}
+EXPORT_SYMBOL(mtk_vcp_ipc_send_compl);
+
+int mtk_vcp_mbox_ipc_register(struct mtk_ipi_device *ipidev, int id,
+ mbox_pin_cb_t cb, void *prdata, void *msg)
+{
+ if (!ipidev || !ipidev->ipi_inited)
+ return IPI_DEV_ILLEGAL;
+ if (!msg)
+ return IPI_NO_MSGBUF;
+
+ if (ipidev->table[id].pin_buf)
+ return IPI_ALREADY_USED;
+ ipidev->table[id].mbox_pin_cb = cb;
+ ipidev->table[id].pin_buf = msg;
+ ipidev->table[id].prdata = prdata;
+
+ return IPI_ACTION_DONE;
+}
+EXPORT_SYMBOL(mtk_vcp_mbox_ipc_register);
+
+int mtk_vcp_mbox_ipc_unregister(struct mtk_ipi_device *ipidev, int id)
+{
+ if (!ipidev || !ipidev->ipi_inited)
+ return IPI_DEV_ILLEGAL;
+
+ /* Drop the ipi and reset the record */
+ complete(&ipidev->table[id].notify);
+
+ ipidev->table[id].mbox_pin_cb = NULL;
+ ipidev->table[id].pin_buf = NULL;
+ ipidev->table[id].prdata = NULL;
+
+ return IPI_ACTION_DONE;
+}
+EXPORT_SYMBOL(mtk_vcp_mbox_ipc_unregister);
+
+static void mtk_fill_in_entry(struct mtk_ipi_chan_table *entry, const u32 ipi_id,
+ const struct mtk_mbox_table *mbdev)
+{
+ const struct mtk_mbox_send_table *mbox_send = mbdev->send_table;
+ u32 index;
+
+ for (index = 0; index < mbdev->send_count; index++) {
+ if (ipi_id != mbox_send[index].ipi_id)
+ continue;
+
+ entry->send_ofs = mbox_send[index].offset;
+ entry->send_index = mbox_send[index].pin_index;
+ entry->msg_size = mbox_send[index].msg_size;
+ entry->mbox = mbox_send[index].mbox_id;
+ return;
+ }
+
+ entry->mbox = -ENOENT;
+}
+
+int mtk_vcp_ipc_device_register(struct mtk_ipi_device *ipidev,
+ u32 ipi_chan_count, struct mtk_vcp_ipc *vcp_ipc)
+{
+ struct mtk_ipi_chan_table *ipi_chan_table;
+ struct mtk_mbox_table *mbdev;
+ u32 index;
+
+ if (!vcp_ipc || !ipidev)
+ return -EINVAL;
+
+ ipi_chan_table = kcalloc(ipi_chan_count,
+ sizeof(struct mtk_ipi_chan_table), GFP_KERNEL);
+ if (!ipi_chan_table)
+ return -ENOMEM;
+
+ mbdev = vcp_ipc->mbdev;
+ vcp_ipc->ipi_priv = (void *)ipidev;
+ ipidev->table = ipi_chan_table;
+ ipidev->vcp_ipc = vcp_ipc;
+
+ for (index = 0; index < ipi_chan_count; index++) {
+ atomic_set(&ipi_chan_table[index].holder, 0);
+ mutex_init(&ipi_chan_table[index].mutex_send);
+ init_completion(&ipi_chan_table[index].notify);
+ mtk_fill_in_entry(&ipi_chan_table[index], index, mbdev);
+ }
+
+ ipidev->ipi_inited = 1;
+
+ dev_dbg(vcp_ipc->dev, "%s (with %d IPI) has registered.\n",
+ ipidev->name, ipi_chan_count);
+
+ return IPI_ACTION_DONE;
+}
+EXPORT_SYMBOL(mtk_vcp_ipc_device_register);
+
+static int setup_mbox_table(struct mtk_mbox_table *mbdev, u32 mbox)
+{
+ struct mtk_mbox_send_table *mbox_send = &mbdev->send_table[0];
+ struct mtk_mbox_recv_table *mbox_recv = &mbdev->recv_table[0];
+ u32 i, last_ofs = 0, last_idx = 0, last_slot = 0, last_sz = 0;
+
+ for (i = 0; i < mbdev->send_count; i++) {
+ if (mbox == mbox_send[i].mbox_id) {
+ mbox_send[i].offset = last_ofs + last_slot;
+ mbox_send[i].pin_index = last_idx + last_sz;
+ last_idx = mbox_send[i].pin_index;
+ last_sz = DIV_ROUND_UP(mbox_send[i].msg_size, MBOX_SLOT_ALIGN);
+ last_ofs = last_sz * MBOX_SLOT_ALIGN;
+ last_slot = last_idx * MBOX_SLOT_ALIGN;
+ } else if (mbox < mbox_send[i].mbox_id) {
+ /* no need to search the rest id */
+ break;
+ }
+ }
+
+ for (i = 0; i < mbdev->recv_count; i++) {
+ if (mbox == mbox_recv[i].mbox_id) {
+ mbox_recv[i].offset = last_ofs + last_slot;
+ mbox_recv[i].pin_index = last_idx + last_sz;
+ last_idx = mbox_recv[i].pin_index;
+ last_sz = DIV_ROUND_UP(mbox_recv[i].msg_size, MBOX_SLOT_ALIGN);
+ last_ofs = last_sz * MBOX_SLOT_ALIGN;
+ last_slot = last_idx * MBOX_SLOT_ALIGN;
+ } else if (mbox < mbox_recv[i].mbox_id) {
+ /* no need to search the rest id */
+ break;
+ }
+ }
+
+ if (last_idx > MBOX_MAX_PIN || (last_ofs + last_slot) > MTK_VCP_MBOX_SLOT_MAX_SIZE / 4)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int mtk_vcp_ipc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mtk_vcp_ipc *vcp_ipc;
+ struct mbox_client *cl;
+ struct mtk_mbox_info *minfo;
+ int ret;
+ u32 mbox, i;
+ struct mtk_mbox_table *mbox_data = dev_get_platdata(dev);
+
+ device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent);
+
+ vcp_ipc = devm_kzalloc(dev, sizeof(*vcp_ipc), GFP_KERNEL);
+ if (!vcp_ipc)
+ return -ENOMEM;
+
+ if (!mbox_data) {
+ dev_err(dev, "No platform data available\n");
+ return -EINVAL;
+ }
+ vcp_ipc->mbdev = mbox_data;
+
+ /* alloc and init mmup_mbox_info */
+ vcp_ipc->info_table = vzalloc(sizeof(*vcp_ipc->info_table) * VCP_MBOX_NUM);
+ if (!vcp_ipc->info_table)
+ return -ENOMEM;
+
+ /* create mbox dev */
+ for (mbox = 0; mbox < VCP_MBOX_NUM; mbox++) {
+ minfo = &vcp_ipc->info_table[mbox];
+ minfo->mbox_id = mbox;
+ minfo->vcp_ipc = vcp_ipc;
+ spin_lock_init(&minfo->mbox_lock);
+
+ ret = setup_mbox_table(vcp_ipc->mbdev, mbox);
+ if (ret)
+ return ret;
+
+ cl = &minfo->cl;
+ cl->dev = &pdev->dev;
+ cl->tx_block = false;
+ cl->knows_txdone = false;
+ cl->tx_prepare = NULL;
+ cl->rx_callback = mtk_vcp_ipc_recv;
+ minfo->ch = mbox_request_channel_byname(cl, mbox_names[mbox]);
+ if (IS_ERR(minfo->ch)) {
+ ret = PTR_ERR(minfo->ch);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "Failed to request mbox channel %s ret %d\n",
+ mbox_names[mbox], ret);
+
+ for (i = 0; i < mbox; i++) {
+ minfo = &vcp_ipc->info_table[i];
+ mbox_free_channel(minfo->ch);
+ }
+
+ vfree(vcp_ipc->info_table);
+ return ret;
+ }
+ }
+
+ vcp_ipc->dev = dev;
+ dev_set_drvdata(dev, vcp_ipc);
+ dev_dbg(dev, "MTK VCP IPC initialized\n");
+
+ return 0;
+}
+
+static void mtk_vcp_ipc_remove(struct platform_device *pdev)
+{
+ struct mtk_vcp_ipc *vcp_ipc = dev_get_drvdata(&pdev->dev);
+ struct mtk_mbox_info *minfo;
+ int i;
+
+ for (i = 0; i < VCP_MBOX_NUM; i++) {
+ minfo = &vcp_ipc->info_table[i];
+ mbox_free_channel(minfo->ch);
+ }
+
+ vfree(vcp_ipc->info_table);
+}
+
+static struct platform_driver mtk_vcp_ipc_driver = {
+ .probe = mtk_vcp_ipc_probe,
+ .remove = mtk_vcp_ipc_remove,
+ .driver = {
+ .name = "mtk-vcp-ipc",
+ },
+};
+builtin_platform_driver(mtk_vcp_ipc_driver);
+
+MODULE_AUTHOR("Jjian Zhou <jjian.zhou@mediatek.com>");
+MODULE_DESCRIPTION("MediaTek VCP IPC Controller");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/firmware/mediatek/mtk-vcp-ipc.h b/include/linux/firmware/mediatek/mtk-vcp-ipc.h
new file mode 100644
index 000000000000..dc34b0ba9dd8
--- /dev/null
+++ b/include/linux/firmware/mediatek/mtk-vcp-ipc.h
@@ -0,0 +1,151 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Copyright (c) 2024 MediaTek Inc.
+ */
+
+#ifndef __MTK_VCP_IPC_H__
+#define __MTK_VCP_IPC_H__
+
+#include <linux/completion.h>
+#include <linux/mailbox_client.h>
+#include <linux/mailbox/mtk-vcp-mailbox.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+/* IPI result definition */
+#define IPI_ACTION_DONE 0
+#define IPI_DEV_ILLEGAL -1 /* ipi device is not initialized */
+#define IPI_ALREADY_USED -2 /* the ipi has be registered */
+#define IPI_UNAVAILABLE -3 /* the ipi can't be found */
+#define IPI_NO_MSGBUF -4 /* receiver doesn't have message buffer */
+#define IPI_MSG_TOO_BIG -5 /* message length is larger than defined */
+#define IPI_MBOX_ERR -99 /* some error from rpmsg layer */
+
+/* mbox recv action definition */
+enum mtk_ipi_recv_opt {
+ MBOX_RECV_MESSAGE = 0,
+ MBOX_RECV_ACK = 1,
+};
+
+/* mbox table item number definition */
+#define send_item_num 3
+#define recv_item_num 4
+#define VCP_MBOX_NUM 5
+
+/* mbox slot size definition: 1 slot for 4 bytes */
+#define MBOX_SLOT_SIZE 0x4
+#define MBOX_MAX_PIN 32
+#define VCP_MBOX_NUM 5
+#define MBOX_SLOT_ALIGN 2
+
+struct mtk_vcp_ipc;
+struct mtk_ipi_chan_table;
+
+typedef int (*mbox_pin_cb_t)(u32 ipi_id, void *prdata, void *data, u32 len);
+
+/**
+ * mbox pin structure, this is for send definition,
+ * @offset: message offset in the slots of a mbox
+ * @msg_size: message used slots in the mbox, 4 bytes alignment
+ * @pin_index: bit offset in the mbox
+ * @ipi_id: ipi enum number
+ * @mbox_id: mbox number id
+ */
+struct mtk_mbox_send_table {
+ u32 offset;
+ u32 msg_size;
+ u32 pin_index;
+ u32 ipi_id;
+ u32 mbox_id;
+};
+
+/**
+ * mbox pin structure, this is for receive definition,
+ * @offset: message offset in the slots of a mbox
+ * @recv_opt: recv option, 0:receive ,1: response
+ * @msg_size: message used slots in the mbox, 4 bytes alignment
+ * @pin_index: bit offset in the mbox
+ * @ipi_id: ipi enum number
+ * @mbox_id: mbox number id
+ */
+struct mtk_mbox_recv_table {
+ u32 offset;
+ u32 recv_opt;
+ u32 msg_size;
+ u32 pin_index;
+ u32 ipi_id;
+ u32 mbox_id;
+};
+
+/**
+ * struct mtk_ipi_device - device for represent the tinysys using mtk ipi
+ * @name: name of tinysys device
+ * @id: device id (used to match between rpmsg drivers and devices)
+ * @vcp_ipc: vcp ipc structure for tinysys device
+ * @table: channel table with endpoint & channel_info & mbox_pin info
+ * @prdata: private data for the callback use
+ * @ipi_inited: set when vcp_ipi_device_register() done
+ */
+struct mtk_ipi_device {
+ const char *name;
+ struct mtk_vcp_ipc *vcp_ipc;
+ struct mtk_ipi_chan_table *table;
+ void *prdata;
+ int ipi_inited;
+};
+
+/**
+ * The mtk_mbox_table is a structure used to record the send
+ * table and recv table. The send table is used to record
+ * the feature ID and size of the sent data. The recv table
+ * is used to record the feature ID and size of the received
+ * data, and whether a callback needs to be invoked.
+ *
+ * Following are platform specific interfacer
+ * @recv_table: structure mtk_mbox_recv_table
+ * @send_table: structure mtk_mbox_send_table
+ * @recv_count: receive feature number in this channel
+ * @send_count: send feature number in this channel
+ */
+struct mtk_mbox_table {
+ struct mtk_mbox_recv_table recv_table[32];
+ struct mtk_mbox_send_table send_table[32];
+ u32 recv_count;
+ u32 send_count;
+};
+
+/**
+ * Mbox is a dedicate hardware of a tinysys consists of:
+ * 1) a share memory tightly coupled to the tinysys
+ * 2) several IRQs
+ *
+ * Following are platform specific interface
+ * @dev: vcp device
+ * @name: identity of the device
+ * @info_table: mbox info structure
+ * @ipi_priv: private data for synchronization layer
+ * @mbox_id: mbox number
+ * @mbdev: mtk_mbox_table structure
+ */
+struct mtk_vcp_ipc {
+ struct device *dev;
+ const char *name;
+ struct mtk_mbox_info *info_table;
+ void *ipi_priv;
+ void *mbox_id;
+ struct mtk_mbox_table *mbdev;
+};
+
+int mtk_vcp_ipc_device_register(struct mtk_ipi_device *ipidev,
+ u32 ipi_chan_count,
+ struct mtk_vcp_ipc *vcp_ipc);
+int mtk_vcp_ipc_send(struct mtk_ipi_device *ipidev, u32 ipi_id,
+ void *data, u32 len);
+int mtk_vcp_ipc_send_compl(struct mtk_ipi_device *ipidev, u32 ipi_id,
+ void *data, u32 len, u32 timeout_ms);
+int mtk_vcp_mbox_ipc_register(struct mtk_ipi_device *ipidev, int ipi_id,
+ mbox_pin_cb_t cb, void *prdata, void *msg);
+int mtk_vcp_mbox_ipc_unregister(struct mtk_ipi_device *ipidev, int ipi_id);
+
+#endif
--
2.46.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v3 4/6] remoteproc: mediatek: Add VCP ipi-mbox init driver
2026-03-20 3:18 [PATCH v3 0/6] ASoC: mediatek: Add VCP driver on Mediatek MT8196 SoC Xiangzhi Tang
` (2 preceding siblings ...)
2026-03-20 3:18 ` [PATCH v3 3/6] firmware: mediatek: Add VCP IPC protocol interfaces driver Xiangzhi Tang
@ 2026-03-20 3:18 ` Xiangzhi Tang
2026-03-20 3:18 ` [PATCH v3 5/6] remoteproc: mediatek: Add VCP ipi communication sync mechanism Xiangzhi Tang
2026-03-20 3:18 ` [PATCH v3 6/6] remoterpoc: mediatek: vcp: Add vcp suspned and resume feature Xiangzhi Tang
5 siblings, 0 replies; 11+ messages in thread
From: Xiangzhi Tang @ 2026-03-20 3:18 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, Project_Global_Chrome_Upstream_Group, Hailong Fan,
Huayu Zong, Xiangzhi Tang
Add VCP ipi-mbox init driver.
Signed-off-by: Xiangzhi Tang <xiangzhi.tang@mediatek.com>
---
drivers/remoteproc/Kconfig | 2 +
drivers/remoteproc/mtk_vcp_rproc.c | 98 +++++++++++++++++++++++
drivers/remoteproc/mtk_vcp_rproc.h | 6 ++
include/linux/remoteproc/mtk_vcp_public.h | 55 +++++++++++++
4 files changed, 161 insertions(+)
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index 93827f6fd3c5..54b416db0c0b 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -70,6 +70,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_rproc.c b/drivers/remoteproc/mtk_vcp_rproc.c
index bcb5f3786cf7..6e0fecef72ce 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>
@@ -54,6 +55,18 @@ 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;
@@ -93,6 +106,34 @@ 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))
+ return dev_err_probe(vcp->dev, PTR_ERR(pdev), "ipc_data register failed\n");
+
+ 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)
+ return dev_err_probe(vcp->dev, -EPROBE_DEFER, "get vcp_ipc drvdata failed\n");
+
+ ret = mtk_vcp_ipc_device_register(vcp->ipi_dev, VCP_IPI_COUNT, vcp_ipc);
+ if (ret)
+ dev_err_probe(vcp->dev, ret, "ipi_dev register failed, ret %d\n", 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)
@@ -138,7 +179,9 @@ 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;
@@ -166,6 +209,10 @@ static struct mtk_vcp_device *vcp_rproc_init(struct platform_device *pdev,
if (ret)
return ERR_PTR(dev_err_probe(dev, ret, "vcp_wdt_irq_init failed\n"));
+ ret = vcp_ipi_mbox_init(vcp);
+ if (ret)
+ return ERR_PTR(dev_err_probe(dev, ret, "vcp_ipi_mbox_init failed\n"));
+
pm_runtime_get_sync(dev);
return vcp;
@@ -284,6 +331,55 @@ static struct mtk_vcp_reserved_mem_table mt8196_memory_tb[NUMS_MEM_ID] = {
{ .memory_id = MMQOS_MEM_ID, .size = 0x1000 },
};
+static struct mtk_mbox_table mt8196_ipc_tb = {
+ .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_get_mem_phys = vcp_get_reserve_mem_phys,
@@ -296,6 +392,8 @@ static const struct mtk_vcp_of_data mt8196_of_data = {
.auto_boot = true,
.sysfs_read_only = true,
.rtos_static_iova = 0x180600000,
+ .ipc_data = &mt8196_ipc_tb,
+ .ipi_ops = &mt8196_vcp_ipi_ops,
.feature_tb = mt8196_feature_tb,
.memory_tb = mt8196_memory_tb,
.fw_name = "mediatek/mt8196/vcp.img",
diff --git a/drivers/remoteproc/mtk_vcp_rproc.h b/drivers/remoteproc/mtk_vcp_rproc.h
index 11ce0f6d562b..ff3e67fc2611 100644
--- a/drivers/remoteproc/mtk_vcp_rproc.h
+++ b/drivers/remoteproc/mtk_vcp_rproc.h
@@ -21,6 +21,7 @@
* @sram_offset: core sram memory layout
* @share_mem_iova: shared memory iova base
* @share_mem_size: shared memory size
+ * @vcp_ipidev: struct mtk_ipi_device
* @vcp_memory_tb: vcp memory allocated table
*/
struct mtk_vcp_of_cluster {
@@ -34,6 +35,7 @@ struct mtk_vcp_of_cluster {
u32 sram_offset[VCP_CORE_TOTAL];
dma_addr_t share_mem_iova;
size_t share_mem_size;
+ struct mtk_ipi_device vcp_ipidev;
struct vcp_reserve_mblock vcp_memory_tb[NUMS_MEM_ID];
};
@@ -43,6 +45,8 @@ 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
* @feature_tb: vcp feature table structure
* @memory_tb: vcp memory table structure
* @fw_name: vcp image name and path
@@ -51,6 +55,8 @@ 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;
struct mtk_vcp_feature_table *feature_tb;
struct mtk_vcp_reserved_mem_table *memory_tb;
char *fw_name;
diff --git a/include/linux/remoteproc/mtk_vcp_public.h b/include/linux/remoteproc/mtk_vcp_public.h
index 3bbc2055f9f8..58ba4b8bb023 100644
--- a/include/linux/remoteproc/mtk_vcp_public.h
+++ b/include/linux/remoteproc/mtk_vcp_public.h
@@ -7,8 +7,18 @@
#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)
+
+enum vcp_notify_event {
+ VCP_EVENT_READY = 0,
+ VCP_EVENT_STOP,
+ VCP_EVENT_SUSPEND,
+ VCP_EVENT_RESUME,
+};
+
enum vcp_reserve_mem_id {
VCP_RTOS_MEM_ID,
VDEC_MEM_ID,
@@ -36,15 +46,59 @@ enum vcp_feature_id {
NUM_FEATURE_ID,
};
+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 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 {
phys_addr_t (*vcp_get_mem_phys)(struct mtk_vcp_device *vcp,
enum vcp_reserve_mem_id id);
@@ -59,6 +113,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] 11+ messages in thread
* [PATCH v3 5/6] remoteproc: mediatek: Add VCP ipi communication sync mechanism
2026-03-20 3:18 [PATCH v3 0/6] ASoC: mediatek: Add VCP driver on Mediatek MT8196 SoC Xiangzhi Tang
` (3 preceding siblings ...)
2026-03-20 3:18 ` [PATCH v3 4/6] remoteproc: mediatek: Add VCP ipi-mbox init driver Xiangzhi Tang
@ 2026-03-20 3:18 ` Xiangzhi Tang
2026-03-20 3:18 ` [PATCH v3 6/6] remoterpoc: mediatek: vcp: Add vcp suspned and resume feature Xiangzhi Tang
5 siblings, 0 replies; 11+ messages in thread
From: Xiangzhi Tang @ 2026-03-20 3:18 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, Project_Global_Chrome_Upstream_Group, Hailong Fan,
Huayu Zong, Xiangzhi Tang
1.Add vcp ready ipi register driver.
2.Add vcp ready notify work mechanism.
3.Add vcp feature resgiter mechanism.
Signed-off-by: Xiangzhi Tang <xiangzhi.tang@mediatek.com>
---
drivers/remoteproc/mtk_vcp_common.c | 279 ++++++++++++++++++++++
drivers/remoteproc/mtk_vcp_common.h | 56 +++++
drivers/remoteproc/mtk_vcp_rproc.c | 62 ++++-
drivers/remoteproc/mtk_vcp_rproc.h | 18 ++
include/linux/remoteproc/mtk_vcp_public.h | 12 +
5 files changed, 425 insertions(+), 2 deletions(-)
diff --git a/drivers/remoteproc/mtk_vcp_common.c b/drivers/remoteproc/mtk_vcp_common.c
index 97ea8099912d..f3b506034e95 100644
--- a/drivers/remoteproc/mtk_vcp_common.c
+++ b/drivers/remoteproc/mtk_vcp_common.c
@@ -24,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);
+
phys_addr_t vcp_get_reserve_mem_phys(struct mtk_vcp_device *vcp,
enum vcp_reserve_mem_id id)
{
@@ -153,6 +156,40 @@ int vcp_reserve_memory_init(struct mtk_vcp_device *vcp)
return 0;
}
+static bool vcp_is_core_ready(struct mtk_vcp_device *vcp,
+ enum vcp_core_id core_id)
+{
+ 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];
+ }
+}
+
+static enum vcp_core_id get_core_by_feature(struct mtk_vcp_device *vcp,
+ enum vcp_feature_id id)
+{
+ for (u32 i = 0; i < NUM_FEATURE_ID; i++) {
+ if (vcp->platdata->feature_tb[i].feature_id == id)
+ return vcp->platdata->feature_tb[i].core_id;
+ }
+
+ return 0;
+}
+
+bool is_vcp_ready(struct mtk_vcp_device *vcp,
+ enum vcp_feature_id id)
+{
+ enum vcp_core_id core_id = get_core_by_feature(vcp, id);
+
+ return vcp_is_core_ready(vcp, core_id);
+}
+
int wait_core_hart_shutdown(struct mtk_vcp_device *vcp,
enum vcp_core_id core_id)
{
@@ -215,9 +252,120 @@ int wait_core_hart_shutdown(struct mtk_vcp_device *vcp,
return ret;
}
+void vcp_A_register_notify(struct mtk_vcp_device *vcp,
+ enum vcp_feature_id id,
+ struct notifier_block *nb)
+{
+ enum vcp_core_id core_id = get_core_by_feature(vcp, id);
+
+ switch (core_id) {
+ case VCP_ID:
+ blocking_notifier_chain_register(&vcp_notifier_list, nb);
+ if (vcp_is_core_ready(vcp, VCP_ID))
+ nb->notifier_call(nb, VCP_EVENT_READY, NULL);
+ break;
+ case MMUP_ID:
+ blocking_notifier_chain_register(&mmup_notifier_list, nb);
+ if (vcp_is_core_ready(vcp, MMUP_ID))
+ nb->notifier_call(nb, VCP_EVENT_READY, NULL);
+ break;
+ default:
+ dev_err(vcp->dev, "%s(), No Support core id\n", __func__);
+ break;
+ }
+}
+
+void vcp_A_unregister_notify(struct mtk_vcp_device *vcp,
+ enum vcp_feature_id id,
+ struct notifier_block *nb)
+{
+ enum vcp_core_id core_id = get_core_by_feature(vcp, 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:
+ dev_err(vcp->dev, "%s(), No Support core id\n", __func__);
+ 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_err(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 (!vcp_is_core_ready(vcp, VCP_ID))
+ vcp_A_set_ready(vcp, VCP_ID);
+ break;
+ case IPI_IN_VCP_READY_1:
+ if (!vcp_is_core_ready(vcp, MMUP_ID))
+ vcp_A_set_ready(vcp, MMUP_ID);
+ break;
+ default:
+ dev_err(vcp->dev, "%s(), No Support ipi id\n", __func__);
+ break;
+ }
+
+ return 0;
+}
+
int reset_vcp(struct mtk_vcp_device *vcp)
{
struct arm_smccc_res res;
+ bool mmup_status, vcp_status;
+ int ret;
if (vcp->vcp_cluster->core_nums > MMUP_ID) {
writel((u32)VCP_PACK_IOVA(vcp->vcp_cluster->share_mem_iova),
@@ -228,6 +376,16 @@ 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);
+
+ ret = read_poll_timeout(vcp_is_core_ready,
+ mmup_status, mmup_status,
+ USEC_PER_MSEC,
+ VCP_READY_TIMEOUT_MS * USEC_PER_MSEC,
+ false, vcp, MMUP_ID);
+ if (ret) {
+ dev_err(vcp->dev, "MMUP_ID bootup timeout. Stop vcp booting\n");
+ return ret;
+ }
}
writel((u32)VCP_PACK_IOVA(vcp->vcp_cluster->share_mem_iova),
@@ -239,6 +397,127 @@ int reset_vcp(struct mtk_vcp_device *vcp)
MTK_TINYSYS_VCP_KERNEL_OP_RESET_RELEASE,
1, 0, 0, 0, 0, 0, &res);
+ ret = read_poll_timeout(vcp_is_core_ready,
+ vcp_status, vcp_status,
+ USEC_PER_MSEC,
+ VCP_READY_TIMEOUT_MS * USEC_PER_MSEC,
+ false, vcp, VCP_ID);
+ if (ret) {
+ dev_err(vcp->dev, "VCP_ID bootup timeout. Stop vcp booting\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static int vcp_enable_pm_clk(struct mtk_vcp_device *vcp, enum vcp_feature_id id)
+{
+ struct vcp_slp_ctrl slp_data;
+ bool suspend_status;
+ int ret;
+
+ if (vcp->vcp_cluster->feature_enable[id]) {
+ dev_err(vcp->dev, "%s feature(id=%d) already enabled\n",
+ __func__, id);
+ return -EINVAL;
+ }
+
+ if (id != RTOS_FEATURE_ID) {
+ slp_data.cmd = SLP_WAKE_LOCK;
+ slp_data.feature = id;
+ ret = vcp->ipi_ops->ipi_send_compl(vcp->ipi_dev, IPI_OUT_C_SLEEP_0,
+ &slp_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;
+ }
+ }
+
+ return 0;
+}
+
+static int vcp_disable_pm_clk(struct mtk_vcp_device *vcp, enum vcp_feature_id id)
+{
+ struct vcp_slp_ctrl slp_data;
+ bool suspend_status;
+ int ret;
+
+ if (!vcp->vcp_cluster->feature_enable[id]) {
+ dev_err(vcp->dev, "%s feature(id=%d) already disabled\n",
+ __func__, id);
+ return -EINVAL;
+ }
+
+ if (id != RTOS_FEATURE_ID) {
+ slp_data.cmd = SLP_WAKE_UNLOCK;
+ slp_data.feature = id;
+ ret = vcp->ipi_ops->ipi_send_compl(vcp->ipi_dev, IPI_OUT_C_SLEEP_0,
+ &slp_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;
+ }
+ }
+
+ return 0;
+}
+
+int vcp_A_register_feature(struct mtk_vcp_device *vcp, enum vcp_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);
+ ret = vcp_enable_pm_clk(vcp, id);
+ if (ret)
+ dev_err(vcp->dev, "%s feature(id=%d) register failed\n",
+ __func__, id);
+ else
+ vcp->vcp_cluster->feature_enable[id] = true;
+ mutex_unlock(&vcp->vcp_cluster->vcp_feature_mutex);
+
+ return ret;
+}
+
+int vcp_A_deregister_feature(struct mtk_vcp_device *vcp, enum vcp_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);
+ ret = vcp_disable_pm_clk(vcp, id);
+ if (ret)
+ dev_err(vcp->dev, "%s feature(id=%d) deregister failed\n",
+ __func__, id);
+ else
+ vcp->vcp_cluster->feature_enable[id] = false;
+ mutex_unlock(&vcp->vcp_cluster->vcp_feature_mutex);
+
+ 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)
+ return dev_err_probe(vcp->dev, -EINVAL, "vcp_workqueue create fail\n");
+
+ 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;
}
diff --git a/drivers/remoteproc/mtk_vcp_common.h b/drivers/remoteproc/mtk_vcp_common.h
index d048757c955a..8b19fcb78a79 100644
--- a/drivers/remoteproc/mtk_vcp_common.h
+++ b/drivers/remoteproc/mtk_vcp_common.h
@@ -13,10 +13,13 @@
#include <linux/remoteproc/mtk_vcp_public.h>
/* VCP timeout definition */
+#define VCP_READY_TIMEOUT_MS 3000
+#define VCP_IPI_DEV_READY_TIMEOUT 1000
#define CORE_HART_SHUTDOWN_TIMEOUT_MS 10
/* VCP platform definition */
#define DMA_MAX_MASK_BIT 33
+#define PIN_OUT_C_SIZE_SLEEP_0 2
/* VCP load image definition */
#define VCM_IMAGE_MAGIC (0x58881688)
@@ -90,6 +93,14 @@ enum vcp_core_id {
VCP_CORE_TOTAL,
};
+enum vcp_slp_cmd {
+ SLP_WAKE_LOCK = 0,
+ SLP_WAKE_UNLOCK,
+ SLP_STATUS_DBG,
+ SLP_SUSPEND,
+ SLP_RESUME,
+};
+
enum mtk_tinysys_vcp_kernel_op {
MTK_TINYSYS_VCP_KERNEL_OP_RESET_SET = 0,
MTK_TINYSYS_VCP_KERNEL_OP_RESET_RELEASE,
@@ -154,6 +165,32 @@ struct vcp_reserve_mblock {
size_t size;
};
+/**
+ * struct vcp_slp_ctrl - sleep ctrl data sync with AP and VCP
+ *
+ * @feature: Feature id
+ * @cmd: sleep cmd flag.
+ */
+struct vcp_slp_ctrl {
+ u32 feature;
+ u32 cmd;
+};
+
+/**
+ * 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.
*
@@ -201,6 +238,20 @@ struct vcp_region_info_st {
u32 coredump_dram_offset;
};
+int vcp_A_ready_ipi_handler(u32 id, void *prdata,
+ void *data, u32 len);
+bool is_vcp_ready(struct mtk_vcp_device *vcp,
+ enum vcp_feature_id id);
+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(struct mtk_vcp_device *vcp,
+ enum vcp_feature_id id,
+ struct notifier_block *nb);
+void vcp_A_unregister_notify(struct mtk_vcp_device *vcp,
+ enum vcp_feature_id id,
+ struct notifier_block *nb);
+
int vcp_reserve_memory_init(struct mtk_vcp_device *vcp);
phys_addr_t vcp_get_reserve_mem_phys(struct mtk_vcp_device *vcp, enum vcp_reserve_mem_id id);
dma_addr_t vcp_get_reserve_mem_iova(struct mtk_vcp_device *vcp, enum vcp_reserve_mem_id id);
@@ -213,5 +264,10 @@ int mtk_vcp_load(struct rproc *rproc, const struct firmware *fw);
int vcp_wdt_irq_init(struct mtk_vcp_device *vcp);
+int vcp_A_register_feature(struct mtk_vcp_device *vcp,
+ enum vcp_feature_id id);
+int vcp_A_deregister_feature(struct mtk_vcp_device *vcp,
+ enum vcp_feature_id id);
+
int 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
index 6e0fecef72ce..833a0dc69d9c 100644
--- a/drivers/remoteproc/mtk_vcp_rproc.c
+++ b/drivers/remoteproc/mtk_vcp_rproc.c
@@ -71,6 +71,30 @@ 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);
+ 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,
@@ -83,10 +107,22 @@ static int mtk_vcp_start(struct rproc *rproc)
1, 0, 0, 0, 0, 0, &res);
ret = reset_vcp(vcp);
- if (ret)
+ if (ret) {
dev_err(vcp->dev, "bootup fail\n");
- else
+ } else {
dev_info(vcp->dev, "bootup successfully\n");
+ if (vcp_A_register_feature(vcp, RTOS_FEATURE_ID) < 0)
+ vcp_A_deregister_feature(vcp, RTOS_FEATURE_ID);
+ }
+
+ 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);
+slp_ipi_unregister:
+ vcp->ipi_ops->ipi_unregister(vcp->ipi_dev, IPI_OUT_C_SLEEP_0);
return ret;
}
@@ -97,6 +133,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;
}
@@ -185,6 +224,8 @@ static struct mtk_vcp_device *vcp_rproc_init(struct platform_device *pdev,
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_ready_mutex);
platform_set_drvdata(pdev, vcp);
ret = vcp_reserve_memory_init(vcp);
@@ -213,6 +254,10 @@ static struct mtk_vcp_device *vcp_rproc_init(struct platform_device *pdev,
if (ret)
return ERR_PTR(dev_err_probe(dev, ret, "vcp_ipi_mbox_init failed\n"));
+ ret = vcp_notify_work_init(vcp);
+ if (ret)
+ return ERR_PTR(dev_err_probe(dev, ret, "vcp_notify_work_init failed\n"));
+
pm_runtime_get_sync(dev);
return vcp;
@@ -287,6 +332,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);
@@ -297,6 +344,12 @@ static void vcp_device_shutdown(struct platform_device *pdev)
struct mtk_vcp_device *vcp = platform_get_drvdata(pdev);
int 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);
ret = wait_core_hart_shutdown(vcp, VCP_ID);
if (ret)
@@ -382,6 +435,11 @@ 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_register_feature = vcp_A_register_feature,
+ .vcp_deregister_feature = vcp_A_deregister_feature,
+ .vcp_register_notify = vcp_A_register_notify,
+ .vcp_unregister_notify = vcp_A_unregister_notify,
.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,
diff --git a/drivers/remoteproc/mtk_vcp_rproc.h b/drivers/remoteproc/mtk_vcp_rproc.h
index ff3e67fc2611..600715b77124 100644
--- a/drivers/remoteproc/mtk_vcp_rproc.h
+++ b/drivers/remoteproc/mtk_vcp_rproc.h
@@ -19,10 +19,19 @@
* @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
+ * @slp_ipi_ack_data: sleep ipi msg data
+ * @feature_enable: feature status 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_ready_mutex: vcp core ready mutex structure
* @vcp_ipidev: struct mtk_ipi_device
+ * @vcp_workqueue: ready workqueue_struct
* @vcp_memory_tb: vcp memory allocated table
+ * @vcp_ready_notify_wk: vcp_work_struct structure
*/
struct mtk_vcp_of_cluster {
void __iomem *sram_base;
@@ -33,10 +42,19 @@ 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;
+ u32 slp_ipi_ack_data;
+ bool feature_enable[NUM_FEATURE_ID];
+ 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_ready_mutex;
struct mtk_ipi_device vcp_ipidev;
+ struct workqueue_struct *vcp_workqueue;
struct vcp_reserve_mblock vcp_memory_tb[NUMS_MEM_ID];
+ struct vcp_work_struct vcp_ready_notify_wk[VCP_CORE_TOTAL];
};
/**
diff --git a/include/linux/remoteproc/mtk_vcp_public.h b/include/linux/remoteproc/mtk_vcp_public.h
index 58ba4b8bb023..b9e1d86685fd 100644
--- a/include/linux/remoteproc/mtk_vcp_public.h
+++ b/include/linux/remoteproc/mtk_vcp_public.h
@@ -100,6 +100,18 @@ struct mtk_vcp_ipi_ops {
};
struct mtk_vcp_ops {
+ bool (*vcp_is_ready)(struct mtk_vcp_device *vcp,
+ enum vcp_feature_id id);
+ int (*vcp_register_feature)(struct mtk_vcp_device *vcp,
+ enum vcp_feature_id id);
+ int (*vcp_deregister_feature)(struct mtk_vcp_device *vcp,
+ enum vcp_feature_id id);
+ void (*vcp_register_notify)(struct mtk_vcp_device *vcp,
+ enum vcp_feature_id id,
+ struct notifier_block *nb);
+ void (*vcp_unregister_notify)(struct mtk_vcp_device *vcp,
+ enum vcp_feature_id id,
+ struct notifier_block *nb);
phys_addr_t (*vcp_get_mem_phys)(struct mtk_vcp_device *vcp,
enum vcp_reserve_mem_id id);
dma_addr_t (*vcp_get_mem_iova)(struct mtk_vcp_device *vcp,
--
2.46.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v3 6/6] remoterpoc: mediatek: vcp: Add vcp suspned and resume feature
2026-03-20 3:18 [PATCH v3 0/6] ASoC: mediatek: Add VCP driver on Mediatek MT8196 SoC Xiangzhi Tang
` (4 preceding siblings ...)
2026-03-20 3:18 ` [PATCH v3 5/6] remoteproc: mediatek: Add VCP ipi communication sync mechanism Xiangzhi Tang
@ 2026-03-20 3:18 ` Xiangzhi Tang
5 siblings, 0 replies; 11+ messages in thread
From: Xiangzhi Tang @ 2026-03-20 3:18 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, Project_Global_Chrome_Upstream_Group, Hailong Fan,
Huayu Zong, Xiangzhi Tang
1.Add vcp suspend and resume callback
Signed-off-by: Xiangzhi Tang <xiangzhi.tang@mediatek.com>
---
drivers/remoteproc/mtk_vcp_common.c | 114 ++++++++++++++++++++++
drivers/remoteproc/mtk_vcp_common.h | 6 ++
drivers/remoteproc/mtk_vcp_rproc.c | 59 +++++++++++
drivers/remoteproc/mtk_vcp_rproc.h | 2 +
include/linux/remoteproc/mtk_vcp_public.h | 1 +
5 files changed, 182 insertions(+)
diff --git a/drivers/remoteproc/mtk_vcp_common.c b/drivers/remoteproc/mtk_vcp_common.c
index f3b506034e95..f4952fbace3c 100644
--- a/drivers/remoteproc/mtk_vcp_common.c
+++ b/drivers/remoteproc/mtk_vcp_common.c
@@ -190,6 +190,11 @@ bool is_vcp_ready(struct mtk_vcp_device *vcp,
return vcp_is_core_ready(vcp, core_id);
}
+bool is_vcp_suspending(struct mtk_vcp_device *vcp)
+{
+ return vcp->vcp_cluster->is_suspending ? true : false;
+}
+
int wait_core_hart_shutdown(struct mtk_vcp_device *vcp,
enum vcp_core_id core_id)
{
@@ -252,6 +257,95 @@ int wait_core_hart_shutdown(struct mtk_vcp_device *vcp,
return ret;
}
+void vcp_wait_core_stop(struct mtk_vcp_device *vcp, enum vcp_core_id core_id)
+{
+ u32 status;
+ u32 stop_ctrl;
+ int ret;
+
+ stop_ctrl = (vcp->vcp_cluster->twohart[core_id] ?
+ (B_CORE_GATED | B_HART0_HALT | B_HART1_HALT | B_CORE_AXIS_BUSY) :
+ (B_CORE_GATED | B_HART0_HALT | B_CORE_AXIS_BUSY));
+
+ switch (core_id) {
+ case VCP_ID:
+ ret = readl_poll_timeout(vcp->vcp_cluster->cfg + R_CORE0_STATUS,
+ status,
+ (status & stop_ctrl) == (stop_ctrl & ~B_CORE_AXIS_BUSY),
+ USEC_PER_MSEC,
+ CORE_HART_SHUTDOWN_TIMEOUT_MS * USEC_PER_MSEC);
+ if (ret) {
+ dev_err(vcp->dev, "wait [%s] core stop timeout, current status 0x%x\n",
+ core_id ? "MMUP_ID" : "VCP_ID", status);
+ return;
+ }
+ break;
+ case MMUP_ID:
+ ret = readl_poll_timeout(vcp->vcp_cluster->cfg + R_CORE1_STATUS,
+ status,
+ (status & stop_ctrl) == (stop_ctrl & ~B_CORE_AXIS_BUSY),
+ USEC_PER_MSEC,
+ CORE_HART_SHUTDOWN_TIMEOUT_MS * USEC_PER_MSEC);
+ if (ret) {
+ dev_err(vcp->dev, "wait [%s] core stop timeout, current status 0x%x\n",
+ core_id ? "MMUP_ID" : "VCP_ID", status);
+ return;
+ }
+ break;
+ default:
+ dev_err(vcp->dev, "%s(), No Support core id\n", __func__);
+ break;
+ }
+}
+
+static bool vcp_get_suspend_resume_status(struct mtk_vcp_device *vcp)
+{
+ if (vcp->vcp_cluster->core_nums > MMUP_ID)
+ return !!(readl(vcp->vcp_cluster->cfg_sec + R_GPR3_SEC) & VCP_AP_SUSPEND) &&
+ !!(readl(vcp->vcp_cluster->cfg_sec + R_GPR2_SEC) & MMUP_AP_SUSPEND);
+
+ return !!(readl(vcp->vcp_cluster->cfg_sec + R_GPR3_SEC) & VCP_AP_SUSPEND);
+}
+
+void vcp_wait_suspend_resume(struct mtk_vcp_device *vcp, bool suspend)
+{
+ bool status;
+ int ret;
+
+ if (suspend) {
+ writel(B_CORE0_SUSPEND, vcp->vcp_cluster->cfg_core + AP_R_GPR2);
+ writel(SUSPEND_MAGIC, vcp->vcp_cluster->cfg + VCP_C0_GPR0_SUSPEND_RESUME);
+ if (vcp->vcp_cluster->core_nums > MMUP_ID) {
+ writel(B_CORE1_SUSPEND, vcp->vcp_cluster->cfg_core + AP_R_GPR3);
+ writel(SUSPEND_MAGIC, vcp->vcp_cluster->cfg + VCP_C1_GPR0_SUSPEND_RESUME);
+ }
+ } else {
+ writel(B_CORE0_RESUME, vcp->vcp_cluster->cfg_core + AP_R_GPR2);
+ writel(RESUME_MAGIC, vcp->vcp_cluster->cfg + VCP_C0_GPR0_SUSPEND_RESUME);
+ if (vcp->vcp_cluster->core_nums > MMUP_ID) {
+ writel(B_CORE1_RESUME, vcp->vcp_cluster->cfg_core + AP_R_GPR3);
+ writel(RESUME_MAGIC, vcp->vcp_cluster->cfg + VCP_C1_GPR0_SUSPEND_RESUME);
+ }
+ }
+
+ writel(B_GIPC4_SETCLR_3, vcp->vcp_cluster->cfg_core + R_GIPC_IN_SET);
+
+ ret = read_poll_timeout(vcp_get_suspend_resume_status,
+ status, (status == suspend),
+ USEC_PER_MSEC,
+ SUSPEND_WAIT_TIMEOUT_MS * USEC_PER_MSEC,
+ false, vcp);
+ if (ret)
+ 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(struct mtk_vcp_device *vcp,
enum vcp_feature_id id,
struct notifier_block *nb)
@@ -416,6 +510,16 @@ static int vcp_enable_pm_clk(struct mtk_vcp_device *vcp, enum vcp_feature_id id)
bool suspend_status;
int ret;
+ ret = read_poll_timeout(is_vcp_suspending,
+ suspend_status, !suspend_status,
+ USEC_PER_MSEC,
+ SUSPEND_WAIT_TIMEOUT_MS * USEC_PER_MSEC,
+ false, vcp);
+ if (ret) {
+ dev_err(vcp->dev, "%s blocked by vcp suspend\n", __func__);
+ return ret;
+ }
+
if (vcp->vcp_cluster->feature_enable[id]) {
dev_err(vcp->dev, "%s feature(id=%d) already enabled\n",
__func__, id);
@@ -443,6 +547,16 @@ static int vcp_disable_pm_clk(struct mtk_vcp_device *vcp, enum vcp_feature_id id
bool suspend_status;
int ret;
+ ret = read_poll_timeout(is_vcp_suspending,
+ suspend_status, !suspend_status,
+ USEC_PER_MSEC,
+ SUSPEND_WAIT_TIMEOUT_MS * USEC_PER_MSEC,
+ false, vcp);
+ if (ret) {
+ dev_err(vcp->dev, "%s blocked by vcp suspend\n", __func__);
+ return ret;
+ }
+
if (!vcp->vcp_cluster->feature_enable[id]) {
dev_err(vcp->dev, "%s feature(id=%d) already disabled\n",
__func__, id);
diff --git a/drivers/remoteproc/mtk_vcp_common.h b/drivers/remoteproc/mtk_vcp_common.h
index 8b19fcb78a79..eff2199c7610 100644
--- a/drivers/remoteproc/mtk_vcp_common.h
+++ b/drivers/remoteproc/mtk_vcp_common.h
@@ -16,9 +16,12 @@
#define VCP_READY_TIMEOUT_MS 3000
#define VCP_IPI_DEV_READY_TIMEOUT 1000
#define CORE_HART_SHUTDOWN_TIMEOUT_MS 10
+#define SUSPEND_WAIT_TIMEOUT_MS 100
/* VCP platform definition */
#define DMA_MAX_MASK_BIT 33
+#define RESUME_MAGIC 0x12345678
+#define SUSPEND_MAGIC 0x87654321
#define PIN_OUT_C_SIZE_SLEEP_0 2
/* VCP load image definition */
@@ -269,5 +272,8 @@ int vcp_A_register_feature(struct mtk_vcp_device *vcp,
int vcp_A_deregister_feature(struct mtk_vcp_device *vcp,
enum vcp_feature_id id);
+bool is_vcp_suspending(struct mtk_vcp_device *vcp);
int 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_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 833a0dc69d9c..a44a8081e7a3 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"
@@ -67,6 +68,57 @@ 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);
+
+ for (u32 id = RTOS_FEATURE_ID + 1; id < NUM_FEATURE_ID; id++) {
+ if (vcp->vcp_cluster->feature_enable[id]) {
+ dev_err(vcp->dev, "%s Check feature(id=%d) statue(%d)\n",
+ __func__, id,
+ vcp->vcp_cluster->feature_enable[id]);
+ return -EINVAL;
+ }
+ }
+
+ if (!vcp->vcp_cluster->is_suspending) {
+ 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);
+ }
+ 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) {
+ pm_runtime_get_sync(dev);
+
+ 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;
@@ -436,6 +488,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_feature = vcp_A_register_feature,
.vcp_deregister_feature = vcp_A_deregister_feature,
.vcp_register_notify = vcp_A_register_notify,
@@ -458,6 +511,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},
{}
@@ -471,6 +529,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 600715b77124..e3a76e368fd7 100644
--- a/drivers/remoteproc/mtk_vcp_rproc.h
+++ b/drivers/remoteproc/mtk_vcp_rproc.h
@@ -23,6 +23,7 @@
* @msg_vcp_ready1: core1 ready ipi msg data
* @slp_ipi_ack_data: sleep ipi msg data
* @feature_enable: feature status 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
@@ -46,6 +47,7 @@ struct mtk_vcp_of_cluster {
u32 msg_vcp_ready1;
u32 slp_ipi_ack_data;
bool feature_enable[NUM_FEATURE_ID];
+ 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 b9e1d86685fd..dbdcf5aa0f99 100644
--- a/include/linux/remoteproc/mtk_vcp_public.h
+++ b/include/linux/remoteproc/mtk_vcp_public.h
@@ -100,6 +100,7 @@ struct mtk_vcp_ipi_ops {
};
struct mtk_vcp_ops {
+ bool (*vcp_is_suspending)(struct mtk_vcp_device *vcp);
bool (*vcp_is_ready)(struct mtk_vcp_device *vcp,
enum vcp_feature_id id);
int (*vcp_register_feature)(struct mtk_vcp_device *vcp,
--
2.46.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v3 1/6] dt-bindings: remoteproc: Add VCP support for mt8196
2026-03-20 3:18 ` [PATCH v3 1/6] dt-bindings: remoteproc: Add VCP support for mt8196 Xiangzhi Tang
@ 2026-03-20 5:25 ` Rob Herring (Arm)
2026-03-20 9:01 ` Krzysztof Kozlowski
1 sibling, 0 replies; 11+ messages in thread
From: Rob Herring (Arm) @ 2026-03-20 5:25 UTC (permalink / raw)
To: Xiangzhi Tang
Cc: Mathieu Poirier, Krzysztof Kozlowski, Matthias Brugger,
Bjorn Andersson, linux-mediatek, Hailong Fan,
AngeloGioacchino Del Regno, Xiangzhi Tang, Huayu Zong, devicetree,
linux-kernel, linux-remoteproc,
Project_Global_Chrome_Upstream_Group, linux-arm-kernel,
Conor Dooley
On Fri, 20 Mar 2026 11:18:03 +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 | 161 ++++++++++++++++++
> 1 file changed, 161 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:140: 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:1614: dt_binding_check] Error 2
make: *** [Makefile:248: __sub-make] Error 2
doc reference errors (make refcheckdocs):
See https://patchwork.kernel.org/project/devicetree/patch/20260320032014.13608-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] 11+ messages in thread
* Re: [PATCH v3 1/6] dt-bindings: remoteproc: Add VCP support for mt8196
2026-03-20 3:18 ` [PATCH v3 1/6] dt-bindings: remoteproc: Add VCP support for mt8196 Xiangzhi Tang
2026-03-20 5:25 ` Rob Herring (Arm)
@ 2026-03-20 9:01 ` Krzysztof Kozlowski
1 sibling, 0 replies; 11+ messages in thread
From: Krzysztof Kozlowski @ 2026-03-20 9:01 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, Project_Global_Chrome_Upstream_Group, Hailong Fan,
Huayu Zong
On 20/03/2026 04:18, Xiangzhi Tang wrote:
> Add the new binding document for MediaTek Video Companion
> Processor(VCP) on MediaTek mt8196.
This wasn't tested/checked, so only limited review. Please do not send
untested code.
...
> + mboxes:
> + maxItems: 5
You need to list the items
> +
> + mbox-names:
> + maxItems: 5
You need to list the items. Open any binding - there is no such syntax
alone, right?
> +
> + 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:
There is no such company or prefix as mtk. You already received feedback
for this.
> + enum: [0, 1]
> + $ref: /schemas/types.yaml#/definitions/uint32
What is the point of this property?
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v3 2/6] remoteproc: Mediatek: Add VCP remoteproc driver
2026-03-20 3:18 ` [PATCH v3 2/6] remoteproc: Mediatek: Add VCP remoteproc driver Xiangzhi Tang
@ 2026-03-20 9:15 ` Krzysztof Kozlowski
0 siblings, 0 replies; 11+ messages in thread
From: Krzysztof Kozlowski @ 2026-03-20 9:15 UTC (permalink / raw)
To: Xiangzhi Tang
Cc: Bjorn Andersson, Mathieu Poirier, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, linux-remoteproc, devicetree,
linux-kernel, linux-arm-kernel, linux-mediatek,
Project_Global_Chrome_Upstream_Group, Hailong Fan, Huayu Zong
On Fri, Mar 20, 2026 at 11:18:04AM +0800, Xiangzhi Tang wrote:
> +
> +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,
There is no such thing as remove_new (anymore) and it is a proof thar
you are using some obsolete kernel, so it cannot be taken upstream.
Please work on upstream/mainline kernel.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v3 3/6] firmware: mediatek: Add VCP IPC protocol interfaces driver
2026-03-20 3:18 ` [PATCH v3 3/6] firmware: mediatek: Add VCP IPC protocol interfaces driver Xiangzhi Tang
@ 2026-08-13 22:10 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-08-13 22:10 UTC (permalink / raw)
To: Xiangzhi Tang; +Cc: conor+dt, robh, devicetree
Thank you for your contribution! Sashiko AI review found 10 potential issue(s) to consider:
- [High] Concurrent messages on different IPI channels sharing the same mailbox will silently corrupt shared metadata.
- [High] Missing unregister API permanently leaks ipi_chan_table and causes a dangling pointer.
- [High] Missing `reinit_completion()` allows late interrupts to falsely complete subsequent requests.
- [High] Assigning -ENOENT to the u32 mbox index causes a massive out-of-bounds array access.
- [High] Comparing payload length in bytes directly with slot capacity incorrectly rejects valid messages.
- [High] Lack of synchronization in `mtk_vcp_mbox_ipc_unregister` causes a NULL pointer dereference race with the receive handler.
- [High] `mtk_vcp_ipc_recv` dereferences `vcp_ipc->ipi_priv` before it is initialized.
- [Medium] Error path for `setup_mbox_table()` leaks memory and mailbox channels.
- [Medium] Spurious completions due to checking an uninitialized field (`table->recv_opt`).
- [Medium] Exported APIs use user-supplied IDs as array indices without bounds checking.
--
--- Patch [3]: [PATCH v3 3/6] firmware: mediatek: Add VCP IPC protocol interfaces driver ---
Note: The format of this review has been altered due to recitation restrictions. I am providing a free-form summary of the findings without directly quoting the original patch code.
commit f8da7c9eaf7b9e57116dc5cb1b79d9e5e51adfc9
Author: Xiangzhi Tang <xiangzhi.tang@mediatek.com>
Subject: firmware: mediatek: Add VCP IPC protocol interfaces driver
This patch adds support for the MediaTek VCP IPC between the host AP and the firmware running on the VCP. It offers send and receive interfaces using mediatek-mailbox APIs.
[Severity: High]
In mtk_vcp_ipc_send() and mtk_vcp_ipc_send_compl(), the code acquires a
per-channel lock (table[id].mutex_send) but then modifies the shared
per-mailbox state (minfo->ipi_info.msg). If multiple threads send messages
concurrently on different IPI channels mapped to the same mailbox, won't
this overwrite the shared message metadata? Since the mailbox framework
queues this asynchronously, this might lead to corrupted or duplicated
payloads being sent.
[Severity: High]
The mtk_vcp_ipc_device_register() function allocates ipi_chan_table and
sets the global vcp_ipc->ipi_priv pointer, but there doesn't seem to be a
corresponding unregister function. If the client device module unbinds or
is unloaded, doesn't this permanently leak the allocated table and leave a
dangling pointer in vcp_ipc->ipi_priv that will crash on the next mailbox
interrupt?
[Severity: High]
In mtk_vcp_ipc_send_compl(), wait_for_completion_timeout() is used on
table[id].notify. However, reinit_completion() is never called before
starting a new request. If a previous request times out and the hardware
interrupt fires late (calling complete()), won't the next call to
mtk_vcp_ipc_send_compl() immediately consume the stale token and return
success prematurely?
[Severity: High]
In mtk_fill_in_entry(), the u32 mbox index is assigned -ENOENT if an IPI
mapping is missing. This underflows to a massive unsigned value. Later in
mtk_vcp_ipc_send(), this index is used to look up minfo:
minfo = &ipidev->vcp_ipc->info_table[table[id].mbox].
Because this simply computes an out-of-bounds pointer, the subsequent NULL
check (!minfo) will pass. Will this cause arbitrary memory corruption when
the driver writes to minfo->ipi_info?
[Severity: High]
In mtk_vcp_ipc_send(), the caller-provided length (len) is compared directly
against table[id].msg_size. However, the receive path shows that msg_size
represents the number of 4-byte slots, whereas len is in bytes. If a client
sends an 8-byte message on a 2-slot channel, won't 8 > 2 evaluate to true
and incorrectly reject the valid message with IPI_MSG_TOO_BIG?
[Severity: Medium]
In mtk_vcp_ipc_probe(), if setup_mbox_table() returns an error for any
channel other than the first one, the function immediately returns the error
code. Does this error path leak the previously allocated info_table memory
and fail to release the previously acquired mailbox channels using
mbox_free_channel()?
[Severity: Medium]
In mtk_vcp_ipc_recv(), there is a check comparing table->recv_opt to
MBOX_RECV_MESSAGE. However, recv_opt doesn't appear to be populated from
the platform data during mtk_fill_in_entry(), leaving it initialized as 0
(MBOX_RECV_MESSAGE). Won't this condition always evaluate to true, falsely
triggering a complete() for every received message even if it is meant to
be ignored?
[Severity: High]
In mtk_vcp_mbox_ipc_unregister(), table[id].pin_buf is set to NULL without
any locking. Concurrently, an interrupt might fire and enter
mtk_vcp_ipc_recv(). If the receive handler passes the NULL check but the
unregister thread clears the pointer right before the memcpy executes,
could this cause a NULL pointer dereference or a use-after-free if the
client frees the buffer?
[Severity: High]
During mtk_vcp_ipc_probe(), mbox_request_channel_byname() is called, which
registers mtk_vcp_ipc_recv and enables interrupts. But vcp_ipc->ipi_priv
is only initialized later during client registration. If a pending message
or spurious interrupt triggers the handler early, won't mtk_vcp_ipc_recv()
dereference a NULL ipidev pointer and panic the kernel?
[Severity: Medium]
Exported APIs like mtk_vcp_ipc_send() and mtk_vcp_mbox_ipc_register() take
an id parameter from the caller and directly use it to index ipidev->table
without any bounds checking. Since ipi_chan_count is not saved during
registration, bounds validation isn't currently possible. Can a bad ID
passed from a client module silently corrupt adjacent heap memory?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260320032014.13608-1-xiangzhi.tang@mediatek.com?part=3
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-08-13 22:10 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-20 3:18 [PATCH v3 0/6] ASoC: mediatek: Add VCP driver on Mediatek MT8196 SoC Xiangzhi Tang
2026-03-20 3:18 ` [PATCH v3 1/6] dt-bindings: remoteproc: Add VCP support for mt8196 Xiangzhi Tang
2026-03-20 5:25 ` Rob Herring (Arm)
2026-03-20 9:01 ` Krzysztof Kozlowski
2026-03-20 3:18 ` [PATCH v3 2/6] remoteproc: Mediatek: Add VCP remoteproc driver Xiangzhi Tang
2026-03-20 9:15 ` Krzysztof Kozlowski
2026-03-20 3:18 ` [PATCH v3 3/6] firmware: mediatek: Add VCP IPC protocol interfaces driver Xiangzhi Tang
2026-08-13 22:10 ` sashiko-bot
2026-03-20 3:18 ` [PATCH v3 4/6] remoteproc: mediatek: Add VCP ipi-mbox init driver Xiangzhi Tang
2026-03-20 3:18 ` [PATCH v3 5/6] remoteproc: mediatek: Add VCP ipi communication sync mechanism Xiangzhi Tang
2026-03-20 3:18 ` [PATCH v3 6/6] remoterpoc: mediatek: vcp: Add vcp suspned and resume feature Xiangzhi Tang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox