* [PATCH v8 01/14] media: mediatek: vcodec: support vcp architecture
2026-10-10 8:35 [PATCH v8 00/14] media: mediatek: vcodec: support video decoder in mt8196 Kyrie Wu
@ 2026-10-10 8:36 ` Kyrie Wu
2026-10-10 8:36 ` [PATCH v8 02/14] media: mediatek: vcodec: add driver to support vcp Kyrie Wu
` (12 subsequent siblings)
13 siblings, 0 replies; 24+ messages in thread
From: Kyrie Wu @ 2026-10-10 8:36 UTC (permalink / raw)
To: Tiffany Lin, Andrew-CT Chen, Yunfei Dong, Mauro Carvalho Chehab,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Kyrie Wu, Nicolas Dufresne,
Ricardo Ribalda, Kees Cook, Hans Verkuil, Fei Shao, Haoxiang Li,
Chen-Yu Tsai, Laurent Pinchart, Tomasz Figa, Philipp Zabel,
Benjamin Gaignard, Qianfeng Rong, Irui Wang, Jacopo Mondi, Fan Wu,
linux-media, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek
Cc: Sakari Ailus
Some platforms expose the video codec through the VCP coprocessor.
Use the VCP architecture when the VCP coprocessor is found.
Signed-off-by: Kyrie Wu <kyrie.wu@mediatek.com>
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
---
drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.h | 1 +
.../platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c | 3 +++
2 files changed, 4 insertions(+)
diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.h b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.h
index 300363a4..c1642fb0 100644
--- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.h
+++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.h
@@ -14,6 +14,7 @@ struct mtk_vcodec_enc_dev;
enum mtk_vcodec_fw_type {
VPU,
SCP,
+ VCP,
};
enum mtk_vcodec_fw_use {
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c
index e936ed8d..d220b645 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c
@@ -379,6 +379,9 @@ static int mtk_vcodec_probe(struct platform_device *pdev)
} else if (!of_property_read_u32(pdev->dev.of_node, "mediatek,scp",
&rproc_phandle)) {
fw_type = SCP;
+ } else if (!of_property_read_u32(pdev->dev.of_node, "mediatek,vcp",
+ &rproc_phandle)) {
+ fw_type = VCP;
} else {
dev_dbg(&pdev->dev, "Could not get vdec IPI device");
return -ENODEV;
^ permalink raw reply related [flat|nested] 24+ messages in thread* [PATCH v8 02/14] media: mediatek: vcodec: add driver to support vcp
2026-10-10 8:35 [PATCH v8 00/14] media: mediatek: vcodec: support video decoder in mt8196 Kyrie Wu
2026-10-10 8:36 ` [PATCH v8 01/14] media: mediatek: vcodec: support vcp architecture Kyrie Wu
@ 2026-10-10 8:36 ` Kyrie Wu
2026-10-10 8:53 ` sashiko-bot
2026-10-10 8:36 ` [PATCH v8 03/14] media: mediatek: vcodec: add driver to support vcp encoder Kyrie Wu
` (11 subsequent siblings)
13 siblings, 1 reply; 24+ messages in thread
From: Kyrie Wu @ 2026-10-10 8:36 UTC (permalink / raw)
To: Tiffany Lin, Andrew-CT Chen, Yunfei Dong, Mauro Carvalho Chehab,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Kyrie Wu, Nicolas Dufresne,
Ricardo Ribalda, Kees Cook, Hans Verkuil, Fei Shao, Haoxiang Li,
Chen-Yu Tsai, Laurent Pinchart, Tomasz Figa, Philipp Zabel,
Benjamin Gaignard, Qianfeng Rong, Irui Wang, Jacopo Mondi, Fan Wu,
linux-media, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek
Cc: Sakari Ailus
The processor is changed from scp to vcp in mt8196 platform.
Adding new firmware interface to communicate kernel with vcp
for the communication method is changed.
Signed-off-by: Kyrie Wu <kyrie.wu@mediatek.com>
---
.../media/platform/mediatek/vcodec/Kconfig | 4 +
.../platform/mediatek/vcodec/common/Makefile | 4 +
.../mediatek/vcodec/common/mtk_vcodec_fw.c | 23 +-
.../mediatek/vcodec/common/mtk_vcodec_fw.h | 6 +-
.../vcodec/common/mtk_vcodec_fw_priv.h | 12 +
.../vcodec/common/mtk_vcodec_fw_scp.c | 1 +
.../vcodec/common/mtk_vcodec_fw_vcp.c | 571 ++++++++++++++++++
.../vcodec/common/mtk_vcodec_fw_vcp.h | 152 +++++
.../vcodec/common/mtk_vcodec_fw_vpu.c | 1 +
.../vcodec/decoder/mtk_vcodec_dec_drv.c | 5 +-
.../vcodec/decoder/mtk_vcodec_dec_drv.h | 2 +
.../vcodec/encoder/mtk_vcodec_enc_drv.c | 4 +-
.../vcodec/encoder/mtk_vcodec_enc_drv.h | 2 +
13 files changed, 768 insertions(+), 19 deletions(-)
create mode 100644 drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c
create mode 100644 drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.h
diff --git a/drivers/media/platform/mediatek/vcodec/Kconfig b/drivers/media/platform/mediatek/vcodec/Kconfig
index bc829223..d23dad5c 100644
--- a/drivers/media/platform/mediatek/vcodec/Kconfig
+++ b/drivers/media/platform/mediatek/vcodec/Kconfig
@@ -1,4 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
+config VIDEO_MEDIATEK_VCODEC_VCP
+ bool
+
config VIDEO_MEDIATEK_VCODEC_SCP
bool
@@ -21,6 +24,7 @@ config VIDEO_MEDIATEK_VCODEC
select V4L2_MEM2MEM_DEV
select VIDEO_MEDIATEK_VCODEC_VPU if VIDEO_MEDIATEK_VPU
select VIDEO_MEDIATEK_VCODEC_SCP if MTK_SCP
+ select VIDEO_MEDIATEK_VCODEC_VCP if MTK_VCP_RPROC
select V4L2_H264
select V4L2_VP9
select MEDIA_CONTROLLER
diff --git a/drivers/media/platform/mediatek/vcodec/common/Makefile b/drivers/media/platform/mediatek/vcodec/common/Makefile
index d0479914..2f68692e 100644
--- a/drivers/media/platform/mediatek/vcodec/common/Makefile
+++ b/drivers/media/platform/mediatek/vcodec/common/Makefile
@@ -14,6 +14,10 @@ ifneq ($(CONFIG_VIDEO_MEDIATEK_VCODEC_SCP),)
mtk-vcodec-common-y += mtk_vcodec_fw_scp.o
endif
+ifneq ($(CONFIG_VIDEO_MEDIATEK_VCODEC_VCP),)
+mtk-vcodec-common-y += mtk_vcodec_fw_vcp.o
+endif
+
ifneq ($(CONFIG_DEBUG_FS),)
obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC) += mtk-vcodec-dbgfs.o
diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c
index 08949b08..552d0d8a 100644
--- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c
+++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c
@@ -4,25 +4,18 @@
#include "../encoder/mtk_vcodec_enc_drv.h"
#include "mtk_vcodec_fw_priv.h"
-struct mtk_vcodec_fw *mtk_vcodec_fw_select(void *priv, enum mtk_vcodec_fw_type type,
- enum mtk_vcodec_fw_use fw_use)
+struct mtk_vcodec_fw *mtk_vcodec_fw_select(void *priv, enum mtk_vcodec_fw_use fw_use,
+ mtk_vcodec_fw_init_func fw_init)
{
- struct platform_device *plat_dev;
-
- if (fw_use == ENCODER)
- plat_dev = ((struct mtk_vcodec_enc_dev *)priv)->plat_dev;
- else
- plat_dev = ((struct mtk_vcodec_dec_dev *)priv)->plat_dev;
+ if (!fw_init)
+ return ERR_PTR(-EINVAL);
- switch (type) {
- case VPU:
- return mtk_vcodec_fw_vpu_init(priv, fw_use);
- case SCP:
- return mtk_vcodec_fw_scp_init(priv, fw_use);
- default:
- dev_err(&plat_dev->dev, "Invalid vcodec fw type");
+ if (fw_use != ENCODER && fw_use != DECODER) {
+ pr_err("Invalid firmware use %d\n", fw_use);
return ERR_PTR(-EINVAL);
}
+
+ return fw_init(priv, fw_use);
}
EXPORT_SYMBOL_GPL(mtk_vcodec_fw_select);
diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.h b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.h
index c1642fb0..50d93d47 100644
--- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.h
+++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.h
@@ -24,11 +24,13 @@ enum mtk_vcodec_fw_use {
struct mtk_vcodec_fw;
+typedef struct mtk_vcodec_fw *(*mtk_vcodec_fw_init_func)(void *priv,
+ enum mtk_vcodec_fw_use fw_use);
typedef void (*mtk_vcodec_ipi_handler) (void *data,
unsigned int len, void *priv);
-struct mtk_vcodec_fw *mtk_vcodec_fw_select(void *priv, enum mtk_vcodec_fw_type type,
- enum mtk_vcodec_fw_use fw_use);
+struct mtk_vcodec_fw *mtk_vcodec_fw_select(void *priv, enum mtk_vcodec_fw_use fw_use,
+ mtk_vcodec_fw_init_func fw_init);
void mtk_vcodec_fw_release(struct mtk_vcodec_fw *fw);
int mtk_vcodec_fw_load_firmware(struct mtk_vcodec_fw *fw);
diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_priv.h b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_priv.h
index 99603acc..0a2a9b01 100644
--- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_priv.h
+++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_priv.h
@@ -4,6 +4,7 @@
#define _MTK_VCODEC_FW_PRIV_H_
#include "mtk_vcodec_fw.h"
+#include "mtk_vcodec_fw_vcp.h"
struct mtk_vcodec_dec_dev;
struct mtk_vcodec_enc_dev;
@@ -13,6 +14,7 @@ struct mtk_vcodec_fw {
const struct mtk_vcodec_fw_ops *ops;
struct platform_device *pdev;
struct mtk_scp *scp;
+ struct mtk_vcp *vcp;
enum mtk_vcodec_fw_use fw_use;
};
@@ -49,4 +51,14 @@ mtk_vcodec_fw_scp_init(void *priv, enum mtk_vcodec_fw_use fw_use)
}
#endif /* CONFIG_VIDEO_MEDIATEK_VCODEC_SCP */
+#if IS_ENABLED(CONFIG_VIDEO_MEDIATEK_VCODEC_VCP)
+struct mtk_vcodec_fw *mtk_vcodec_fw_vcp_init(void *priv, enum mtk_vcodec_fw_use fw_use);
+#else
+static inline struct mtk_vcodec_fw *
+mtk_vcodec_fw_vcp_init(void *priv, enum mtk_vcodec_fw_use fw_use)
+{
+ return ERR_PTR(-ENODEV);
+}
+#endif /* CONFIG_VIDEO_MEDIATEK_VCODEC_VCP */
+
#endif /* _MTK_VCODEC_FW_PRIV_H_ */
diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_scp.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_scp.c
index 1b0bc473..1aad5c3e 100644
--- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_scp.c
+++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_scp.c
@@ -90,3 +90,4 @@ struct mtk_vcodec_fw *mtk_vcodec_fw_scp_init(void *priv, enum mtk_vcodec_fw_use
return fw;
}
+EXPORT_SYMBOL_GPL(mtk_vcodec_fw_scp_init);
diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c
new file mode 100644
index 00000000..150d842c
--- /dev/null
+++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c
@@ -0,0 +1,571 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 MediaTek Inc.
+ * Author: Kyrie Wu <kyrie.wu@mediatek.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/dma-direction.h>
+#include <linux/dma-mapping.h>
+#include <linux/iommu.h>
+#include <linux/remoteproc/mtk_vcp_public.h>
+#include <linux/firmware/mediatek/mtk-vcp-ipc.h>
+
+#include "../decoder/mtk_vcodec_dec_drv.h"
+#include "../decoder/vdec_ipi_msg.h"
+#include "mtk_vcodec_fw_priv.h"
+
+#define IPI_SEND_TIMEOUT_MS 100U
+#define IPI_TIMEOUT_MS 100U
+
+#define VCP_IPI_HEADER_SIZE (sizeof(u32) * 2)
+#define VCP_IPI_ALIGN (4)
+
+static bool mtk_vcodec_vcp_ipi_id_valid(unsigned int ipi_id)
+{
+ return ipi_id < VCP_IPI_MAX;
+}
+
+static void mtk_vcodec_vcp_ipi_lock(struct mtk_vcp *vcp, u32 ipi_id)
+{
+ mutex_lock(&vcp->ipi_desc[ipi_id].lock);
+}
+
+static void mtk_vcodec_vcp_ipi_unlock(struct mtk_vcp *vcp, u32 ipi_id)
+{
+ lockdep_assert_held(&vcp->ipi_desc[ipi_id].lock);
+ mutex_unlock(&vcp->ipi_desc[ipi_id].lock);
+}
+
+static void mtk_vcodec_vcp_msq_queue_lock(struct mtk_vcodec_fw *fw, unsigned long *flags)
+{
+ spin_lock_irqsave(&fw->vcp->msg_queue.lock, *flags);
+}
+
+static void mtk_vcodec_vcp_msq_queue_unlock(struct mtk_vcodec_fw *fw, unsigned long *flags)
+{
+ spin_unlock_irqrestore(&fw->vcp->msg_queue.lock, *flags);
+}
+
+static int mtk_vcodec_vcp_notifier(struct notifier_block *nb, unsigned long event, void *ptr)
+{
+ struct mtk_vcp *vcp = container_of(nb, struct mtk_vcp, vcp_notify);
+
+ switch (event) {
+ case VCP_EVENT_SUSPEND:
+ case VCP_EVENT_STOP:
+ dev_dbg(&vcp->pdev->dev, "vcp notifier suspend");
+ break;
+ case VCP_EVENT_READY:
+ case VCP_EVENT_RESUME:
+ dev_dbg(&vcp->pdev->dev, "vcp notifier ready");
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static void mtk_vcodec_vcp_free_msg_node(struct mtk_vcodec_fw *fw,
+ struct mtk_vcp_msg_node *msg_node)
+{
+ unsigned long flags;
+
+ mtk_vcodec_vcp_msq_queue_lock(fw, &flags);
+ list_add(&msg_node->list, &fw->vcp->msg_queue.node_list);
+ mtk_vcodec_vcp_msq_queue_unlock(fw, &flags);
+}
+
+static int mtk_vcodec_vcp_ipi_register(struct mtk_vcp *vcp, u32 ipi_id, vcp_ipi_handler_t handler,
+ void *priv)
+{
+ if (!vcp)
+ return -EPROBE_DEFER;
+
+ if (WARN_ON(!mtk_vcodec_vcp_ipi_id_valid(ipi_id)) || WARN_ON(!handler))
+ return -EINVAL;
+
+ mtk_vcodec_vcp_ipi_lock(vcp, ipi_id);
+ vcp->ipi_desc[ipi_id].handler = handler;
+ vcp->ipi_desc[ipi_id].priv = priv;
+ mtk_vcodec_vcp_ipi_unlock(vcp, ipi_id);
+
+ return 0;
+}
+
+static int mtk_vcodec_vcp_msg_process_thread(void *arg)
+{
+ struct mtk_vcodec_fw *fw = arg;
+ struct vdec_vpu_ipi_ack *msg = NULL;
+ struct mtk_vcp_share_obj *obj;
+ struct mtk_vcp_msg_node *msg_node;
+ vcp_ipi_handler_t handler;
+ unsigned long flags;
+ int ret = 0;
+
+ do {
+ ret = wait_event_interruptible(fw->vcp->msg_queue.wq,
+ atomic_read(&fw->vcp->msg_queue.cnt) > 0 ||
+ kthread_should_stop());
+ if (ret < 0) {
+ dev_err(&fw->pdev->dev, "wait msg queue ack timeout %d %d\n",
+ ret, atomic_read(&fw->vcp->msg_queue.cnt));
+ continue;
+ }
+ if (kthread_should_stop())
+ break;
+
+ mtk_vcodec_vcp_msq_queue_lock(fw, &flags);
+ msg_node = list_entry(fw->vcp->msg_queue.msg_list.next,
+ struct mtk_vcp_msg_node, list);
+ list_del(&msg_node->list);
+ atomic_dec(&fw->vcp->msg_queue.cnt);
+ mtk_vcodec_vcp_msq_queue_unlock(fw, &flags);
+
+ obj = &msg_node->ipi_data;
+ msg = (struct vdec_vpu_ipi_ack *)obj->share_buf;
+
+ if (!msg->ap_inst_addr) {
+ dev_err(&fw->pdev->dev, "invalid message address\n");
+ mtk_vcodec_vcp_free_msg_node(fw, msg_node);
+ continue;
+ }
+
+ dev_dbg(&fw->pdev->dev, "msg ack id %d len %d msg_id 0x%x\n", obj->id, obj->len,
+ msg->msg_id);
+
+ if (!mtk_vcodec_vcp_ipi_id_valid(obj->id)) {
+ dev_err(&fw->pdev->dev, "invalid ack ipi id %u\n", obj->id);
+ mtk_vcodec_vcp_free_msg_node(fw, msg_node);
+ continue;
+ }
+
+ mtk_vcodec_vcp_ipi_lock(fw->vcp, obj->id);
+ handler = fw->vcp->ipi_desc[obj->id].handler;
+ if (!handler) {
+ dev_err(&fw->pdev->dev, "invalid ack ipi handler id = %d\n", obj->id);
+ mtk_vcodec_vcp_ipi_unlock(fw->vcp, obj->id);
+ mtk_vcodec_vcp_free_msg_node(fw, msg_node);
+ continue;
+ }
+
+ handler(msg, obj->len, fw->vcp->ipi_desc[obj->id].priv);
+ mtk_vcodec_vcp_ipi_unlock(fw->vcp, obj->id);
+
+ fw->vcp->msg_signaled[obj->id] = true;
+ wake_up(&fw->vcp->msg_wq[obj->id]);
+
+ mtk_vcodec_vcp_free_msg_node(fw, msg_node);
+ } while (!kthread_should_stop());
+
+ return ret;
+}
+
+static int mtk_vcodec_vcp_msg_ack_isr(unsigned int id, void *prdata, void *data, unsigned int len)
+{
+ struct mtk_vcodec_fw *fw = prdata;
+ struct mtk_vcp_msg_queue *msg_queue = &fw->vcp->msg_queue;
+ struct mtk_vcp_msg_node *msg_node;
+ struct vdec_vpu_ipi_ack *msg = NULL;
+ struct mtk_vcp_share_obj *obj = data;
+ unsigned long flags;
+
+ msg = (struct vdec_vpu_ipi_ack *)obj->share_buf;
+
+ if (!mtk_vcodec_vcp_ipi_id_valid(obj->id)) {
+ dev_err(&fw->pdev->dev, "invalid ack ipi id %u\n", obj->id);
+ return -EINVAL;
+ }
+
+ mtk_vcodec_vcp_msq_queue_lock(fw, &flags);
+ if (!list_empty(&msg_queue->node_list)) {
+ msg_node = list_entry(msg_queue->node_list.next, struct mtk_vcp_msg_node, list);
+
+ memcpy(&msg_node->ipi_data, obj, sizeof(*obj));
+ list_move_tail(&msg_node->list, &msg_queue->msg_list);
+ atomic_inc(&msg_queue->cnt);
+ mtk_vcodec_vcp_msq_queue_unlock(fw, &flags);
+
+ dev_dbg(&fw->pdev->dev, "push ipi_id %x msg_id %x, msg cnt %d\n",
+ obj->id, msg->msg_id, atomic_read(&msg_queue->cnt));
+
+ wake_up(&msg_queue->wq);
+ } else {
+ mtk_vcodec_vcp_msq_queue_unlock(fw, &flags);
+ dev_err(&fw->pdev->dev, "no free nodes in msg queue\n");
+ }
+
+ return 0;
+}
+
+static int mtk_vcodec_vcp_msg_ipi_send(struct mtk_vcodec_fw *fw, int id, void *buf,
+ unsigned int len, unsigned int wait)
+{
+ struct mtk_vcp *vcp = fw->vcp;
+ struct mtk_vcp_device *vcp_device = vcp->vcp_device;
+ bool *msg_signaled;
+ wait_queue_head_t *msg_wq;
+ int ret, ipi_size, feature_id, mailbox_id, retry_cnt = 0;
+ unsigned long timeout_jiffies = 0;
+ struct mtk_vcp_share_obj obj = {0};
+ unsigned int *data;
+
+ if (id < 0 || !mtk_vcodec_vcp_ipi_id_valid(id)) {
+ dev_err(&fw->pdev->dev, "invalid ipi id %d\n", id);
+ return -EINVAL;
+ }
+
+ msg_signaled = &vcp->msg_signaled[id];
+ msg_wq = &vcp->msg_wq[id];
+
+ if (!vcp_device) {
+ dev_dbg(&fw->pdev->dev, "vcp device is null\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&vcp->ipi_mutex);
+ feature_id = VDEC_FEATURE_ID;
+ mailbox_id = IPI_OUT_VDEC_1;
+
+ timeout_jiffies = jiffies + msecs_to_jiffies(VCP_SYNC_TIMEOUT_MS);
+ while (!vcp_device->ops->vcp_is_ready(vcp_device, feature_id)) {
+ if (time_after(jiffies, timeout_jiffies)) {
+ vcp->ipi_id_ack[id] = -EINVAL;
+ ret = -EINVAL;
+ goto error;
+ }
+ usleep_range(1000, 2000);
+ }
+
+ if (len > VCP_SHARE_BUF_SIZE) {
+ vcp->ipi_id_ack[id] = -EINVAL;
+ ret = -EINVAL;
+ goto error;
+ }
+
+ obj.id = id;
+ obj.len = len;
+ memcpy(obj.share_buf, buf, len);
+
+ ipi_size = round_up(VCP_IPI_HEADER_SIZE + len, VCP_IPI_ALIGN);
+ data = (unsigned int *)obj.share_buf;
+ dev_dbg(&fw->pdev->dev, "vcp send message: id %d len %d data 0x%x\n",
+ obj.id, obj.len, data[0]);
+
+ *msg_signaled = false;
+ vcp->ipi_id_ack[id] = VCODEC_IPI_MSG_STATUS_OK;
+
+ ret = mtk_vcp_ipc_send(vcp_get_ipidev(vcp_device), mailbox_id, &obj, ipi_size);
+ if (ret != IPI_ACTION_DONE) {
+ vcp->ipi_id_ack[id] = -EIO;
+ ret = -EIO;
+ goto error;
+ }
+
+wait_ack:
+ /* wait for VCP's ACK */
+ ret = wait_event_interruptible_timeout(*msg_wq, *msg_signaled,
+ msecs_to_jiffies(IPI_TIMEOUT_MS));
+ if (!ret) {
+ vcp->ipi_id_ack[id] = VCODEC_IPI_MSG_STATUS_FAIL;
+ dev_err(&fw->pdev->dev, "wait ipi ack timeout! %d %d\n", ret, vcp->ipi_id_ack[id]);
+ } else if (ret == -ERESTARTSYS) {
+ if (retry_cnt++ < 5)
+ goto wait_ack;
+
+ dev_err(&fw->pdev->dev, "wait ipi ack err (%d)\n", vcp->ipi_id_ack[id]);
+ vcp->ipi_id_ack[id] = VCODEC_IPI_MSG_STATUS_FAIL;
+ } else if (ret < 0) {
+ dev_err(&fw->pdev->dev, "wait ipi ack fail ret %d %d\n", ret, vcp->ipi_id_ack[id]);
+ vcp->ipi_id_ack[id] = VCODEC_IPI_MSG_STATUS_FAIL;
+ }
+
+ dev_dbg(&fw->pdev->dev, "receive message: id %d len %d data 0x%x\n",
+ obj.id, obj.len, data[0]);
+
+ mutex_unlock(&vcp->ipi_mutex);
+
+ return vcp->ipi_id_ack[id];
+
+error:
+ mutex_unlock(&vcp->ipi_mutex);
+ dev_err(&fw->pdev->dev, "send msg error type:%d msg:%d > %d ret:%d\n", fw->type, len,
+ VCP_SHARE_BUF_SIZE, ret);
+
+ return ret;
+}
+
+static bool mtk_vcodec_vcp_driver_loaded(struct mtk_vcodec_fw *fw)
+{
+ struct device *dev = &fw->pdev->dev;
+ struct device_driver *drv;
+
+ drv = driver_find("mtk-vcp", &platform_bus_type);
+ if (!drv) {
+ dev_dbg(dev, "find mtk-vcp driver failed, need to reload.");
+ return false;
+ }
+
+ return true;
+}
+
+static int mtk_vcodec_vcp_get_vcp_device(struct mtk_vcodec_fw *fw)
+{
+ struct device *dev = &fw->pdev->dev;
+ int retry = 0, retry_cnt = 10000;
+ phandle vcp_phandle;
+
+ while (!try_then_request_module(mtk_vcodec_vcp_driver_loaded(fw), "mtk-vcp")) {
+ if (++retry > retry_cnt) {
+ dev_err(dev, "failed to load mtk-vcp module");
+ return -EPROBE_DEFER;
+ }
+ usleep_range(1000, 2000);
+ }
+
+ if (of_property_read_u32(dev->of_node, "mediatek,vcp", &vcp_phandle)) {
+ dev_err(dev, "can't get vcp handle.\n");
+ return -ENODEV;
+ }
+
+ fw->vcp->vcp_device = mtk_vcp_get_by_phandle(vcp_phandle);
+ if (!fw->vcp->vcp_device) {
+ dev_err(dev, "get vcp device failed\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void mtk_vcodec_vcp_put_device(struct mtk_vcodec_fw *fw)
+{
+ if (!fw->vcp->vcp_device)
+ return;
+
+ rproc_put(fw->vcp->vcp_device->rproc);
+ fw->vcp->vcp_device = NULL;
+}
+
+static int mtk_vcodec_vcp_load_firmware(struct mtk_vcodec_fw *fw)
+{
+ struct mtk_vcp_device *vcp_device;
+ int ret, feature_id, mem_id, mailbox_id, ipi_id;
+ int i;
+
+ if (fw->vcp->is_init_done) {
+ dev_dbg(&fw->pdev->dev, "vcp has already been initialized done.\n");
+ return 0;
+ }
+
+ if (mtk_vcodec_vcp_get_vcp_device(fw) < 0) {
+ dev_err(&fw->pdev->dev, "vcp device is null.\n");
+ return -EINVAL;
+ }
+
+ vcp_device = fw->vcp->vcp_device;
+
+ feature_id = VDEC_FEATURE_ID;
+ mem_id = VDEC_MEM_ID;
+ mailbox_id = IPI_IN_VDEC_1;
+ ipi_id = VCP_IPI_LAT_DECODER;
+
+ ret = mtk_vcp_mbox_ipc_register(vcp_get_ipidev(vcp_device), mailbox_id,
+ mtk_vcodec_vcp_msg_ack_isr, fw, &fw->vcp->share_data);
+ if (ret) {
+ dev_dbg(&fw->pdev->dev, "ipi register fail %d %d %d %d\n", ret, feature_id,
+ mem_id, mailbox_id);
+ ret = -EINVAL;
+ goto err_put_device;
+ }
+ fw->vcp->feature_id = feature_id;
+ fw->vcp->mailbox_id = mailbox_id;
+ fw->vcp->is_ipi_registered = true;
+
+ fw->vcp->vcp_notify.notifier_call = mtk_vcodec_vcp_notifier;
+ fw->vcp->vcp_notify.priority = 1;
+ vcp_device->ops->register_notify(vcp_device, feature_id, &fw->vcp->vcp_notify);
+ fw->vcp->is_notify_registered = true;
+
+ if (!fw->vcp->is_register_done) {
+ ret = vcp_device->ops->register_feature(vcp_device, feature_id);
+ if (ret < 0) {
+ dev_err(&fw->pdev->dev, "%d register to vcp fail(%d)\n", feature_id, ret);
+ ret = -EINVAL;
+ goto err_unregister_notify;
+ }
+
+ fw->vcp->is_register_done = true;
+ }
+
+ fw->vcp->is_init_done = true;
+
+ for (i = 0; i < VCP_IPI_MAX; i++)
+ mutex_init(&fw->vcp->ipi_desc[i].lock);
+ mutex_init(&fw->vcp->ipi_mutex);
+
+ init_waitqueue_head(&fw->vcp->msg_wq[VCP_IPI_LAT_DECODER]);
+ init_waitqueue_head(&fw->vcp->msg_wq[VCP_IPI_CORE_DECODER]);
+ fw->vcp->msg_thread =
+ kthread_run(mtk_vcodec_vcp_msg_process_thread, fw, "vcp_vdec_msq_thread");
+ if (IS_ERR(fw->vcp->msg_thread)) {
+ ret = PTR_ERR(fw->vcp->msg_thread);
+ fw->vcp->msg_thread = NULL;
+ goto err_deregister_feature;
+ }
+
+ fw->vcp->vsi_addr = vcp_device->ops->get_mem_virt(vcp_device, mem_id);
+ fw->vcp->vsi_core_addr = fw->vcp->vsi_addr + VCODEC_VSI_LEN;
+ fw->vcp->vsi_size = vcp_device->ops->get_mem_size(vcp_device, mem_id);
+ fw->vcp->iova_addr = vcp_device->ops->get_mem_iova(vcp_device, mem_id);
+
+ dev_dbg(&fw->pdev->dev, "vdec vcp init done => va: %p size:0x%x iova:%p.\n",
+ fw->vcp->vsi_addr, fw->vcp->vsi_size, &fw->vcp->iova_addr);
+
+ return 0;
+
+err_deregister_feature:
+ if (fw->vcp->is_register_done) {
+ vcp_device->ops->deregister_feature(vcp_device, feature_id);
+ fw->vcp->is_register_done = false;
+ }
+err_unregister_notify:
+ if (fw->vcp->is_notify_registered) {
+ vcp_device->ops->unregister_notify(vcp_device, feature_id,
+ &fw->vcp->vcp_notify);
+ fw->vcp->is_notify_registered = false;
+ }
+ if (fw->vcp->is_ipi_registered) {
+ mtk_vcp_mbox_ipc_unregister(vcp_get_ipidev(vcp_device), mailbox_id);
+ fw->vcp->is_ipi_registered = false;
+ }
+ fw->vcp->is_init_done = false;
+
+err_put_device:
+ mtk_vcodec_vcp_put_device(fw);
+ return ret;
+}
+
+static unsigned int mtk_vcodec_vcp_get_vdec_capa(struct mtk_vcodec_fw *fw)
+{
+ return MTK_VDEC_FORMAT_MM21 | MTK_VDEC_FORMAT_H264_SLICE | MTK_VDEC_FORMAT_VP9_FRAME |
+ MTK_VDEC_FORMAT_AV1_FRAME | MTK_VDEC_FORMAT_HEVC_FRAME |
+ MTK_VDEC_IS_SUPPORT_10BIT | MTK_VDEC_IS_SUPPORT_EXT;
+}
+
+static void *mtk_vcodec_vcp_dm_addr(struct mtk_vcodec_fw *fw, u32 dtcm_dmem_addr)
+{
+ return NULL;
+}
+
+static int mtk_vcodec_vcp_set_ipi_register(struct mtk_vcodec_fw *fw, int id,
+ mtk_vcodec_ipi_handler handler,
+ const char *name, void *priv)
+{
+ return mtk_vcodec_vcp_ipi_register(fw->vcp, id, handler, priv);
+}
+
+static int mtk_vcodec_vcp_ipi_send(struct mtk_vcodec_fw *fw, int id, void *buf,
+ unsigned int len, unsigned int wait)
+{
+ return mtk_vcodec_vcp_msg_ipi_send(fw, id, buf, len, wait);
+}
+
+static void mtk_vcodec_vcp_release(struct mtk_vcodec_fw *fw)
+{
+ struct mtk_vcp_device *vcp_device = fw->vcp->vcp_device;
+ struct device *dev = &fw->pdev->dev;
+ int ret;
+
+ if (!fw->vcp->vcp_device) {
+ dev_err(dev, "vcp device is null\n");
+ return;
+ }
+
+ if (fw->vcp->is_ipi_registered) {
+ mtk_vcp_mbox_ipc_unregister(vcp_get_ipidev(vcp_device), fw->vcp->mailbox_id);
+ fw->vcp->is_ipi_registered = false;
+ }
+
+ if (fw->vcp->msg_thread) {
+ kthread_stop(fw->vcp->msg_thread);
+ fw->vcp->msg_thread = NULL;
+ }
+
+ if (fw->vcp->is_notify_registered) {
+ vcp_device->ops->unregister_notify(vcp_device, fw->vcp->feature_id,
+ &fw->vcp->vcp_notify);
+ fw->vcp->is_notify_registered = false;
+ }
+
+ if (!fw->vcp->is_register_done) {
+ fw->vcp->is_init_done = false;
+ goto put_device;
+ }
+
+ ret = vcp_device->ops->deregister_feature(vcp_device, fw->vcp->feature_id);
+ if (ret < 0) {
+ dev_err(dev, "deregister feature_id(%d) fail(%d)\n", fw->vcp->feature_id, ret);
+ return;
+ }
+
+ fw->vcp->is_register_done = false;
+ fw->vcp->is_init_done = false;
+
+put_device:
+ mtk_vcodec_vcp_put_device(fw);
+}
+
+static const struct mtk_vcodec_fw_ops mtk_vcodec_vcp_msg = {
+ .load_firmware = mtk_vcodec_vcp_load_firmware,
+ .get_vdec_capa = mtk_vcodec_vcp_get_vdec_capa,
+ .map_dm_addr = mtk_vcodec_vcp_dm_addr,
+ .ipi_register = mtk_vcodec_vcp_set_ipi_register,
+ .ipi_send = mtk_vcodec_vcp_ipi_send,
+ .release = mtk_vcodec_vcp_release,
+};
+
+struct mtk_vcodec_fw *mtk_vcodec_fw_vcp_init(void *priv, enum mtk_vcodec_fw_use fw_use)
+{
+ struct mtk_vcp_msg_node *msg_node;
+ struct platform_device *plat_dev;
+ struct mtk_vcodec_fw *fw;
+ int i;
+
+ if (fw_use == DECODER) {
+ struct mtk_vcodec_dec_dev *dec_dev = priv;
+
+ plat_dev = dec_dev->plat_dev;
+ } else {
+ pr_err("Invalid fw_use %d (use a reasonable fw id here)\n", fw_use);
+ return ERR_PTR(-EINVAL);
+ }
+
+ fw = devm_kzalloc(&plat_dev->dev, sizeof(*fw), GFP_KERNEL);
+ if (!fw)
+ return ERR_PTR(-ENOMEM);
+
+ fw->type = VCP;
+ fw->pdev = plat_dev;
+ fw->fw_use = fw_use;
+ fw->ops = &mtk_vcodec_vcp_msg;
+ fw->vcp = devm_kzalloc(&plat_dev->dev, sizeof(*fw->vcp), GFP_KERNEL);
+ if (!fw->vcp)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&fw->vcp->msg_queue.msg_list);
+ INIT_LIST_HEAD(&fw->vcp->msg_queue.node_list);
+ spin_lock_init(&fw->vcp->msg_queue.lock);
+ init_waitqueue_head(&fw->vcp->msg_queue.wq);
+ atomic_set(&fw->vcp->msg_queue.cnt, 0);
+ fw->vcp->pdev = plat_dev;
+
+ for (i = 0; i < VCP_MAX_MQ_NODE_CNT; i++) {
+ msg_node = devm_kzalloc(&plat_dev->dev, sizeof(*msg_node), GFP_KERNEL);
+ if (!msg_node)
+ return ERR_PTR(-ENOMEM);
+
+ list_add(&msg_node->list, &fw->vcp->msg_queue.node_list);
+ }
+
+ return fw;
+}
+EXPORT_SYMBOL_GPL(mtk_vcodec_fw_vcp_init);
diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.h b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.h
new file mode 100644
index 00000000..83742096
--- /dev/null
+++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.h
@@ -0,0 +1,152 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2025 MediaTek Inc.
+ * Author: Kyrie Wu <kyrie.wu@mediatek.com>
+ */
+
+#ifndef _MTK_VCODEC_FW_VCP_H_
+#define _MTK_VCODEC_FW_VCP_H_
+
+typedef void (*vcp_ipi_handler_t) (void *data, unsigned int len, void *priv);
+
+#define VCP_MAX_MQ_NODE_CNT 6
+#define VCP_SHARE_BUF_SIZE 64
+
+#define VCODEC_VSI_LEN (0x2000)
+
+/* enum mtk_vcp_ipi_index - index used to separate different hardware */
+enum mtk_vcp_ipi_index {
+ VCP_IPI_LAT_DECODER,
+ VCP_IPI_CORE_DECODER,
+ VCP_IPI_MAX,
+};
+
+/**
+ * struct mtk_vcp_msg_queue - process the vcp message between kernel with vcp
+ *
+ * @msg_list: store share buffer list which from vcp to kernel
+ * @wq: waitqueue that can be used to wait for vcp message
+ * @lock: protect msg list
+ * @cnt: the count of share obj in msg list
+ * @node_list: share obj list
+ */
+struct mtk_vcp_msg_queue {
+ struct list_head msg_list;
+ wait_queue_head_t wq;
+ /* protect msg_list and node_list */
+ spinlock_t lock;
+ atomic_t cnt;
+ struct list_head node_list;
+};
+
+/**
+ * struct mtk_vcp_ipi_desc - store the ack handler
+ *
+ * @lock: protect ack handler data
+ * @handler: calling this handler when kernel receive ack
+ * @priv: private data when calling handler to process
+ */
+struct mtk_vcp_ipi_desc {
+ /* protect handler and priv */
+ struct mutex lock;
+ vcp_ipi_handler_t handler;
+ void *priv;
+};
+
+/**
+ * struct mtk_vcp_share_obj - share buffer used to send data to vcp
+ *
+ * @id: message index
+ * @len: message size
+ * @share_buf: message data
+ */
+struct mtk_vcp_share_obj {
+ unsigned int id;
+ unsigned int len;
+ unsigned char share_buf[VCP_SHARE_BUF_SIZE];
+};
+
+/* enum mtk_vcp_ipi_msg_status - the status when send message to vcp */
+enum mtk_vcp_ipi_msg_status {
+ VCODEC_IPI_MSG_STATUS_OK = 0,
+ VCODEC_IPI_MSG_STATUS_FAIL = -1,
+ VCODEC_IPI_MSG_STATUS_MAX_INST = -2,
+ VCODEC_IPI_MSG_STATUS_ILSEQ = -3,
+ VCODEC_IPI_MSG_STATUS_INVALID_ID = -4,
+ VCODEC_IPI_MSG_STATUS_DMA_FAIL = -5,
+};
+
+/**
+ * struct mtk_vcp_msg_node - share buffer used to send data to vcp
+ *
+ * @ipi_data: share obj data
+ * @list: list to store msg node
+ */
+struct mtk_vcp_msg_node {
+ struct mtk_vcp_share_obj ipi_data;
+ struct list_head list;
+};
+
+/**
+ * struct mtk_vcp - vcp firmware private data
+ *
+ * @is_init_done: vcp is ready to use
+ *
+ * @ipi_mutex: used to protect ipi data
+ * @msg_signaled: whether receive ack from vcp
+ * @msg_wq: wake message queue
+ *
+ * @ipi_desc: store ack handler
+ * @ipi_id_ack: the ack handler status
+ *
+ * @msg_queue: process vcp message
+ * @share_data: temp share obj data
+ *
+ * @vcp_notify: register notifier to vcp
+ * @msg_thread: process VCP message queue
+ *
+ * @vsi_addr: vsi virtual data address
+ * @vsi_core_addr: vsi core virtual data address
+ * @iova_addr: vsi iova address
+ * @vsi_size: vsi size
+ *
+ * @pdev: platform device
+ * @vcp_device: vcp private data
+ * @feature_id: registered VCP feature id
+ * @mailbox_id: registered VCP mailbox id
+ * @is_register_done: feature registration state
+ * @is_ipi_registered: mailbox IPC registration state
+ * @is_notify_registered: notifier registration state
+ */
+struct mtk_vcp {
+ bool is_init_done;
+
+ /* serialize ipi message send/receive */
+ struct mutex ipi_mutex;
+ bool msg_signaled[VCP_IPI_MAX];
+ wait_queue_head_t msg_wq[VCP_IPI_MAX];
+
+ struct mtk_vcp_ipi_desc ipi_desc[VCP_IPI_MAX];
+ int ipi_id_ack[VCP_IPI_MAX];
+
+ struct mtk_vcp_msg_queue msg_queue;
+ struct mtk_vcp_share_obj share_data;
+
+ struct notifier_block vcp_notify;
+ struct task_struct *msg_thread;
+
+ void *vsi_addr;
+ void *vsi_core_addr;
+ dma_addr_t iova_addr;
+ int vsi_size;
+
+ struct platform_device *pdev;
+ struct mtk_vcp_device *vcp_device;
+ int feature_id;
+ int mailbox_id;
+ bool is_register_done;
+ bool is_ipi_registered;
+ bool is_notify_registered;
+};
+
+#endif
diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vpu.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vpu.c
index 3632037f..41643db9 100644
--- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vpu.c
+++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vpu.c
@@ -130,3 +130,4 @@ struct mtk_vcodec_fw *mtk_vcodec_fw_vpu_init(void *priv, enum mtk_vcodec_fw_use
return fw;
}
+EXPORT_SYMBOL_GPL(mtk_vcodec_fw_vpu_init);
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c
index d220b645..ab5f6c01 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c
@@ -376,19 +376,22 @@ static int mtk_vcodec_probe(struct platform_device *pdev)
if (!of_property_read_u32(pdev->dev.of_node, "mediatek,vpu",
&rproc_phandle)) {
fw_type = VPU;
+ dev->fw_init = mtk_vcodec_fw_vpu_init;
} else if (!of_property_read_u32(pdev->dev.of_node, "mediatek,scp",
&rproc_phandle)) {
fw_type = SCP;
+ dev->fw_init = mtk_vcodec_fw_scp_init;
} else if (!of_property_read_u32(pdev->dev.of_node, "mediatek,vcp",
&rproc_phandle)) {
fw_type = VCP;
+ dev->fw_init = mtk_vcodec_fw_vcp_init;
} else {
dev_dbg(&pdev->dev, "Could not get vdec IPI device");
return -ENODEV;
}
dma_set_max_seg_size(&pdev->dev, UINT_MAX);
- dev->fw_handler = mtk_vcodec_fw_select(dev, fw_type, DECODER);
+ dev->fw_handler = mtk_vcodec_fw_select(dev, DECODER, dev->fw_init);
if (IS_ERR(dev->fw_handler))
return PTR_ERR(dev->fw_handler);
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h
index c9d27534..15e43732 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h
+++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h
@@ -251,6 +251,7 @@ struct mtk_vcodec_dec_ctx {
* @vdecsys_regmap: VDEC_SYS register space passed through syscon
*
* @fw_handler: used to communicate with the firmware.
+ * @fw_init: firmware-specific init callback selected at probe time
* @id_counter: used to identify current opened instance
*
* @dec_mutex: decoder hardware lock
@@ -292,6 +293,7 @@ struct mtk_vcodec_dec_dev {
struct regmap *vdecsys_regmap;
struct mtk_vcodec_fw *fw_handler;
+ struct mtk_vcodec_fw *(*fw_init)(void *priv, enum mtk_vcodec_fw_use fw_use);
u64 id_counter;
/* decoder hardware mutex lock */
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c
index 4e4541b2..811bc62a 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c
+++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c
@@ -259,16 +259,18 @@ static int mtk_vcodec_probe(struct platform_device *pdev)
if (!of_property_read_u32(pdev->dev.of_node, "mediatek,vpu",
&rproc_phandle)) {
fw_type = VPU;
+ dev->fw_init = mtk_vcodec_fw_vpu_init;
} else if (!of_property_read_u32(pdev->dev.of_node, "mediatek,scp",
&rproc_phandle)) {
fw_type = SCP;
+ dev->fw_init = mtk_vcodec_fw_scp_init;
} else {
dev_err(&pdev->dev, "[MTK VCODEC] Could not get venc IPI device");
return -ENODEV;
}
dma_set_max_seg_size(&pdev->dev, UINT_MAX);
- dev->fw_handler = mtk_vcodec_fw_select(dev, fw_type, ENCODER);
+ dev->fw_handler = mtk_vcodec_fw_select(dev, ENCODER, dev->fw_init);
if (IS_ERR(dev->fw_handler))
return PTR_ERR(dev->fw_handler);
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h
index 0cddfa13..934ff648 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h
+++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h
@@ -174,6 +174,7 @@ struct mtk_vcodec_enc_ctx {
* @venc_pdata: encoder IC-specific data
*
* @fw_handler: used to communicate with the firmware.
+ * @fw_init: firmware-specific init callback selected at probe time
* @id_counter: used to identify current opened instance
*
* @enc_mutex: encoder hardware lock.
@@ -201,6 +202,7 @@ struct mtk_vcodec_enc_dev {
const struct mtk_vcodec_enc_pdata *venc_pdata;
struct mtk_vcodec_fw *fw_handler;
+ struct mtk_vcodec_fw *(*fw_init)(void *priv, enum mtk_vcodec_fw_use fw_use);
u64 id_counter;
/* encoder hardware mutex lock */
^ permalink raw reply related [flat|nested] 24+ messages in thread* Re: [PATCH v8 02/14] media: mediatek: vcodec: add driver to support vcp
2026-10-10 8:36 ` [PATCH v8 02/14] media: mediatek: vcodec: add driver to support vcp Kyrie Wu
@ 2026-10-10 8:53 ` sashiko-bot
0 siblings, 0 replies; 24+ messages in thread
From: sashiko-bot @ 2026-10-10 8:53 UTC (permalink / raw)
To: Kyrie Wu; +Cc: media-ci, conor+dt, devicetree, robh
Thank you for your contribution! Sashiko AI review found 11 potential issue(s) to consider:
- [High] media: vcodec: vcp: Modprobe bomb in mtk_vcodec_vcp_get_vcp_device retry loop
- [High] media: vcodec: vcp: IPC desync and UAF from unhandled late ACKs
- [High] media: vcodec: vcp: Out-of-bounds read in mtk_vcodec_vcp_msg_ack_isr
- [High] media: vcodec: vcp: Unvalidated firmware payload length enables OOB read
- [High] build: missing headers and identifiers in mtk_vcodec_fw_vcp.c
- [High] module: 10000-iteration modprobe fork bomb in mtk_vcodec_vcp_get_vcp_device
- [Medium] media: vcodec: vcp: Remote processor reference leak in mtk_vcodec_vcp_release
- [Medium] media: vcodec: vcp: Signal retry loop in mtk_vcodec_vcp_msg_ipi_send instantly bypasses timeout
- [Medium] media: vcodec: vcp: Race condition from premature is_init_done flag assignment
- [Low] media: vcodec: vcp: Incorrect printk format specifier for dma_addr_t
- [Low] media: vcodec: vcp: Unused fw_type variable in probe functions
--
commit bd79ec43ee8b23d8e7b2f7ace79397de331b886a
Author: Kyrie Wu <kyrie.wu@mediatek.com>
Subject: media: mediatek: vcodec: add driver to support vcp
The processor is changed from scp to vcp in mt8196 platform.
Adding new firmware interface to communicate kernel with vcp
for the communication method is changed.
> diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c
> new file mode 100644
> index 0000000000000..150d842c7d226
> --- /dev/null
> +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c
> @@ -0,0 +1,571 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2025 MediaTek Inc.
> + * Author: Kyrie Wu <kyrie.wu@mediatek.com>
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/dma-direction.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/iommu.h>
> +#include <linux/remoteproc/mtk_vcp_public.h>
> +#include <linux/firmware/mediatek/mtk-vcp-ipc.h>
[Severity: High]
Are these included headers missing from the kernel tree? It looks like
mtk_vcp_public.h and mtk-vcp-ipc.h are completely missing, along with
identifiers like VDEC_FEATURE_ID and IPI_OUT_VDEC_1, which will cause
compilation failures.
[ ... ]
> + if (!handler) {
> + dev_err(&fw->pdev->dev, "invalid ack ipi handler id = %d\n", obj->id);
> + mtk_vcodec_vcp_ipi_unlock(fw->vcp, obj->id);
> + mtk_vcodec_vcp_free_msg_node(fw, msg_node);
> + continue;
> + }
> +
> + handler(msg, obj->len, fw->vcp->ipi_desc[obj->id].priv);
[Severity: High]
Does this unvalidated length field allow an out-of-bounds read? The obj->len
value is extracted directly from the untrusted IPC payload and passed to the
handler without bounds checking against the physical constraints of the
share_buf array.
> + mtk_vcodec_vcp_ipi_unlock(fw->vcp, obj->id);
> +
> + fw->vcp->msg_signaled[obj->id] = true;
> + wake_up(&fw->vcp->msg_wq[obj->id]);
[Severity: High]
Can unhandled late ACKs cause IPC desynchronization and a use-after-free?
If wait_event_interruptible_timeout() in mtk_vcodec_vcp_msg_ipi_send() times
out, the wait is abandoned. A subsequent delayed ACK processed here would
signal completion prematurely for a new wait, or cast the stale
msg->ap_inst_addr to a potentially freed context.
[ ... ]
> +static int mtk_vcodec_vcp_msg_ack_isr(unsigned int id, void *prdata, void *data, unsigned int len)
> +{
[ ... ]
> + if (!list_empty(&msg_queue->node_list)) {
> + msg_node = list_entry(msg_queue->node_list.next, struct mtk_vcp_msg_node, list);
> +
> + memcpy(&msg_node->ipi_data, obj, sizeof(*obj));
[Severity: High]
Will this memcpy() perform an out-of-bounds read if the provided len is smaller
than sizeof(*obj)? The length parameter passed to the ISR is ignored, which
could lead to a page fault if the payload sits at the end of a mapped page.
[ ... ]
> +static int mtk_vcodec_vcp_msg_ipi_send(struct mtk_vcodec_fw *fw, int id, void *buf,
> + unsigned int len, unsigned int wait)
> +{
[ ... ]
> +wait_ack:
> + /* wait for VCP's ACK */
> + ret = wait_event_interruptible_timeout(*msg_wq, *msg_signaled,
> + msecs_to_jiffies(IPI_TIMEOUT_MS));
> + if (!ret) {
> + vcp->ipi_id_ack[id] = VCODEC_IPI_MSG_STATUS_FAIL;
> + dev_err(&fw->pdev->dev, "wait ipi ack timeout! %d %d\n", ret, vcp->ipi_id_ack[id]);
> + } else if (ret == -ERESTARTSYS) {
> + if (retry_cnt++ < 5)
> + goto wait_ack;
> +
> + dev_err(&fw->pdev->dev, "wait ipi ack err (%d)\n", vcp->ipi_id_ack[id]);
[Severity: Medium]
Will this retry loop instantly exhaust all its attempts? Since the pending
signal on the task is never cleared, subsequent calls to
wait_event_interruptible_timeout() will immediately see the signal and
return -ERESTARTSYS again without sleeping.
[ ... ]
> +static int mtk_vcodec_vcp_get_vcp_device(struct mtk_vcodec_fw *fw)
> +{
> + struct device *dev = &fw->pdev->dev;
> + int retry = 0, retry_cnt = 10000;
> + phandle vcp_phandle;
> +
> + while (!try_then_request_module(mtk_vcodec_vcp_driver_loaded(fw), "mtk-vcp")) {
> + if (++retry > retry_cnt) {
> + dev_err(dev, "failed to load mtk-vcp module");
> + return -EPROBE_DEFER;
> + }
> + usleep_range(1000, 2000);
> + }
[Severity: High]
Does this loop act as a local denial-of-service fork bomb? The
try_then_request_module macro internally calls __request_module() synchronously
every time its condition evaluates to false. If the module is missing or fails
to probe, this will spawn up to 10,000 modprobe helper processes in rapid
succession.
[ ... ]
> +static int mtk_vcodec_vcp_load_firmware(struct mtk_vcodec_fw *fw)
> +{
[ ... ]
> + fw->vcp->is_register_done = true;
> + }
> +
> + fw->vcp->is_init_done = true;
> +
> + for (i = 0; i < VCP_IPI_MAX; i++)
> + mutex_init(&fw->vcp->ipi_desc[i].lock);
> + mutex_init(&fw->vcp->ipi_mutex);
[Severity: Medium]
Is it safe to set is_init_done to true before initializing the mutexes and
waitqueues? A concurrent operation observing is_init_done == true could
attempt to use these uninitialized primitives, leading to lockdep crashes or
NULL pointer dereferences.
[ ... ]
> + fw->vcp->vsi_addr = vcp_device->ops->get_mem_virt(vcp_device, mem_id);
> + fw->vcp->vsi_core_addr = fw->vcp->vsi_addr + VCODEC_VSI_LEN;
> + fw->vcp->vsi_size = vcp_device->ops->get_mem_size(vcp_device, mem_id);
> + fw->vcp->iova_addr = vcp_device->ops->get_mem_iova(vcp_device, mem_id);
> +
> + dev_dbg(&fw->pdev->dev, "vdec vcp init done => va: %p size:0x%x iova:%p.\n",
> + fw->vcp->vsi_addr, fw->vcp->vsi_size, &fw->vcp->iova_addr);
[Severity: Low]
Does this print the memory address of the iova_addr variable instead of its
value? The %p format specifier prints the pointer itself, but %pad should
be used for printing a dma_addr_t passed by reference.
[ ... ]
> +static void mtk_vcodec_vcp_release(struct mtk_vcodec_fw *fw)
> +{
[ ... ]
> + ret = vcp_device->ops->deregister_feature(vcp_device, fw->vcp->feature_id);
> + if (ret < 0) {
> + dev_err(dev, "deregister feature_id(%d) fail(%d)\n", fw->vcp->feature_id, ret);
> + return;
> + }
> +
> + fw->vcp->is_register_done = false;
> + fw->vcp->is_init_done = false;
> +
> +put_device:
> + mtk_vcodec_vcp_put_device(fw);
> +}
[Severity: Medium]
Will this early return leak the remoteproc reference? By returning early when
deregister_feature fails, the function bypasses the put_device cleanup label,
preventing the remote processor driver from being cleanly unloaded.
> diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c
> index 426d060d937c1..0f9cb651e59b0 100644
> --- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c
> +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c
> @@ -376,16 +376,22 @@ static int mtk_vcodec_probe(struct platform_device *pdev)
> if (!of_property_read_u32(pdev->dev.of_node, "mediatek,vpu",
> &rproc_phandle)) {
> fw_type = VPU;
> + dev->fw_init = mtk_vcodec_fw_vpu_init;
> } else if (!of_property_read_u32(pdev->dev.of_node, "mediatek,scp",
> &rproc_phandle)) {
> fw_type = SCP;
> + dev->fw_init = mtk_vcodec_fw_scp_init;
> + } else if (!of_property_read_u32(pdev->dev.of_node, "mediatek,vcp",
> + &rproc_phandle)) {
> + fw_type = VCP;
> + dev->fw_init = mtk_vcodec_fw_vcp_init;
[Severity: Low]
Is the fw_type variable unused after this assignment? The mtk_vcodec_fw_select
call below was updated to use dev->fw_init instead, which leaves fw_type
orphaned and could trigger a -Wunused-but-set-variable compiler warning.
> diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c
> index 4e4541b2fc8ea..811bc62a196f5 100644
> --- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c
> +++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c
> @@ -259,16 +259,18 @@ static int mtk_vcodec_probe(struct platform_device *pdev)
> if (!of_property_read_u32(pdev->dev.of_node, "mediatek,vpu",
> &rproc_phandle)) {
> fw_type = VPU;
> + dev->fw_init = mtk_vcodec_fw_vpu_init;
> } else if (!of_property_read_u32(pdev->dev.of_node, "mediatek,scp",
> &rproc_phandle)) {
> fw_type = SCP;
> + dev->fw_init = mtk_vcodec_fw_scp_init;
> } else {
> dev_err(&pdev->dev, "[MTK VCODEC] Could not get venc IPI device");
> return -ENODEV;
> }
> dma_set_max_seg_size(&pdev->dev, UINT_MAX);
>
> - dev->fw_handler = mtk_vcodec_fw_select(dev, fw_type, ENCODER);
> + dev->fw_handler = mtk_vcodec_fw_select(dev, ENCODER, dev->fw_init);
[Severity: Low]
Similarly here, is fw_type unused after assignment now that
mtk_vcodec_fw_select has been updated to take the dev->fw_init callback?
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1791619742.git.kyrie.wu@mediatek.com?part=2
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v8 03/14] media: mediatek: vcodec: add driver to support vcp encoder
2026-10-10 8:35 [PATCH v8 00/14] media: mediatek: vcodec: support video decoder in mt8196 Kyrie Wu
2026-10-10 8:36 ` [PATCH v8 01/14] media: mediatek: vcodec: support vcp architecture Kyrie Wu
2026-10-10 8:36 ` [PATCH v8 02/14] media: mediatek: vcodec: add driver to support vcp Kyrie Wu
@ 2026-10-10 8:36 ` Kyrie Wu
2026-10-10 8:59 ` sashiko-bot
2026-10-10 8:36 ` [PATCH v8 04/14] media: mediatek: vcodec: get different firmware ipi id Kyrie Wu
` (10 subsequent siblings)
13 siblings, 1 reply; 24+ messages in thread
From: Kyrie Wu @ 2026-10-10 8:36 UTC (permalink / raw)
To: Tiffany Lin, Andrew-CT Chen, Yunfei Dong, Mauro Carvalho Chehab,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Kyrie Wu, Nicolas Dufresne,
Ricardo Ribalda, Kees Cook, Hans Verkuil, Fei Shao, Haoxiang Li,
Chen-Yu Tsai, Laurent Pinchart, Tomasz Figa, Philipp Zabel,
Benjamin Gaignard, Qianfeng Rong, Irui Wang, Jacopo Mondi, Fan Wu,
linux-media, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek
Cc: Sakari Ailus
Encoder also need to call vcp interface to communicate with vcp,
add driver to support encoder.
Signed-off-by: Kyrie Wu <kyrie.wu@mediatek.com>
---
.../vcodec/common/mtk_vcodec_fw_vcp.c | 52 ++++++++++++++++---
.../vcodec/common/mtk_vcodec_fw_vcp.h | 1 +
.../mediatek/vcodec/encoder/mtk_vcodec_enc.c | 1 -
.../mediatek/vcodec/encoder/mtk_vcodec_enc.h | 2 +
4 files changed, 47 insertions(+), 9 deletions(-)
diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c
index 150d842c..ece4e3e8 100644
--- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c
+++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c
@@ -13,6 +13,8 @@
#include "../decoder/mtk_vcodec_dec_drv.h"
#include "../decoder/vdec_ipi_msg.h"
+#include "../encoder/mtk_vcodec_enc.h"
+#include "../encoder/mtk_vcodec_enc_drv.h"
#include "mtk_vcodec_fw_priv.h"
#define IPI_SEND_TIMEOUT_MS 100U
@@ -223,8 +225,13 @@ static int mtk_vcodec_vcp_msg_ipi_send(struct mtk_vcodec_fw *fw, int id, void *b
}
mutex_lock(&vcp->ipi_mutex);
- feature_id = VDEC_FEATURE_ID;
- mailbox_id = IPI_OUT_VDEC_1;
+ if (fw->fw_use == ENCODER) {
+ feature_id = VENC_FEATURE_ID;
+ mailbox_id = IPI_OUT_VENC_0;
+ } else {
+ feature_id = VDEC_FEATURE_ID;
+ mailbox_id = IPI_OUT_VDEC_1;
+ }
timeout_jiffies = jiffies + msecs_to_jiffies(VCP_SYNC_TIMEOUT_MS);
while (!vcp_device->ops->vcp_is_ready(vcp_device, feature_id)) {
@@ -348,7 +355,7 @@ static void mtk_vcodec_vcp_put_device(struct mtk_vcodec_fw *fw)
static int mtk_vcodec_vcp_load_firmware(struct mtk_vcodec_fw *fw)
{
struct mtk_vcp_device *vcp_device;
- int ret, feature_id, mem_id, mailbox_id, ipi_id;
+ int ret, feature_id, mem_id, mailbox_id;
int i;
if (fw->vcp->is_init_done) {
@@ -363,10 +370,15 @@ static int mtk_vcodec_vcp_load_firmware(struct mtk_vcodec_fw *fw)
vcp_device = fw->vcp->vcp_device;
- feature_id = VDEC_FEATURE_ID;
- mem_id = VDEC_MEM_ID;
- mailbox_id = IPI_IN_VDEC_1;
- ipi_id = VCP_IPI_LAT_DECODER;
+ if (fw->fw_use == ENCODER) {
+ feature_id = VENC_FEATURE_ID;
+ mem_id = VENC_MEM_ID;
+ mailbox_id = IPI_IN_VENC_0;
+ } else {
+ feature_id = VDEC_FEATURE_ID;
+ mem_id = VDEC_MEM_ID;
+ mailbox_id = IPI_IN_VDEC_1;
+ }
ret = mtk_vcp_mbox_ipc_register(vcp_get_ipidev(vcp_device), mailbox_id,
mtk_vcodec_vcp_msg_ack_isr, fw, &fw->vcp->share_data);
@@ -402,6 +414,26 @@ static int mtk_vcodec_vcp_load_firmware(struct mtk_vcodec_fw *fw)
mutex_init(&fw->vcp->ipi_desc[i].lock);
mutex_init(&fw->vcp->ipi_mutex);
+ if (fw->fw_use == ENCODER) {
+ init_waitqueue_head(&fw->vcp->msg_wq[VCP_IPI_ENCODER]);
+ fw->vcp->msg_thread =
+ kthread_run(mtk_vcodec_vcp_msg_process_thread, fw, "vcp_enc_msq_thread");
+ if (IS_ERR(fw->vcp->msg_thread)) {
+ ret = PTR_ERR(fw->vcp->msg_thread);
+ fw->vcp->msg_thread = NULL;
+ goto err_deregister_feature;
+ }
+
+ fw->vcp->vsi_addr = vcp_device->ops->get_mem_virt(vcp_device, mem_id);
+ fw->vcp->vsi_size = vcp_device->ops->get_mem_size(vcp_device, mem_id);
+ fw->vcp->iova_addr = vcp_device->ops->get_mem_iova(vcp_device, mem_id);
+
+ dev_dbg(&fw->pdev->dev, "enc vcp init done => va: %p size:0x%x iova:%pad.\n",
+ fw->vcp->vsi_addr, fw->vcp->vsi_size, &fw->vcp->iova_addr);
+
+ return 0;
+ }
+
init_waitqueue_head(&fw->vcp->msg_wq[VCP_IPI_LAT_DECODER]);
init_waitqueue_head(&fw->vcp->msg_wq[VCP_IPI_CORE_DECODER]);
fw->vcp->msg_thread =
@@ -530,7 +562,11 @@ struct mtk_vcodec_fw *mtk_vcodec_fw_vcp_init(void *priv, enum mtk_vcodec_fw_use
struct mtk_vcodec_fw *fw;
int i;
- if (fw_use == DECODER) {
+ if (fw_use == ENCODER) {
+ struct mtk_vcodec_enc_dev *enc_dev = priv;
+
+ plat_dev = enc_dev->plat_dev;
+ } else if (fw_use == DECODER) {
struct mtk_vcodec_dec_dev *dec_dev = priv;
plat_dev = dec_dev->plat_dev;
diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.h b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.h
index 83742096..97ba642d 100644
--- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.h
+++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.h
@@ -16,6 +16,7 @@ typedef void (*vcp_ipi_handler_t) (void *data, unsigned int len, void *priv);
/* enum mtk_vcp_ipi_index - index used to separate different hardware */
enum mtk_vcp_ipi_index {
+ VCP_IPI_ENCODER,
VCP_IPI_LAT_DECODER,
VCP_IPI_CORE_DECODER,
VCP_IPI_MAX,
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c
index 0d4e9446..48cb5dde 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c
+++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c
@@ -26,7 +26,6 @@
#define MTK_DEFAULT_FRAMERATE_NUM 1001
#define MTK_DEFAULT_FRAMERATE_DENOM 30000
-#define MTK_VENC_4K_CAPABILITY_ENABLE BIT(0)
static void mtk_venc_worker(struct work_struct *work);
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.h b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.h
index 908d8179..84156c10 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.h
+++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.h
@@ -23,6 +23,8 @@
#define MTK_VENC_IRQ_STATUS_OFFSET 0x05C
#define MTK_VENC_IRQ_ACK_OFFSET 0x060
+#define MTK_VENC_4K_CAPABILITY_ENABLE BIT(0)
+
/**
* struct mtk_video_enc_buf - Private data related to each VB2 buffer.
* @m2m_buf: M2M buffer
^ permalink raw reply related [flat|nested] 24+ messages in thread* Re: [PATCH v8 03/14] media: mediatek: vcodec: add driver to support vcp encoder
2026-10-10 8:36 ` [PATCH v8 03/14] media: mediatek: vcodec: add driver to support vcp encoder Kyrie Wu
@ 2026-10-10 8:59 ` sashiko-bot
0 siblings, 0 replies; 24+ messages in thread
From: sashiko-bot @ 2026-10-10 8:59 UTC (permalink / raw)
To: Kyrie Wu; +Cc: devicetree, robh, conor+dt, media-ci
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] media: mtk_vcodec: encoder initialization fails due to unmapped IPI ID for VCP
- [High] media: mtk_vcodec: delayed ACK causes protocol desynchronization in VCP IPC
- [High] media: vcodec: vcp encoder fails to initialize due to out-of-bounds ipi_id
- [High] media: mediatek: vcodec: UAF in IPI message handlers on device close due to IPC timeout
--
commit c1db7dc6cad08382fb97ce9b64a0f609116bfae6
Author: Kyrie Wu <kyrie.wu@mediatek.com>
media: mediatek: vcodec: add driver to support vcp encoder
This commit adds encoder support for the VCP interface to allow communication
between the encoder and VCP hardware.
> diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c
> index 150d842c7d226..ece4e3e87e62d 100644
> --- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c
> +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c
[ ... ]
> @@ -223,8 +225,13 @@ static int mtk_vcodec_vcp_msg_ipi_send(struct mtk_vcodec_fw *fw, int id, void *b
> }
>
> mutex_lock(&vcp->ipi_mutex);
> - feature_id = VDEC_FEATURE_ID;
> - mailbox_id = IPI_OUT_VDEC_1;
> + if (fw->fw_use == ENCODER) {
> + feature_id = VENC_FEATURE_ID;
> + mailbox_id = IPI_OUT_VENC_0;
> + } else {
> + feature_id = VDEC_FEATURE_ID;
> + mailbox_id = IPI_OUT_VDEC_1;
> + }
>
> timeout_jiffies = jiffies + msecs_to_jiffies(VCP_SYNC_TIMEOUT_MS);
[Severity: High]
Will this initialization succeed for encoders?
The encoder driver appears to initialize its ID to SCP_IPI_VENC_H264 (4)
or IPI_VENC_H264 (3), as seen in h264_enc_init():
if (is_ext)
inst->vpu_inst.id = SCP_IPI_VENC_H264;
else
inst->vpu_inst.id = IPI_VENC_H264;
However, earlier in mtk_vcodec_vcp_msg_ipi_send() and in
mtk_vcodec_vcp_ipi_register(), the code checks mtk_vcodec_vcp_ipi_id_valid(),
which strictly requires the ID to be less than VCP_IPI_MAX.
Since VCP_IPI_MAX is 3, these encoder IDs will always fail the validation
check, returning -EINVAL and preventing the VCP encoder from communicating.
[Severity: High]
Could a delayed ACK from the firmware cause memory corruption or state
desynchronization?
When waiting for an ACK later in mtk_vcodec_vcp_msg_ipi_send(), the function
uses a hardcoded 100ms timeout and relies on a shared boolean:
wait_ack:
ret = wait_event_interruptible_timeout(*msg_wq, *msg_signaled,
msecs_to_jiffies(IPI_TIMEOUT_MS));
If the firmware takes longer than 100ms to reply, the wait times out
and returns an error. However, the firmware might still process the message
and send the ACK later.
This appears to lead to two potential issues:
1. If the timeout occurs during device teardown, such as when sending a
DEINIT message before closing the device, the driver proceeds to free
the instance via kfree(inst). When the delayed ACK eventually arrives,
the asynchronous message thread calls the IPI handler:
vpu_enc_ipi_handler()
vpu = (struct venc_vpu_inst *)(unsigned long)msg->venc_inst;
mtk_venc_debug(vpu->ctx, ...);
The handler extracts the freed instance from the message and dereferences
vpu->ctx, resulting in a use-after-free.
2. Because there are no sequence numbers matching ACKs to requests, a
delayed ACK could arrive during a subsequent message on the same ID.
The new message resets the shared flag and waits, but the delayed ACK
from the previous message would immediately wake it up prematurely,
desynchronizing the state between the driver and the firmware.
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1791619742.git.kyrie.wu@mediatek.com?part=3
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v8 04/14] media: mediatek: vcodec: get different firmware ipi id
2026-10-10 8:35 [PATCH v8 00/14] media: mediatek: vcodec: support video decoder in mt8196 Kyrie Wu
` (2 preceding siblings ...)
2026-10-10 8:36 ` [PATCH v8 03/14] media: mediatek: vcodec: add driver to support vcp encoder Kyrie Wu
@ 2026-10-10 8:36 ` Kyrie Wu
2026-10-10 8:56 ` sashiko-bot
2026-10-10 8:36 ` [PATCH v8 05/14] media: mediatek: vcodec: get share memory address Kyrie Wu
` (9 subsequent siblings)
13 siblings, 1 reply; 24+ messages in thread
From: Kyrie Wu @ 2026-10-10 8:36 UTC (permalink / raw)
To: Tiffany Lin, Andrew-CT Chen, Yunfei Dong, Mauro Carvalho Chehab,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Kyrie Wu, Nicolas Dufresne,
Ricardo Ribalda, Kees Cook, Hans Verkuil, Fei Shao, Haoxiang Li,
Chen-Yu Tsai, Laurent Pinchart, Tomasz Figa, Philipp Zabel,
Benjamin Gaignard, Qianfeng Rong, Irui Wang, Jacopo Mondi, Fan Wu,
linux-media, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek
Cc: Sakari Ailus
Getting ipi(inter-processor interrupt) id according to firmware
type and hardware index for different architecture.
Signed-off-by: Kyrie Wu <kyrie.wu@mediatek.com>
---
.../mediatek/vcodec/common/mtk_vcodec_fw.c | 14 ++++++++++++++
.../mediatek/vcodec/common/mtk_vcodec_fw.h | 1 +
.../vcodec/decoder/vdec/vdec_av1_req_lat_if.c | 5 +++--
.../vcodec/decoder/vdec/vdec_h264_req_multi_if.c | 5 +++--
.../vcodec/decoder/vdec/vdec_hevc_req_multi_if.c | 5 +++--
.../mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c | 5 +++--
.../vcodec/decoder/vdec/vdec_vp9_req_lat_if.c | 5 +++--
7 files changed, 30 insertions(+), 10 deletions(-)
diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c
index 552d0d8a..8630e871 100644
--- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c
+++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c
@@ -3,6 +3,20 @@
#include "../decoder/mtk_vcodec_dec_drv.h"
#include "../encoder/mtk_vcodec_enc_drv.h"
#include "mtk_vcodec_fw_priv.h"
+#include "mtk_vcodec_fw_vcp.h"
+
+int mtk_vcodec_fw_get_ipi(enum mtk_vcodec_fw_type type, int hw_id)
+{
+ switch (type) {
+ case SCP:
+ return hw_id == MTK_VDEC_LAT0 ? SCP_IPI_VDEC_LAT : SCP_IPI_VDEC_CORE;
+ case VCP:
+ return hw_id == MTK_VDEC_LAT0 ? VCP_IPI_LAT_DECODER : VCP_IPI_CORE_DECODER;
+ default:
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL_GPL(mtk_vcodec_fw_get_ipi);
struct mtk_vcodec_fw *mtk_vcodec_fw_select(void *priv, enum mtk_vcodec_fw_use fw_use,
mtk_vcodec_fw_init_func fw_init)
diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.h b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.h
index 50d93d47..005ac996 100644
--- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.h
+++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.h
@@ -43,5 +43,6 @@ int mtk_vcodec_fw_ipi_register(struct mtk_vcodec_fw *fw, int id,
int mtk_vcodec_fw_ipi_send(struct mtk_vcodec_fw *fw, int id,
void *buf, unsigned int len, unsigned int wait);
int mtk_vcodec_fw_get_type(struct mtk_vcodec_fw *fw);
+int mtk_vcodec_fw_get_ipi(enum mtk_vcodec_fw_type type, int hw_id);
#endif /* _MTK_VCODEC_FW_H_ */
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
index 49d9b4a7..84d28a0b 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
@@ -1878,6 +1878,7 @@ static int vdec_av1_slice_init(struct mtk_vcodec_dec_ctx *ctx)
{
struct vdec_av1_slice_instance *instance;
struct vdec_av1_slice_init_vsi *vsi;
+ enum mtk_vcodec_fw_type fw_type = ctx->dev->fw_handler->type;
int ret;
instance = kzalloc_obj(*instance);
@@ -1885,8 +1886,8 @@ static int vdec_av1_slice_init(struct mtk_vcodec_dec_ctx *ctx)
return -ENOMEM;
instance->ctx = ctx;
- instance->vpu.id = SCP_IPI_VDEC_LAT;
- instance->vpu.core_id = SCP_IPI_VDEC_CORE;
+ instance->vpu.id = mtk_vcodec_fw_get_ipi(fw_type, MTK_VDEC_LAT0);
+ instance->vpu.core_id = mtk_vcodec_fw_get_ipi(fw_type, MTK_VDEC_CORE);
instance->vpu.ctx = ctx;
instance->vpu.codec_type = ctx->current_codec;
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c
index 10359ce9..69d60717 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c
@@ -1204,6 +1204,7 @@ static int vdec_h264_slice_single_decode(void *h_vdec, struct mtk_vcodec_mem *bs
static int vdec_h264_slice_init(struct mtk_vcodec_dec_ctx *ctx)
{
+ enum mtk_vcodec_fw_type fw_type = ctx->dev->fw_handler->type;
struct vdec_h264_slice_inst *inst;
int err, vsi_size;
unsigned char *temp;
@@ -1214,8 +1215,8 @@ static int vdec_h264_slice_init(struct mtk_vcodec_dec_ctx *ctx)
inst->ctx = ctx;
- inst->vpu.id = SCP_IPI_VDEC_LAT;
- inst->vpu.core_id = SCP_IPI_VDEC_CORE;
+ inst->vpu.id = mtk_vcodec_fw_get_ipi(fw_type, MTK_VDEC_LAT0);
+ inst->vpu.core_id = mtk_vcodec_fw_get_ipi(fw_type, MTK_VDEC_CORE);
inst->vpu.ctx = ctx;
inst->vpu.codec_type = ctx->current_codec;
inst->vpu.capture_type = ctx->capture_fourcc;
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c
index 02f39954..dd638ef4 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c
@@ -855,6 +855,7 @@ static int vdec_hevc_slice_setup_core_buffer(struct vdec_hevc_slice_inst *inst,
static int vdec_hevc_slice_init(struct mtk_vcodec_dec_ctx *ctx)
{
+ enum mtk_vcodec_fw_type fw_type = ctx->dev->fw_handler->type;
struct vdec_hevc_slice_inst *inst;
int err, vsi_size;
@@ -864,8 +865,8 @@ static int vdec_hevc_slice_init(struct mtk_vcodec_dec_ctx *ctx)
inst->ctx = ctx;
- inst->vpu.id = SCP_IPI_VDEC_LAT;
- inst->vpu.core_id = SCP_IPI_VDEC_CORE;
+ inst->vpu.id = mtk_vcodec_fw_get_ipi(fw_type, MTK_VDEC_LAT0);
+ inst->vpu.core_id = mtk_vcodec_fw_get_ipi(fw_type, MTK_VDEC_CORE);
inst->vpu.ctx = ctx;
inst->vpu.codec_type = ctx->current_codec;
inst->vpu.capture_type = ctx->capture_fourcc;
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c
index 391e789a..d65e276f 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c
@@ -272,6 +272,7 @@ static int vdec_vp8_slice_get_decode_parameters(struct vdec_vp8_slice_inst *inst
static int vdec_vp8_slice_init(struct mtk_vcodec_dec_ctx *ctx)
{
+ enum mtk_vcodec_fw_type fw_type = ctx->dev->fw_handler->type;
struct vdec_vp8_slice_inst *inst;
int err;
@@ -281,8 +282,8 @@ static int vdec_vp8_slice_init(struct mtk_vcodec_dec_ctx *ctx)
inst->ctx = ctx;
- inst->vpu.id = SCP_IPI_VDEC_LAT;
- inst->vpu.core_id = SCP_IPI_VDEC_CORE;
+ inst->vpu.id = mtk_vcodec_fw_get_ipi(fw_type, MTK_VDEC_LAT0);
+ inst->vpu.core_id = mtk_vcodec_fw_get_ipi(fw_type, MTK_VDEC_CORE);
inst->vpu.ctx = ctx;
inst->vpu.codec_type = ctx->current_codec;
inst->vpu.capture_type = ctx->capture_fourcc;
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c
index ba7a1cd9..b4926867 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c
@@ -1833,6 +1833,7 @@ static int vdec_vp9_slice_update_core(struct vdec_vp9_slice_instance *instance,
static int vdec_vp9_slice_init(struct mtk_vcodec_dec_ctx *ctx)
{
+ enum mtk_vcodec_fw_type fw_type = ctx->dev->fw_handler->type;
struct vdec_vp9_slice_instance *instance;
struct vdec_vp9_slice_init_vsi *vsi;
int ret;
@@ -1842,8 +1843,8 @@ static int vdec_vp9_slice_init(struct mtk_vcodec_dec_ctx *ctx)
return -ENOMEM;
instance->ctx = ctx;
- instance->vpu.id = SCP_IPI_VDEC_LAT;
- instance->vpu.core_id = SCP_IPI_VDEC_CORE;
+ instance->vpu.id = mtk_vcodec_fw_get_ipi(fw_type, MTK_VDEC_LAT0);
+ instance->vpu.core_id = mtk_vcodec_fw_get_ipi(fw_type, MTK_VDEC_CORE);
instance->vpu.ctx = ctx;
instance->vpu.codec_type = ctx->current_codec;
^ permalink raw reply related [flat|nested] 24+ messages in thread* Re: [PATCH v8 04/14] media: mediatek: vcodec: get different firmware ipi id
2026-10-10 8:36 ` [PATCH v8 04/14] media: mediatek: vcodec: get different firmware ipi id Kyrie Wu
@ 2026-10-10 8:56 ` sashiko-bot
0 siblings, 0 replies; 24+ messages in thread
From: sashiko-bot @ 2026-10-10 8:56 UTC (permalink / raw)
To: Kyrie Wu; +Cc: robh, conor+dt, devicetree, media-ci
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] media: mtk-vcodec: OOB write via unvalidated -EINVAL from fw_get_ipi
--
commit b7bb8fd08e359be31186a13bf22647a6a15432b2
Author: Kyrie Wu <kyrie.wu@mediatek.com>
media: mediatek: vcodec: get different firmware ipi id
This commit updates the driver to get the inter-processor interrupt (IPI) ID
dynamically based on the firmware type and hardware index. This allows
supporting different architectures instead of using hardcoded IDs.
> diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c
> index 552d0d8a84444..8630e871d78e2 100644
> --- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c
> +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c
> @@ -3,6 +3,20 @@
> #include "../decoder/mtk_vcodec_dec_drv.h"
> #include "../encoder/mtk_vcodec_enc_drv.h"
> #include "mtk_vcodec_fw_priv.h"
> +#include "mtk_vcodec_fw_vcp.h"
> +
> +int mtk_vcodec_fw_get_ipi(enum mtk_vcodec_fw_type type, int hw_id)
> +{
> + switch (type) {
> + case SCP:
> + return hw_id == MTK_VDEC_LAT0 ? SCP_IPI_VDEC_LAT : SCP_IPI_VDEC_CORE;
> + case VCP:
> + return hw_id == MTK_VDEC_LAT0 ? VCP_IPI_LAT_DECODER : VCP_IPI_CORE_DECODER;
> + default:
> + return -EINVAL;
> + }
> +}
> +EXPORT_SYMBOL_GPL(mtk_vcodec_fw_get_ipi);
[ ... ]
> diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c
> index 10359ce9b934e..69d60717181a6 100644
> --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c
> +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c
> @@ -1204,6 +1204,7 @@ static int vdec_h264_slice_single_decode(void *h_vdec, struct mtk_vcodec_mem *bs
>
> static int vdec_h264_slice_init(struct mtk_vcodec_dec_ctx *ctx)
> {
> + enum mtk_vcodec_fw_type fw_type = ctx->dev->fw_handler->type;
> struct vdec_h264_slice_inst *inst;
> int err, vsi_size;
> unsigned char *temp;
> @@ -1214,8 +1215,8 @@ static int vdec_h264_slice_init(struct mtk_vcodec_dec_ctx *ctx)
>
> inst->ctx = ctx;
>
> - inst->vpu.id = SCP_IPI_VDEC_LAT;
> - inst->vpu.core_id = SCP_IPI_VDEC_CORE;
> + inst->vpu.id = mtk_vcodec_fw_get_ipi(fw_type, MTK_VDEC_LAT0);
> + inst->vpu.core_id = mtk_vcodec_fw_get_ipi(fw_type, MTK_VDEC_CORE);
[Severity: High]
Does this code handle cases where mtk_vcodec_fw_get_ipi() returns an error
code?
If an unsupported firmware type (such as VPU) is provided,
mtk_vcodec_fw_get_ipi() will return -EINVAL (-22), which is then assigned
directly to inst->vpu.id and inst->vpu.core_id without validation.
When this propagates to vpu_ipi_register() via vpu_dec_init():
drivers/media/platform/mediatek/vpu/mtk_vpu.c:vpu_ipi_register() {
...
if (id < IPI_VPU_MAX && handler) {
ipi_desc = vpu->ipi_desc;
ipi_desc[id].name = name;
...
}
On ARM64 (AArch64), the AAPCS64 dictates that the underlying type for an enum
whose values all fit in a signed 32-bit integer is a signed int, making
enum ipi_id signed.
The bounds check evaluates -22 < IPI_VPU_MAX as true. This bypasses the check
and executes a negative index out-of-bounds write to the ipi_desc array.
Could this lead to memory corruption (slab underflow) before the mtk_vpu
struct if a malformed Device Tree pairs a stateless decoder compatible string
with a VPU firmware handler?
> inst->vpu.ctx = ctx;
> inst->vpu.codec_type = ctx->current_codec;
> inst->vpu.capture_type = ctx->capture_fourcc;
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1791619742.git.kyrie.wu@mediatek.com?part=4
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v8 05/14] media: mediatek: vcodec: get share memory address
2026-10-10 8:35 [PATCH v8 00/14] media: mediatek: vcodec: support video decoder in mt8196 Kyrie Wu
` (3 preceding siblings ...)
2026-10-10 8:36 ` [PATCH v8 04/14] media: mediatek: vcodec: get different firmware ipi id Kyrie Wu
@ 2026-10-10 8:36 ` Kyrie Wu
2026-10-10 8:56 ` sashiko-bot
2026-10-10 8:36 ` [PATCH v8 06/14] media: mediatek: vcodec: add debug information Kyrie Wu
` (8 subsequent siblings)
13 siblings, 1 reply; 24+ messages in thread
From: Kyrie Wu @ 2026-10-10 8:36 UTC (permalink / raw)
To: Tiffany Lin, Andrew-CT Chen, Yunfei Dong, Mauro Carvalho Chehab,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Kyrie Wu, Nicolas Dufresne,
Ricardo Ribalda, Kees Cook, Hans Verkuil, Fei Shao, Haoxiang Li,
Chen-Yu Tsai, Laurent Pinchart, Tomasz Figa, Philipp Zabel,
Benjamin Gaignard, Qianfeng Rong, Irui Wang, Jacopo Mondi, Fan Wu,
linux-media, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek
Cc: Sakari Ailus
There is only one share memory for vcp architecture, need to
divide it into many different functions.
Signed-off-by: Kyrie Wu <kyrie.wu@mediatek.com>
---
.../vcodec/common/mtk_vcodec_fw_vcp.c | 26 +++++++++++++-
.../vcodec/common/mtk_vcodec_fw_vcp.h | 13 +++++++
.../vcodec/decoder/vdec/vdec_av1_req_lat_if.c | 35 ++++++++++++++++---
.../decoder/vdec/vdec_h264_req_multi_if.c | 6 +++-
.../decoder/vdec/vdec_hevc_req_multi_if.c | 7 ++--
.../vcodec/decoder/vdec/vdec_vp9_req_lat_if.c | 22 ++++++++++--
.../mediatek/vcodec/decoder/vdec_vpu_if.c | 10 +++++-
7 files changed, 108 insertions(+), 11 deletions(-)
diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c
index ece4e3e8..468c4bf4 100644
--- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c
+++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c
@@ -483,8 +483,31 @@ static unsigned int mtk_vcodec_vcp_get_vdec_capa(struct mtk_vcodec_fw *fw)
MTK_VDEC_IS_SUPPORT_10BIT | MTK_VDEC_IS_SUPPORT_EXT;
}
-static void *mtk_vcodec_vcp_dm_addr(struct mtk_vcodec_fw *fw, u32 dtcm_dmem_addr)
+static unsigned int mtk_vcodec_vcp_get_venc_capa(struct mtk_vcodec_fw *fw)
{
+ return 0;
+}
+
+static void *mtk_vcodec_vcp_dm_addr(struct mtk_vcodec_fw *fw, u32 mem_type)
+{
+ unsigned char *vsi_core = fw->vcp->vsi_core_addr;
+
+ switch (mem_type) {
+ case ENCODER_MEM:
+ case VCODEC_LAT_MEM:
+ return fw->vcp->vsi_addr;
+ case VCODEC_CORE_MEM:
+ return vsi_core;
+ case VP9_FRAME_MEM:
+ return vsi_core + VCODEC_VSI_LEN;
+ case AV1_CDF_MEM:
+ return vsi_core + VCODEC_VSI_LEN + VP9_FRAME_SIZE;
+ case AV1_IQ_MEM:
+ return vsi_core + VCODEC_VSI_LEN + VP9_FRAME_SIZE + AV1_CDF_SIZE;
+ default:
+ break;
+ }
+
return NULL;
}
@@ -549,6 +572,7 @@ static void mtk_vcodec_vcp_release(struct mtk_vcodec_fw *fw)
static const struct mtk_vcodec_fw_ops mtk_vcodec_vcp_msg = {
.load_firmware = mtk_vcodec_vcp_load_firmware,
.get_vdec_capa = mtk_vcodec_vcp_get_vdec_capa,
+ .get_venc_capa = mtk_vcodec_vcp_get_venc_capa,
.map_dm_addr = mtk_vcodec_vcp_dm_addr,
.ipi_register = mtk_vcodec_vcp_set_ipi_register,
.ipi_send = mtk_vcodec_vcp_ipi_send,
diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.h b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.h
index 97ba642d..79504bb2 100644
--- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.h
+++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.h
@@ -13,6 +13,19 @@ typedef void (*vcp_ipi_handler_t) (void *data, unsigned int len, void *priv);
#define VCP_SHARE_BUF_SIZE 64
#define VCODEC_VSI_LEN (0x2000)
+#define VP9_FRAME_SIZE (0x1000)
+#define AV1_CDF_SIZE (0xFE80)
+#define AV1_IQ_TABLE_SIZE (0x12200)
+
+/* enum mtk_vcp_mem_type - memory type for different hardware */
+enum mtk_vcp_mem_type {
+ ENCODER_MEM,
+ VCODEC_LAT_MEM,
+ VCODEC_CORE_MEM,
+ VP9_FRAME_MEM,
+ AV1_CDF_MEM,
+ AV1_IQ_MEM,
+};
/* enum mtk_vcp_ipi_index - index used to separate different hardware */
enum mtk_vcp_ipi_index {
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
index 84d28a0b..ffb28bad 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
@@ -765,6 +765,15 @@ static void *vdec_av1_get_ctrl_ptr(struct mtk_vcodec_dec_ctx *ctx, int id)
return ctrl->p_cur.p;
}
+static u32 vdec_av1_get_cdf_table_addr(struct mtk_vcodec_dec_ctx *ctx,
+ struct vdec_av1_slice_init_vsi *vsi)
+{
+ if (mtk_vcodec_fw_get_type(ctx->dev->fw_handler) == VCP)
+ return AV1_CDF_MEM;
+ else
+ return (u32)vsi->cdf_table_addr;
+}
+
static int vdec_av1_slice_init_cdf_table(struct vdec_av1_slice_instance *instance)
{
u8 *remote_cdf_table;
@@ -775,7 +784,7 @@ static int vdec_av1_slice_init_cdf_table(struct vdec_av1_slice_instance *instanc
ctx = instance->ctx;
vsi = instance->vpu.vsi;
remote_cdf_table = mtk_vcodec_fw_map_dm_addr(ctx->dev->fw_handler,
- (u32)vsi->cdf_table_addr);
+ vdec_av1_get_cdf_table_addr(ctx, vsi));
if (IS_ERR(remote_cdf_table)) {
mtk_vdec_err(ctx, "failed to map cdf table\n");
return PTR_ERR(remote_cdf_table);
@@ -796,6 +805,15 @@ static int vdec_av1_slice_init_cdf_table(struct vdec_av1_slice_instance *instanc
return 0;
}
+static u32 vdec_av1_get_iq_table_addr(struct mtk_vcodec_dec_ctx *ctx,
+ struct vdec_av1_slice_init_vsi *vsi)
+{
+ if (mtk_vcodec_fw_get_type(ctx->dev->fw_handler) == VCP)
+ return AV1_IQ_MEM;
+ else
+ return (u32)vsi->iq_table_addr;
+}
+
static int vdec_av1_slice_init_iq_table(struct vdec_av1_slice_instance *instance)
{
u8 *remote_iq_table;
@@ -806,7 +824,7 @@ static int vdec_av1_slice_init_iq_table(struct vdec_av1_slice_instance *instance
ctx = instance->ctx;
vsi = instance->vpu.vsi;
remote_iq_table = mtk_vcodec_fw_map_dm_addr(ctx->dev->fw_handler,
- (u32)vsi->iq_table_addr);
+ vdec_av1_get_iq_table_addr(ctx, vsi));
if (IS_ERR(remote_iq_table)) {
mtk_vdec_err(ctx, "failed to map iq table\n");
return PTR_ERR(remote_iq_table);
@@ -1874,6 +1892,15 @@ static int vdec_av1_slice_update_core(struct vdec_av1_slice_instance *instance,
return 0;
}
+static u32 vdec_av1_get_core_vsi_addr(struct mtk_vcodec_dec_ctx *ctx,
+ struct vdec_av1_slice_init_vsi *vsi)
+{
+ if (mtk_vcodec_fw_get_type(ctx->dev->fw_handler) == VCP)
+ return VCODEC_CORE_MEM;
+ else
+ return (u32)vsi->core_vsi;
+}
+
static int vdec_av1_slice_init(struct mtk_vcodec_dec_ctx *ctx)
{
struct vdec_av1_slice_instance *instance;
@@ -1905,8 +1932,8 @@ static int vdec_av1_slice_init(struct mtk_vcodec_dec_ctx *ctx)
goto error_vsi;
}
instance->init_vsi = vsi;
- instance->core_vsi = mtk_vcodec_fw_map_dm_addr(ctx->dev->fw_handler, (u32)vsi->core_vsi);
-
+ instance->core_vsi = mtk_vcodec_fw_map_dm_addr(ctx->dev->fw_handler,
+ vdec_av1_get_core_vsi_addr(ctx, vsi));
if (!instance->core_vsi) {
mtk_vdec_err(ctx, "failed to get AV1 core vsi\n");
ret = -EINVAL;
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c
index 69d60717..544d3bc0 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c
@@ -1233,7 +1233,11 @@ static int vdec_h264_slice_init(struct mtk_vcodec_dec_ctx *ctx)
vsi_size = round_up(vsi_size, VCODEC_DEC_ALIGNED_64);
inst->vsi_ext = inst->vpu.vsi;
temp = (unsigned char *)inst->vsi_ext;
- inst->vsi_core_ext = (struct vdec_h264_slice_vsi_ext *)(temp + vsi_size);
+ if (mtk_vcodec_fw_get_type(ctx->dev->fw_handler) == VCP)
+ inst->vsi_core_ext =
+ mtk_vcodec_fw_map_dm_addr(ctx->dev->fw_handler, VCODEC_CORE_MEM);
+ else
+ inst->vsi_core_ext = (struct vdec_h264_slice_vsi_ext *)(temp + vsi_size);
if (inst->ctx->dev->vdec_pdata->hw_arch == MTK_VDEC_PURE_SINGLE_CORE)
inst->decode = vdec_h264_slice_single_decode_ext;
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c
index dd638ef4..a5dd4298 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c
@@ -879,8 +879,11 @@ static int vdec_hevc_slice_init(struct mtk_vcodec_dec_ctx *ctx)
vsi_size = round_up(sizeof(struct vdec_hevc_slice_vsi), VCODEC_DEC_ALIGNED_64);
inst->vsi = inst->vpu.vsi;
- inst->vsi_core =
- (struct vdec_hevc_slice_vsi *)(((char *)inst->vpu.vsi) + vsi_size);
+ if (mtk_vcodec_fw_get_type(ctx->dev->fw_handler) == VCP)
+ inst->vsi_core = mtk_vcodec_fw_map_dm_addr(ctx->dev->fw_handler, VCODEC_CORE_MEM);
+ else
+ inst->vsi_core =
+ (struct vdec_hevc_slice_vsi *)(((char *)inst->vpu.vsi) + vsi_size);
inst->resolution_changed = true;
inst->realloc_mv_buf = true;
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c
index b4926867..ee4d1cd8 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c
@@ -500,6 +500,15 @@ static DEFINE_MUTEX(vdec_vp9_slice_frame_ctx_lock);
static int vdec_vp9_slice_core_decode(struct vdec_lat_buf *lat_buf);
+static u32 vdec_vp9_get_frame_ctx_addr(struct mtk_vcodec_dec_ctx *ctx,
+ struct vdec_vp9_slice_init_vsi *vsi)
+{
+ if (mtk_vcodec_fw_get_type(ctx->dev->fw_handler) == VCP)
+ return VP9_FRAME_MEM;
+ else
+ return (u32)vsi->default_frame_ctx;
+}
+
static int vdec_vp9_slice_init_default_frame_ctx(struct vdec_vp9_slice_instance *instance)
{
struct vdec_vp9_slice_frame_ctx *remote_frame_ctx;
@@ -514,7 +523,7 @@ static int vdec_vp9_slice_init_default_frame_ctx(struct vdec_vp9_slice_instance
return -EINVAL;
remote_frame_ctx = mtk_vcodec_fw_map_dm_addr(ctx->dev->fw_handler,
- (u32)vsi->default_frame_ctx);
+ vdec_vp9_get_frame_ctx_addr(ctx, vsi));
if (!remote_frame_ctx) {
mtk_vdec_err(ctx, "failed to map default frame ctx\n");
return -EINVAL;
@@ -1831,6 +1840,15 @@ static int vdec_vp9_slice_update_core(struct vdec_vp9_slice_instance *instance,
return 0;
}
+static u32 vdec_vp9_get_core_vsi_addr(struct mtk_vcodec_dec_ctx *ctx,
+ struct vdec_vp9_slice_init_vsi *vsi)
+{
+ if (mtk_vcodec_fw_get_type(ctx->dev->fw_handler) == VCP)
+ return VCODEC_CORE_MEM;
+ else
+ return (u32)vsi->core_vsi;
+}
+
static int vdec_vp9_slice_init(struct mtk_vcodec_dec_ctx *ctx)
{
enum mtk_vcodec_fw_type fw_type = ctx->dev->fw_handler->type;
@@ -1864,7 +1882,7 @@ static int vdec_vp9_slice_init(struct mtk_vcodec_dec_ctx *ctx)
}
instance->init_vsi = vsi;
instance->core_vsi = mtk_vcodec_fw_map_dm_addr(ctx->dev->fw_handler,
- (u32)vsi->core_vsi);
+ vdec_vp9_get_core_vsi_addr(ctx, vsi));
if (!instance->core_vsi) {
mtk_vdec_err(ctx, "failed to get VP9 core vsi\n");
ret = -EINVAL;
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c
index b35759a0..cdb673e6 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c
@@ -9,6 +9,14 @@
#include "vdec_ipi_msg.h"
#include "vdec_vpu_if.h"
+static u32 vpu_dec_get_vsi_addr(struct vdec_vpu_inst *vpu, const struct vdec_vpu_ipi_init_ack *msg)
+{
+ if (mtk_vcodec_fw_get_type(vpu->ctx->dev->fw_handler) == VCP)
+ return VCODEC_LAT_MEM;
+ else
+ return msg->vpu_inst_addr;
+}
+
static void handle_init_ack_msg(const struct vdec_vpu_ipi_init_ack *msg)
{
struct vdec_vpu_inst *vpu = (struct vdec_vpu_inst *)
@@ -19,7 +27,7 @@ static void handle_init_ack_msg(const struct vdec_vpu_ipi_init_ack *msg)
/* mapping VPU address to kernel virtual address */
/* the content in vsi is initialized to 0 in VPU */
vpu->vsi = mtk_vcodec_fw_map_dm_addr(vpu->ctx->dev->fw_handler,
- msg->vpu_inst_addr);
+ vpu_dec_get_vsi_addr(vpu, msg));
vpu->inst_addr = msg->vpu_inst_addr;
mtk_vdec_debug(vpu->ctx, "- vpu_inst_addr = 0x%x", vpu->inst_addr);
^ permalink raw reply related [flat|nested] 24+ messages in thread* Re: [PATCH v8 05/14] media: mediatek: vcodec: get share memory address
2026-10-10 8:36 ` [PATCH v8 05/14] media: mediatek: vcodec: get share memory address Kyrie Wu
@ 2026-10-10 8:56 ` sashiko-bot
0 siblings, 0 replies; 24+ messages in thread
From: sashiko-bot @ 2026-10-10 8:56 UTC (permalink / raw)
To: Kyrie Wu; +Cc: media-ci, robh, conor+dt, devicetree
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] media: vcodec: missing IS_ERR check on fw_map_dm_addr return in VP9/AV1
- [High] media: mediatek: OOB read via unvalidated firmware sizes and VCP offsets
--
commit e6e728fceb78092db0d746297086675978804b45
Author: Kyrie Wu <kyrie.wu@mediatek.com>
media: mediatek: vcodec: get share memory address
This commit divides the single shared memory for the VCP architecture
into multiple functional areas. It introduces helpers to retrieve
specific memory addresses and offsets for different codec components.
> diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c
> index ece4e3e87e62d..468c4bf4e48ba 100644
> --- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c
> +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c
[ ... ]
> +static void *mtk_vcodec_vcp_dm_addr(struct mtk_vcodec_fw *fw, u32 mem_type)
> +{
> + unsigned char *vsi_core = fw->vcp->vsi_core_addr;
> +
> + switch (mem_type) {
> + case ENCODER_MEM:
> + case VCODEC_LAT_MEM:
> + return fw->vcp->vsi_addr;
> + case VCODEC_CORE_MEM:
> + return vsi_core;
> + case VP9_FRAME_MEM:
> + return vsi_core + VCODEC_VSI_LEN;
> + case AV1_CDF_MEM:
> + return vsi_core + VCODEC_VSI_LEN + VP9_FRAME_SIZE;
> + case AV1_IQ_MEM:
> + return vsi_core + VCODEC_VSI_LEN + VP9_FRAME_SIZE + AV1_CDF_SIZE;
[Severity: High]
Does this code compute memory pointers based on hardcoded offsets without
first verifying that the configured VCP memory size (fw->vcp->vsi_size) is
actually large enough to contain them?
If the device tree memory region is insufficiently sized, could this expose
the kernel to out-of-bounds reads?
> + default:
> + break;
> + }
> +
> return NULL;
> }
[ ... ]
> diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
> index 756fbb7778b1f..4932ef4695946 100644
> --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
> +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
[ ... ]
> @@ -775,7 +784,7 @@ static int vdec_av1_slice_init_cdf_table(struct vdec_av1_slice_instance *instanc
> ctx = instance->ctx;
> vsi = instance->vpu.vsi;
> remote_cdf_table = mtk_vcodec_fw_map_dm_addr(ctx->dev->fw_handler,
> - (u32)vsi->cdf_table_addr);
> + vdec_av1_get_cdf_table_addr(ctx, vsi));
[Severity: High]
Is memory being copied based on an untrusted size parameter
(vsi->cdf_table_size) provided by firmware?
Looking at vdec_av1_slice_init_cdf_table(), if a compromised firmware
supplies an excessively large size, couldn't this cause the kernel to read
out of bounds from remote_cdf_table into the dynamically sized DMA buffer?
This seems unmitigated for non-EXT capability devices.
> if (IS_ERR(remote_cdf_table)) {
> mtk_vdec_err(ctx, "failed to map cdf table\n");
> return PTR_ERR(remote_cdf_table);
[ ... ]
> @@ -1904,8 +1931,8 @@ static int vdec_av1_slice_init(struct mtk_vcodec_dec_ctx *ctx)
> goto error_vsi;
> }
> instance->init_vsi = vsi;
> - instance->core_vsi = mtk_vcodec_fw_map_dm_addr(ctx->dev->fw_handler, (u32)vsi->core_vsi);
> -
> + instance->core_vsi = mtk_vcodec_fw_map_dm_addr(ctx->dev->fw_handler,
> + vdec_av1_get_core_vsi_addr(ctx, vsi));
> if (!instance->core_vsi) {
[Severity: High]
Could an error pointer bypass this check when mtk_vcodec_fw_map_dm_addr()
fails?
Since mtk_vcodec_fw_map_dm_addr() returns ERR_PTR(-EINVAL) on VPU and SCP
failures, it seems an error pointer would slip past this NULL check and
lead to invalid memory accesses in subsequent memcpy operations.
> mtk_vdec_err(ctx, "failed to get AV1 core vsi\n");
> ret = -EINVAL;
[ ... ]
> diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c
> index b492686784ec3..ee4d1cd82426f 100644
> --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c
> +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c
[ ... ]
> @@ -514,7 +523,7 @@ static int vdec_vp9_slice_init_default_frame_ctx(struct vdec_vp9_slice_instance
> return -EINVAL;
>
> remote_frame_ctx = mtk_vcodec_fw_map_dm_addr(ctx->dev->fw_handler,
> - (u32)vsi->default_frame_ctx);
> + vdec_vp9_get_frame_ctx_addr(ctx, vsi));
> if (!remote_frame_ctx) {
[Severity: High]
Is it possible for mtk_vcodec_fw_map_dm_addr() to return an ERR_PTR here?
If mapping fails, does this check miss the error pointer, allowing an
invalid memory location to be passed to kmemdup() below?
> mtk_vdec_err(ctx, "failed to map default frame ctx\n");
> return -EINVAL;
[ ... ]
> @@ -1864,7 +1882,7 @@ static int vdec_vp9_slice_init(struct mtk_vcodec_dec_ctx *ctx)
> }
> instance->init_vsi = vsi;
> instance->core_vsi = mtk_vcodec_fw_map_dm_addr(ctx->dev->fw_handler,
> - (u32)vsi->core_vsi);
> + vdec_vp9_get_core_vsi_addr(ctx, vsi));
> if (!instance->core_vsi) {
[Severity: High]
What happens if mtk_vcodec_fw_map_dm_addr() returns an error pointer?
If it only checks for NULL, it appears the error pointer could be stored
in instance->core_vsi and erroneously used as a valid memory location later.
> mtk_vdec_err(ctx, "failed to get VP9 core vsi\n");
> ret = -EINVAL;
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1791619742.git.kyrie.wu@mediatek.com?part=5
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v8 06/14] media: mediatek: vcodec: add debug information
2026-10-10 8:35 [PATCH v8 00/14] media: mediatek: vcodec: support video decoder in mt8196 Kyrie Wu
` (4 preceding siblings ...)
2026-10-10 8:36 ` [PATCH v8 05/14] media: mediatek: vcodec: get share memory address Kyrie Wu
@ 2026-10-10 8:36 ` Kyrie Wu
2026-10-10 8:48 ` sashiko-bot
2026-10-10 8:36 ` [PATCH v8 07/14] media: mediatek: vcodec: send share memory address to vcp Kyrie Wu
` (7 subsequent siblings)
13 siblings, 1 reply; 24+ messages in thread
From: Kyrie Wu @ 2026-10-10 8:36 UTC (permalink / raw)
To: Tiffany Lin, Andrew-CT Chen, Yunfei Dong, Mauro Carvalho Chehab,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Kyrie Wu, Nicolas Dufresne,
Ricardo Ribalda, Kees Cook, Hans Verkuil, Fei Shao, Haoxiang Li,
Chen-Yu Tsai, Laurent Pinchart, Tomasz Figa, Philipp Zabel,
Benjamin Gaignard, Qianfeng Rong, Irui Wang, Jacopo Mondi, Fan Wu,
linux-media, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek
Cc: Sakari Ailus
Print hevc/av1 output format and 10bit capture format
information to debug.
Signed-off-by: Kyrie Wu <kyrie.wu@mediatek.com>
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
---
.../mediatek/vcodec/common/mtk_vcodec_dbgfs.c | 21 +++++++++++++++++--
1 file changed, 19 insertions(+), 2 deletions(-)
diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_dbgfs.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_dbgfs.c
index 2da11521..a9e52a32 100644
--- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_dbgfs.c
+++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_dbgfs.c
@@ -29,6 +29,14 @@ static void mtk_vdec_dbgfs_get_format_type(struct mtk_vcodec_dec_ctx *ctx, char
curr_len = snprintf(buf + *used, total - *used,
"\toutput format: vp9 slice\n");
break;
+ case V4L2_PIX_FMT_HEVC_SLICE:
+ curr_len = snprintf(buf + *used, total - *used,
+ "\toutput format: hevc slice\n");
+ break;
+ case V4L2_PIX_FMT_AV1_FRAME:
+ curr_len = snprintf(buf + *used, total - *used,
+ "\toutput format: av1 slice\n");
+ break;
default:
curr_len = snprintf(buf + *used, total - *used,
"\tunsupported output format: 0x%x\n",
@@ -45,6 +53,14 @@ static void mtk_vdec_dbgfs_get_format_type(struct mtk_vcodec_dec_ctx *ctx, char
curr_len = snprintf(buf + *used, total - *used,
"\tcapture format: MT21C\n");
break;
+ case V4L2_PIX_FMT_MT2110T:
+ curr_len = snprintf(buf + *used, total - *used,
+ "\tcapture format: MT2110T (10bit tile mode)\n");
+ break;
+ case V4L2_PIX_FMT_MT2110R:
+ curr_len = snprintf(buf + *used, total - *used,
+ "\tcapture format: MT2110R (10bit raster mode)\n");
+ break;
default:
curr_len = snprintf(buf + *used, total - *used,
"\tunsupported capture format: 0x%x\n",
@@ -122,9 +138,10 @@ static ssize_t mtk_vdec_dbgfs_read(struct file *filp, char __user *ubuf,
if (dbgfs_index[MTK_VDEC_DBGFS_PICINFO]) {
curr_len = snprintf(buf + used_len, total_len - used_len,
- "\treal(%dx%d)=>align(%dx%d)\n",
+ "\treal(%dx%d)=>align(%dx%d) 10bit(%d)\n",
ctx->picinfo.pic_w, ctx->picinfo.pic_h,
- ctx->picinfo.buf_w, ctx->picinfo.buf_h);
+ ctx->picinfo.buf_w, ctx->picinfo.buf_h,
+ ctx->is_10bit_bitstream);
used_len += curr_len;
}
^ permalink raw reply related [flat|nested] 24+ messages in thread* Re: [PATCH v8 06/14] media: mediatek: vcodec: add debug information
2026-10-10 8:36 ` [PATCH v8 06/14] media: mediatek: vcodec: add debug information Kyrie Wu
@ 2026-10-10 8:48 ` sashiko-bot
0 siblings, 0 replies; 24+ messages in thread
From: sashiko-bot @ 2026-10-10 8:48 UTC (permalink / raw)
To: Kyrie Wu; +Cc: robh, devicetree, conor+dt, media-ci
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] media: mediatek: vcodec: UAF due to lock mismatch in debugfs iteration
--
commit 3dd0220ecce9f2ca273e1906b8312cd6795ec662
Author: Kyrie Wu <kyrie.wu@mediatek.com>
media: mediatek: vcodec: add debug information
This commit adds debug information for HEVC/AV1 output formats and 10-bit
capture formats. It updates the debugfs read function to include 10-bit
bitstream information.
> diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_dbgfs.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_dbgfs.c
> index 2da11521fc7b9..a9e52a32079d3 100644
> --- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_dbgfs.c
> +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_dbgfs.c
> @@ -122,9 +138,10 @@ static ssize_t mtk_vdec_dbgfs_read(struct file *filp, char __user *ubuf,
>
> if (dbgfs_index[MTK_VDEC_DBGFS_PICINFO]) {
> curr_len = snprintf(buf + used_len, total_len - used_len,
> - "\treal(%dx%d)=>align(%dx%d)\n",
> + "\treal(%dx%d)=>align(%dx%d) 10bit(%d)\n",
> ctx->picinfo.pic_w, ctx->picinfo.pic_h,
> - ctx->picinfo.buf_w, ctx->picinfo.buf_h);
> + ctx->picinfo.buf_w, ctx->picinfo.buf_h,
> + ctx->is_10bit_bitstream);
[Severity: High]
Can a concurrent release of the video device cause a use-after-free when
accessing ctx in this debugfs read path?
In mtk_vdec_dbgfs_read(), the iteration over dbgfs_head is protected by
dbgfs->dbgfs_lock. However, a concurrent device release calling
mtk_vcodec_dbgfs_remove() removes and frees dbgfs_inst without acquiring
dbgfs_lock:
drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_dbgfs.c:mtk_vcodec_dbgfs_remove() {
list_for_each_entry(dbgfs_inst, &vcodec_dev->dbgfs.dbgfs_head, node) {
if (dbgfs_inst->inst_id == ctx_id) {
...
list_del(&dbgfs_inst->node);
kfree(dbgfs_inst);
return;
}
}
}
Because mtk_vcodec_dbgfs_remove() does not take dbgfs_lock,
mtk_vdec_dbgfs_read() can dereference a freed dbgfs_inst and a freed ctx
when a user reads the debugfs file at the same time the device is closed.
Should the list removal in mtk_vcodec_dbgfs_remove() be protected by
dbgfs_lock?
> used_len += curr_len;
> }
>
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1791619742.git.kyrie.wu@mediatek.com?part=6
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v8 07/14] media: mediatek: vcodec: send share memory address to vcp
2026-10-10 8:35 [PATCH v8 00/14] media: mediatek: vcodec: support video decoder in mt8196 Kyrie Wu
` (5 preceding siblings ...)
2026-10-10 8:36 ` [PATCH v8 06/14] media: mediatek: vcodec: add debug information Kyrie Wu
@ 2026-10-10 8:36 ` Kyrie Wu
2026-10-10 8:46 ` sashiko-bot
2026-10-10 8:36 ` [PATCH v8 08/14] dt-bindings: media: mediatek,vcodec-subdev-decoder: Add MT8196 Kyrie Wu
` (6 subsequent siblings)
13 siblings, 1 reply; 24+ messages in thread
From: Kyrie Wu @ 2026-10-10 8:36 UTC (permalink / raw)
To: Tiffany Lin, Andrew-CT Chen, Yunfei Dong, Mauro Carvalho Chehab,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Kyrie Wu, Nicolas Dufresne,
Ricardo Ribalda, Kees Cook, Hans Verkuil, Fei Shao, Haoxiang Li,
Chen-Yu Tsai, Laurent Pinchart, Tomasz Figa, Philipp Zabel,
Benjamin Gaignard, Qianfeng Rong, Irui Wang, Jacopo Mondi, Fan Wu,
linux-media, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek
Cc: Sakari Ailus
The share memory is allocated in kernel for vcp architecture, it's
different with vpu which share memors is reserved in vpu micro
processor. Need to send share memory address to vcp.
Signed-off-by: Kyrie Wu <kyrie.wu@mediatek.com>
---
drivers/media/platform/mediatek/vcodec/decoder/vdec_ipi_msg.h | 2 ++
drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c | 2 ++
2 files changed, 4 insertions(+)
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec_ipi_msg.h b/drivers/media/platform/mediatek/vcodec/decoder/vdec_ipi_msg.h
index 47070be2..097561a1 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec_ipi_msg.h
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec_ipi_msg.h
@@ -67,11 +67,13 @@ struct vdec_vpu_ipi_ack {
* @msg_id : AP_IPIMSG_DEC_INIT
* @codec_type : codec fourcc
* @ap_inst_addr : AP video decoder instance address
+ * @shared_iova : reserved share memory address
*/
struct vdec_ap_ipi_init {
uint32_t msg_id;
u32 codec_type;
uint64_t ap_inst_addr;
+ u64 shared_iova;
};
/**
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c
index cdb673e6..3a10b32b 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c
@@ -236,6 +236,8 @@ int vpu_dec_init(struct vdec_vpu_inst *vpu)
msg.msg_id = AP_IPIMSG_DEC_INIT;
msg.ap_inst_addr = (unsigned long)vpu;
msg.codec_type = vpu->codec_type;
+ if (mtk_vcodec_fw_get_type(vpu->ctx->dev->fw_handler) == VCP)
+ msg.shared_iova = vpu->ctx->dev->fw_handler->vcp->iova_addr;
mtk_vdec_debug(vpu->ctx, "vdec_inst=%p", vpu);
^ permalink raw reply related [flat|nested] 24+ messages in thread* Re: [PATCH v8 07/14] media: mediatek: vcodec: send share memory address to vcp
2026-10-10 8:36 ` [PATCH v8 07/14] media: mediatek: vcodec: send share memory address to vcp Kyrie Wu
@ 2026-10-10 8:46 ` sashiko-bot
0 siblings, 0 replies; 24+ messages in thread
From: sashiko-bot @ 2026-10-10 8:46 UTC (permalink / raw)
To: Kyrie Wu; +Cc: devicetree, robh, conor+dt, media-ci
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] media: vcodec: IPI msg size increase may break older firmware
--
commit 82cf992a706e24d85ad99ada3a7b106540bc8145
Author: Kyrie Wu <kyrie.wu@mediatek.com>
media: mediatek: vcodec: send share memory address to vcp
The share memory is allocated in kernel for vcp architecture, it's
different with vpu which share memors is reserved in vpu micro
processor. Need to send share memory address to vcp.
> diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec_ipi_msg.h b/drivers/media/platform/mediatek/vcodec/decoder/vdec_ipi_msg.h
> index c2c2a3a63dfd3..8b771d47053b0 100644
> --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec_ipi_msg.h
> +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec_ipi_msg.h
> @@ -67,11 +67,13 @@ struct vdec_vpu_ipi_ack {
> * @msg_id : AP_IPIMSG_DEC_INIT
> * @codec_type : codec fourcc
> * @ap_inst_addr : AP video decoder instance address
> + * @shared_iova : reserved share memory address
> */
> struct vdec_ap_ipi_init {
> uint32_t msg_id;
> u32 codec_type;
> uint64_t ap_inst_addr;
> + u64 shared_iova;
> };
[Severity: Medium]
Does this struct size change break older firmware?
The size of struct vdec_ap_ipi_init is increased from 16 to 24 bytes here.
[ ... ]
> diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c
> index cdb673e6b4770..3a10b32be0945 100644
> --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c
> +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c
> @@ -236,6 +236,8 @@ int vpu_dec_init(struct vdec_vpu_inst *vpu)
> msg.msg_id = AP_IPIMSG_DEC_INIT;
> msg.ap_inst_addr = (unsigned long)vpu;
> msg.codec_type = vpu->codec_type;
> + if (mtk_vcodec_fw_get_type(vpu->ctx->dev->fw_handler) == VCP)
> + msg.shared_iova = vpu->ctx->dev->fw_handler->vcp->iova_addr;
>
> mtk_vdec_debug(vpu->ctx, "vdec_inst=%p", vpu);
[Severity: Medium]
Will this unconditionally send the new 24-byte size to all firmware types?
In vpu_dec_init(), vcodec_vpu_send_msg() is called right below this:
err = vcodec_vpu_send_msg(vpu, (void *)&msg, sizeof(msg));
If older VPU or SCP firmwares strictly validate the IPI message length,
could the video decoder initialization fail and render the device unusable?
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1791619742.git.kyrie.wu@mediatek.com?part=7
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v8 08/14] dt-bindings: media: mediatek,vcodec-subdev-decoder: Add MT8196
2026-10-10 8:35 [PATCH v8 00/14] media: mediatek: vcodec: support video decoder in mt8196 Kyrie Wu
` (6 preceding siblings ...)
2026-10-10 8:36 ` [PATCH v8 07/14] media: mediatek: vcodec: send share memory address to vcp Kyrie Wu
@ 2026-10-10 8:36 ` Kyrie Wu
2026-10-10 8:36 ` [PATCH v8 09/14] media: mediatek: vcodec: add decoder compatible to support mt8196 Kyrie Wu
` (5 subsequent siblings)
13 siblings, 0 replies; 24+ messages in thread
From: Kyrie Wu @ 2026-10-10 8:36 UTC (permalink / raw)
To: Tiffany Lin, Andrew-CT Chen, Yunfei Dong, Mauro Carvalho Chehab,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Kyrie Wu, Nicolas Dufresne,
Ricardo Ribalda, Kees Cook, Hans Verkuil, Fei Shao, Haoxiang Li,
Chen-Yu Tsai, Laurent Pinchart, Tomasz Figa, Philipp Zabel,
Benjamin Gaignard, Qianfeng Rong, Irui Wang, Jacopo Mondi, Fan Wu,
linux-media, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek
Cc: Sakari Ailus
Compared to previous ICs, the MT8196 supports a 10-bit decoder
and has a decoding capability of 4K@120fps. It also supports
36-bit DRAM IOVA address and Video Power Control to optimize
bandwidth and voltage usage.
Signed-off-by: Kyrie Wu <kyrie.wu@mediatek.com>
Acked-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
---
.../media/mediatek,vcodec-subdev-decoder.yaml | 49 ++++++++++++++++++-
1 file changed, 48 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/media/mediatek,vcodec-subdev-decoder.yaml b/Documentation/devicetree/bindings/media/mediatek,vcodec-subdev-decoder.yaml
index d1d209cb..d8f25ccd 100644
--- a/Documentation/devicetree/bindings/media/mediatek,vcodec-subdev-decoder.yaml
+++ b/Documentation/devicetree/bindings/media/mediatek,vcodec-subdev-decoder.yaml
@@ -76,6 +76,7 @@ properties:
- mediatek,mt8186-vcodec-dec
- mediatek,mt8188-vcodec-dec
- mediatek,mt8195-vcodec-dec
+ - mediatek,mt8196-vcodec-dec
reg:
minItems: 1
@@ -96,6 +97,11 @@ properties:
The node of system control processor (SCP), using
the remoteproc & rpmsg framework.
+ mediatek,vcp:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description: Phandle to the Video Companion Processor (VCP) that runs the
+ video decoder firmware.
+
"#address-cells":
const: 2
@@ -163,7 +169,6 @@ required:
- compatible
- reg
- iommus
- - mediatek,scp
- ranges
if:
@@ -179,6 +184,48 @@ then:
- interrupts
allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: mediatek,mt8196-vcodec-dec
+
+ then:
+ required:
+ - mediatek,vcp
+
+ properties:
+ mediatek,scp: false
+
+ patternProperties:
+ '^video-codec@[0-9a-f]+$':
+ properties:
+ compatible:
+ enum:
+ - mediatek,mtk-vcodec-core
+ - mediatek,mtk-vcodec-lat
+
+ clock-names:
+ items:
+ - const: sel
+ - const: vdec
+ - const: lat
+ - const: top
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - mediatek,mt8192-vcodec-dec
+ - mediatek,mt8186-vcodec-dec
+ - mediatek,mt8188-vcodec-dec
+ - mediatek,mt8195-vcodec-dec
+
+ then:
+ required:
+ - mediatek,scp
+
- if:
properties:
compatible:
^ permalink raw reply related [flat|nested] 24+ messages in thread* [PATCH v8 09/14] media: mediatek: vcodec: add decoder compatible to support mt8196
2026-10-10 8:35 [PATCH v8 00/14] media: mediatek: vcodec: support video decoder in mt8196 Kyrie Wu
` (7 preceding siblings ...)
2026-10-10 8:36 ` [PATCH v8 08/14] dt-bindings: media: mediatek,vcodec-subdev-decoder: Add MT8196 Kyrie Wu
@ 2026-10-10 8:36 ` Kyrie Wu
2026-10-10 8:36 ` [PATCH v8 10/14] media: mediatek: vcodec: define MT8196 vcodec levels Kyrie Wu
` (4 subsequent siblings)
13 siblings, 0 replies; 24+ messages in thread
From: Kyrie Wu @ 2026-10-10 8:36 UTC (permalink / raw)
To: Tiffany Lin, Andrew-CT Chen, Yunfei Dong, Mauro Carvalho Chehab,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Kyrie Wu, Nicolas Dufresne,
Ricardo Ribalda, Kees Cook, Hans Verkuil, Fei Shao, Haoxiang Li,
Chen-Yu Tsai, Laurent Pinchart, Tomasz Figa, Philipp Zabel,
Benjamin Gaignard, Qianfeng Rong, Irui Wang, Jacopo Mondi, Fan Wu,
linux-media, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek
Cc: Sakari Ailus
MT8196 is lat single core architecture. Support its compatible and
use `mtk_lat_sig_core_pdata` to initialize platform data.
Signed-off-by: Kyrie Wu <kyrie.wu@mediatek.com>
---
.../platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c | 6 ++++++
.../platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h | 1 +
2 files changed, 7 insertions(+)
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c
index ab5f6c01..0d27adbf 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c
@@ -347,6 +347,8 @@ static void mtk_vcodec_dec_get_chip_name(struct mtk_vcodec_dec_dev *vdec_dev)
vdec_dev->chip_name = MTK_VDEC_MT8186;
else if (of_device_is_compatible(dev->of_node, "mediatek,mt8188-vcodec-dec"))
vdec_dev->chip_name = MTK_VDEC_MT8188;
+ else if (of_device_is_compatible(dev->of_node, "mediatek,mt8196-vcodec-dec"))
+ vdec_dev->chip_name = MTK_VDEC_MT8196;
else
vdec_dev->chip_name = MTK_VDEC_INVAL;
}
@@ -566,6 +568,10 @@ static const struct of_device_id mtk_vcodec_match[] = {
.compatible = "mediatek,mt8188-vcodec-dec",
.data = &mtk_lat_sig_core_pdata,
},
+ {
+ .compatible = "mediatek,mt8196-vcodec-dec",
+ .data = &mtk_lat_sig_core_pdata,
+ },
{},
};
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h
index 15e43732..53d1708b 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h
+++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h
@@ -29,6 +29,7 @@ enum mtk_vcodec_dec_chip_name {
MTK_VDEC_MT8188 = 8188,
MTK_VDEC_MT8192 = 8192,
MTK_VDEC_MT8195 = 8195,
+ MTK_VDEC_MT8196 = 8196,
};
/*
^ permalink raw reply related [flat|nested] 24+ messages in thread* [PATCH v8 10/14] media: mediatek: vcodec: define MT8196 vcodec levels.
2026-10-10 8:35 [PATCH v8 00/14] media: mediatek: vcodec: support video decoder in mt8196 Kyrie Wu
` (8 preceding siblings ...)
2026-10-10 8:36 ` [PATCH v8 09/14] media: mediatek: vcodec: add decoder compatible to support mt8196 Kyrie Wu
@ 2026-10-10 8:36 ` Kyrie Wu
2026-10-10 8:36 ` [PATCH v8 11/14] media: mediatek: vcodec: support 36bit iova address Kyrie Wu
` (3 subsequent siblings)
13 siblings, 0 replies; 24+ messages in thread
From: Kyrie Wu @ 2026-10-10 8:36 UTC (permalink / raw)
To: Tiffany Lin, Andrew-CT Chen, Yunfei Dong, Mauro Carvalho Chehab,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Kyrie Wu, Nicolas Dufresne,
Ricardo Ribalda, Kees Cook, Hans Verkuil, Fei Shao, Haoxiang Li,
Chen-Yu Tsai, Laurent Pinchart, Tomasz Figa, Philipp Zabel,
Benjamin Gaignard, Qianfeng Rong, Irui Wang, Jacopo Mondi, Fan Wu,
linux-media, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek
Cc: Sakari Ailus
The supported level and profile are not the same for different
codecs and architecture. Select the correct one.
Signed-off-by: Kyrie Wu <kyrie.wu@mediatek.com>
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
---
.../mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c
index ab1894fb..472ece57 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c
@@ -577,6 +577,7 @@ static void mtk_vcodec_dec_fill_h264_level(struct v4l2_ctrl_config *cfg,
cfg->max = V4L2_MPEG_VIDEO_H264_LEVEL_5_2;
break;
case MTK_VDEC_MT8195:
+ case MTK_VDEC_MT8196:
cfg->max = V4L2_MPEG_VIDEO_H264_LEVEL_6_0;
break;
case MTK_VDEC_MT8183:
@@ -595,6 +596,7 @@ static void mtk_vcodec_dec_fill_h264_profile(struct v4l2_ctrl_config *cfg,
switch (ctx->dev->chip_name) {
case MTK_VDEC_MT8188:
case MTK_VDEC_MT8195:
+ case MTK_VDEC_MT8196:
cfg->max = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10;
break;
default:
@@ -611,6 +613,7 @@ static void mtk_vcodec_dec_fill_h265_level(struct v4l2_ctrl_config *cfg,
cfg->max = V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1;
break;
case MTK_VDEC_MT8195:
+ case MTK_VDEC_MT8196:
cfg->max = V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2;
break;
default:
@@ -625,6 +628,7 @@ static void mtk_vcodec_dec_fill_h265_profile(struct v4l2_ctrl_config *cfg,
switch (ctx->dev->chip_name) {
case MTK_VDEC_MT8188:
case MTK_VDEC_MT8195:
+ case MTK_VDEC_MT8196:
cfg->max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10;
break;
default:
@@ -642,6 +646,7 @@ static void mtk_vcodec_dec_fill_vp9_level(struct v4l2_ctrl_config *cfg,
cfg->max = V4L2_MPEG_VIDEO_VP9_LEVEL_5_1;
break;
case MTK_VDEC_MT8195:
+ case MTK_VDEC_MT8196:
cfg->max = V4L2_MPEG_VIDEO_VP9_LEVEL_5_2;
break;
case MTK_VDEC_MT8186:
@@ -659,6 +664,7 @@ static void mtk_vcodec_dec_fill_vp9_profile(struct v4l2_ctrl_config *cfg,
switch (ctx->dev->chip_name) {
case MTK_VDEC_MT8188:
case MTK_VDEC_MT8195:
+ case MTK_VDEC_MT8196:
cfg->max = V4L2_MPEG_VIDEO_VP9_PROFILE_2;
break;
default:
^ permalink raw reply related [flat|nested] 24+ messages in thread* [PATCH v8 11/14] media: mediatek: vcodec: support 36bit iova address
2026-10-10 8:35 [PATCH v8 00/14] media: mediatek: vcodec: support video decoder in mt8196 Kyrie Wu
` (9 preceding siblings ...)
2026-10-10 8:36 ` [PATCH v8 10/14] media: mediatek: vcodec: define MT8196 vcodec levels Kyrie Wu
@ 2026-10-10 8:36 ` Kyrie Wu
2026-10-10 8:36 ` [PATCH v8 12/14] media: mediatek: vcodec: clean xpc status Kyrie Wu
` (2 subsequent siblings)
13 siblings, 0 replies; 24+ messages in thread
From: Kyrie Wu @ 2026-10-10 8:36 UTC (permalink / raw)
To: Tiffany Lin, Andrew-CT Chen, Yunfei Dong, Mauro Carvalho Chehab,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Kyrie Wu, Nicolas Dufresne,
Ricardo Ribalda, Kees Cook, Hans Verkuil, Fei Shao, Haoxiang Li,
Chen-Yu Tsai, Laurent Pinchart, Tomasz Figa, Philipp Zabel,
Benjamin Gaignard, Qianfeng Rong, Irui Wang, Jacopo Mondi, Fan Wu,
linux-media, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek
Cc: Sakari Ailus
Need to set dma mask to support 36bit iova address for decoder
hardware can use 36bit address to decode for mt8196.
Signed-off-by: Kyrie Wu <kyrie.wu@mediatek.com>
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
---
.../platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c
index 0d27adbf..4d65c102 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c
@@ -392,6 +392,13 @@ static int mtk_vcodec_probe(struct platform_device *pdev)
return -ENODEV;
}
dma_set_max_seg_size(&pdev->dev, UINT_MAX);
+ if (dev->chip_name == MTK_VDEC_MT8196) {
+ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(36));
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to enable 36-bit DMA: %d\n", ret);
+ return ret;
+ }
+ }
dev->fw_handler = mtk_vcodec_fw_select(dev, DECODER, dev->fw_init);
if (IS_ERR(dev->fw_handler))
^ permalink raw reply related [flat|nested] 24+ messages in thread* [PATCH v8 12/14] media: mediatek: vcodec: clean xpc status
2026-10-10 8:35 [PATCH v8 00/14] media: mediatek: vcodec: support video decoder in mt8196 Kyrie Wu
` (10 preceding siblings ...)
2026-10-10 8:36 ` [PATCH v8 11/14] media: mediatek: vcodec: support 36bit iova address Kyrie Wu
@ 2026-10-10 8:36 ` Kyrie Wu
2026-10-10 8:50 ` sashiko-bot
2026-10-10 8:36 ` [PATCH v8 13/14] media: mediatek: decoder: fill av1 buffer size with picinfo Kyrie Wu
2026-10-10 8:36 ` [PATCH v8 14/14] media: mediatek: decoder: support av1 extend vsi Kyrie Wu
13 siblings, 1 reply; 24+ messages in thread
From: Kyrie Wu @ 2026-10-10 8:36 UTC (permalink / raw)
To: Tiffany Lin, Andrew-CT Chen, Yunfei Dong, Mauro Carvalho Chehab,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Kyrie Wu, Nicolas Dufresne,
Ricardo Ribalda, Kees Cook, Hans Verkuil, Fei Shao, Haoxiang Li,
Chen-Yu Tsai, Laurent Pinchart, Tomasz Figa, Philipp Zabel,
Benjamin Gaignard, Qianfeng Rong, Irui Wang, Jacopo Mondi, Fan Wu,
linux-media, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek
Cc: Sakari Ailus
The driver need to clean xpc status when receive decoder hardware
interrupt for mt8196 platform.
Signed-off-by: Kyrie Wu <kyrie.wu@mediatek.com>
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
---
.../vcodec/decoder/mtk_vcodec_dec_hw.c | 31 +++++++++++++++++++
.../vcodec/decoder/mtk_vcodec_dec_hw.h | 13 ++++++--
2 files changed, 42 insertions(+), 2 deletions(-)
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_hw.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_hw.c
index 881d5de4..46ac3e41 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_hw.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_hw.c
@@ -61,6 +61,34 @@ static int mtk_vdec_hw_prob_done(struct mtk_vcodec_dec_dev *vdec_dev)
return 0;
}
+static void mtk_vdec_hw_write_reg_mask(void __iomem *reg_base, u32 reg_offset, u32 val, u32 mask)
+{
+ void __iomem *reg_addr = reg_base + reg_offset;
+ u32 reg_val;
+
+ reg_val = readl(reg_addr);
+ reg_val &= ~mask;
+ reg_val |= (val & mask);
+ writel(reg_val, reg_addr);
+}
+
+static void mtk_vdec_hw_clean_xpc(struct mtk_vdec_hw_dev *dev)
+{
+ unsigned long flags;
+ u32 val, mask, addr = VDEC_XPC_CLEAN_ADDR;
+
+ if (dev->main_dev->chip_name != MTK_VDEC_MT8196)
+ return;
+
+ val = dev->hw_idx == MTK_VDEC_LAT0 ? VDEC_XPC_LAT_VAL : VDEC_XPC_CORE_VAL;
+ mask = dev->hw_idx == MTK_VDEC_LAT0 ? VDEC_XPC_LAT_MASK : VDEC_XPC_CORE_MASK;
+
+ spin_lock_irqsave(&dev->main_dev->irqlock, flags);
+ mtk_vdec_hw_write_reg_mask(dev->reg_base[VDEC_HW_XPC], addr, val, mask);
+ mtk_vdec_hw_write_reg_mask(dev->reg_base[VDEC_HW_XPC], addr, 0, mask);
+ spin_unlock_irqrestore(&dev->main_dev->irqlock, flags);
+}
+
static irqreturn_t mtk_vdec_hw_irq_handler(int irq, void *priv)
{
struct mtk_vdec_hw_dev *dev = priv;
@@ -88,6 +116,8 @@ static irqreturn_t mtk_vdec_hw_irq_handler(int irq, void *priv)
writel(dec_done_status | VDEC_IRQ_CFG, vdec_misc_addr);
writel(dec_done_status & ~VDEC_IRQ_CLR, vdec_misc_addr);
+ mtk_vdec_hw_clean_xpc(dev);
+
wake_up_dec_ctx(ctx, MTK_INST_IRQ_RECEIVED, dev->hw_idx);
mtk_v4l2_vdec_dbg(3, ctx, "wake up ctx %d, dec_done_status=%x",
@@ -166,6 +196,7 @@ static int mtk_vdec_hw_probe(struct platform_device *pdev)
subdev_dev->hw_idx = hw_idx;
subdev_dev->main_dev = main_dev;
subdev_dev->reg_base[VDEC_HW_SYS] = main_dev->reg_base[VDEC_HW_SYS];
+ subdev_dev->reg_base[VDEC_HW_XPC] = main_dev->reg_base[VDEC_HW_MISC];
set_bit(subdev_dev->hw_idx, main_dev->subdev_bitmap);
if (IS_SUPPORT_VDEC_HW_IRQ(hw_idx)) {
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_hw.h b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_hw.h
index 83fe8b94..5c906143 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_hw.h
+++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_hw.h
@@ -18,17 +18,26 @@
#define VDEC_IRQ_CLR 0x10
#define VDEC_IRQ_CFG_REG 0xa4
+#define VDEC_XPC_CLEAN_ADDR 0xc
+#define VDEC_XPC_LAT_VAL BIT(0)
+#define VDEC_XPC_LAT_MASK BIT(0)
+
+#define VDEC_XPC_CORE_VAL BIT(4)
+#define VDEC_XPC_CORE_MASK BIT(4)
+
#define IS_SUPPORT_VDEC_HW_IRQ(hw_idx) ((hw_idx) != MTK_VDEC_LAT_SOC)
/**
* enum mtk_vdec_hw_reg_idx - subdev hardware register base index
- * @VDEC_HW_SYS : vdec soc register index
+ * @VDEC_HW_SYS: vdec soc register index
* @VDEC_HW_MISC: vdec misc register index
- * @VDEC_HW_MAX : vdec supported max register index
+ * @VDEC_HW_XPC: vdec xpc register index
+ * @VDEC_HW_MAX: vdec supported max register index
*/
enum mtk_vdec_hw_reg_idx {
VDEC_HW_SYS,
VDEC_HW_MISC,
+ VDEC_HW_XPC,
VDEC_HW_MAX
};
^ permalink raw reply related [flat|nested] 24+ messages in thread* Re: [PATCH v8 12/14] media: mediatek: vcodec: clean xpc status
2026-10-10 8:36 ` [PATCH v8 12/14] media: mediatek: vcodec: clean xpc status Kyrie Wu
@ 2026-10-10 8:50 ` sashiko-bot
0 siblings, 0 replies; 24+ messages in thread
From: sashiko-bot @ 2026-10-10 8:50 UTC (permalink / raw)
To: Kyrie Wu; +Cc: robh, devicetree, media-ci, conor+dt
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] media: mtk-vcodec: NULL pointer deref in mtk_vdec_hw_irq_handler()
- [High] media: mtk-vcodec: NULL pointer dereference in mtk_vdec_hw_clean_xpc() on incomplete DT
--
commit 48b9203929193f7043af88ad2f6320fd0d5acb54
Author: Kyrie Wu <kyrie.wu@mediatek.com>
media: mediatek: vcodec: clean xpc status
The driver need to clean xpc status when receive decoder hardware
interrupt for mt8196 platform.
> diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_hw.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_hw.c
> index 881d5de41e050..46ac3e41c401c 100644
> --- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_hw.c
> +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_hw.c
> @@ -61,6 +61,34 @@ static int mtk_vdec_hw_prob_done(struct mtk_vcodec_dec_dev *vdec_dev)
> return 0;
> }
>
> +static void mtk_vdec_hw_write_reg_mask(void __iomem *reg_base, u32 reg_offset, u32 val, u32 mask)
> +{
> + void __iomem *reg_addr = reg_base + reg_offset;
> + u32 reg_val;
> +
> + reg_val = readl(reg_addr);
[Severity: High]
Does this code dereference a NULL pointer if the parent device tree node
provides only one reg entry?
If the MT8196 parent device tree node provides only one reg entry (which is
permitted by the DT bindings' minItems: 1), mtk_vcodec_get_reg_bases() will map
only VDEC_SYS and leave main_dev->reg_base[VDEC_MISC] as NULL.
This NULL pointer is propagated to subdev_dev->reg_base[VDEC_HW_XPC] during
mtk_vdec_hw_probe():
subdev_dev->reg_base[VDEC_HW_XPC] = main_dev->reg_base[VDEC_HW_MISC];
When the hardware interrupt fires, mtk_vdec_hw_clean_xpc() is unconditionally
called for MT8196 and passes this NULL base to mtk_vdec_hw_write_reg_mask(),
which dereferences it here in readl().
Would it be better to validate the register base before use, or enforce
reg_num >= 2 for MT8196 during probe?
> + reg_val &= ~mask;
> + reg_val |= (val & mask);
> + writel(reg_val, reg_addr);
> +}
[ ... ]
> @@ -88,6 +116,8 @@ static irqreturn_t mtk_vdec_hw_irq_handler(int irq, void *priv)
> writel(dec_done_status | VDEC_IRQ_CFG, vdec_misc_addr);
> writel(dec_done_status & ~VDEC_IRQ_CLR, vdec_misc_addr);
>
> + mtk_vdec_hw_clean_xpc(dev);
> +
> wake_up_dec_ctx(ctx, MTK_INST_IRQ_RECEIVED, dev->hw_idx);
[Severity: High]
Can ctx be NULL here during the context teardown race window?
If a delayed or spurious interrupt fires during the teardown path window
(after mtk_vcodec_set_curr_ctx(dev, NULL, MTK_VDEC_CORE) clears the current
context, but before mtk_vcodec_dec_disable_irq() is called), or if hardware
continues generating events before IRQs are disabled, mtk_vdec_hw_irq_handler()
fetches a NULL ctx.
If the HW is inactive, the NULL ctx is unconditionally dereferenced earlier by
the mtk_v4l2_vdec_err macro to log an error. If the HW is active, it is
unconditionally dereferenced here to wake up the context queue.
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1791619742.git.kyrie.wu@mediatek.com?part=12
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v8 13/14] media: mediatek: decoder: fill av1 buffer size with picinfo
2026-10-10 8:35 [PATCH v8 00/14] media: mediatek: vcodec: support video decoder in mt8196 Kyrie Wu
` (11 preceding siblings ...)
2026-10-10 8:36 ` [PATCH v8 12/14] media: mediatek: vcodec: clean xpc status Kyrie Wu
@ 2026-10-10 8:36 ` Kyrie Wu
2026-10-10 8:57 ` sashiko-bot
2026-10-10 8:36 ` [PATCH v8 14/14] media: mediatek: decoder: support av1 extend vsi Kyrie Wu
13 siblings, 1 reply; 24+ messages in thread
From: Kyrie Wu @ 2026-10-10 8:36 UTC (permalink / raw)
To: Tiffany Lin, Andrew-CT Chen, Yunfei Dong, Mauro Carvalho Chehab,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Kyrie Wu, Nicolas Dufresne,
Ricardo Ribalda, Kees Cook, Hans Verkuil, Fei Shao, Haoxiang Li,
Chen-Yu Tsai, Laurent Pinchart, Tomasz Figa, Philipp Zabel,
Benjamin Gaignard, Qianfeng Rong, Irui Wang, Jacopo Mondi, Fan Wu,
linux-media, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek
Cc: Sakari Ailus
The buffer size of y and c plane has been calculated in vcp/scp,
can fill each frame buffer size with picinfo directly.
Signed-off-by: Kyrie Wu <kyrie.wu@mediatek.com>
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
---
.../vcodec/decoder/vdec/vdec_av1_req_lat_if.c | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
index ffb28bad..2d6e08e7 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
@@ -1812,18 +1812,19 @@ static int vdec_av1_slice_setup_core_buffer(struct vdec_av1_slice_instance *inst
{
struct vb2_buffer *vb;
struct vb2_queue *vq;
- int w, h, plane, size;
+ int plane;
int i;
plane = instance->ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes;
- w = vsi->frame.uh.upscaled_width;
- h = vsi->frame.uh.frame_height;
- size = ALIGN(w, VCODEC_DEC_ALIGNED_64) * ALIGN(h, VCODEC_DEC_ALIGNED_64);
/* frame buffer */
vsi->fb.y.dma_addr = fb->base_y.dma_addr;
+
+ vsi->fb.y.size = instance->ctx->picinfo.fb_sz[0];
+ vsi->fb.c.size = instance->ctx->picinfo.fb_sz[1];
+
if (plane == 1)
- vsi->fb.c.dma_addr = fb->base_y.dma_addr + size;
+ vsi->fb.c.dma_addr = fb->base_y.dma_addr + vsi->fb.y.size;
else
vsi->fb.c.dma_addr = fb->base_c.dma_addr;
@@ -1846,8 +1847,10 @@ static int vdec_av1_slice_setup_core_buffer(struct vdec_av1_slice_instance *inst
}
vref->y.dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+ vref->y.size = vsi->fb.y.size;
+ vref->c.size = vsi->fb.c.size;
if (plane == 1)
- vref->c.dma_addr = vref->y.dma_addr + size;
+ vref->c.dma_addr = vref->y.dma_addr + vsi->fb.y.size;
else
vref->c.dma_addr = vb2_dma_contig_plane_dma_addr(vb, 1);
}
^ permalink raw reply related [flat|nested] 24+ messages in thread* Re: [PATCH v8 13/14] media: mediatek: decoder: fill av1 buffer size with picinfo
2026-10-10 8:36 ` [PATCH v8 13/14] media: mediatek: decoder: fill av1 buffer size with picinfo Kyrie Wu
@ 2026-10-10 8:57 ` sashiko-bot
0 siblings, 0 replies; 24+ messages in thread
From: sashiko-bot @ 2026-10-10 8:57 UTC (permalink / raw)
To: Kyrie Wu; +Cc: robh, conor+dt, devicetree, media-ci
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] media: mtk-vcodec: OOB DMA write due to concurrent global picinfo mutation in stateless AV1 decoder
--
commit 4ecde7c3d617adc20b638103986012affff901df
Author: Kyrie Wu <kyrie.wu@mediatek.com>
media: mediatek: decoder: fill av1 buffer size with picinfo
This patch modifies the AV1 decoder to use the buffer sizes calculated
in the vcp/scp, filling each frame buffer size directly from picinfo
rather than computing it locally from the frame width and height.
> diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
> --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
> +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
[ ... ]
> @@ -1811,18 +1811,19 @@ static int vdec_av1_slice_setup_core_buffer(struct vdec_av1_slice_instance *inst
> {
[ ... ]
> /* frame buffer */
> vsi->fb.y.dma_addr = fb->base_y.dma_addr;
> +
> + vsi->fb.y.size = instance->ctx->picinfo.fb_sz[0];
> + vsi->fb.c.size = instance->ctx->picinfo.fb_sz[1];
> +
[Severity: Critical]
Does this introduce a race condition with concurrent requests?
The driver uses an asynchronous architecture where requests are processed
sequentially in the lat phase by mtk_vdec_worker, but core work is queued
and executed asynchronously.
If mtk_vdec_worker processes a new request, it synchronously updates the
global instance->ctx->picinfo to match the new sequence controls.
Concurrently, if the core worker processing the previous request executes
vdec_av1_slice_setup_core_buffer(), it will now read the mutated
instance->ctx->picinfo.fb_sz instead of the request-local dimension data.
> if (plane == 1)
> - vsi->fb.c.dma_addr = fb->base_y.dma_addr + size;
> + vsi->fb.c.dma_addr = fb->base_y.dma_addr + vsi->fb.y.size;
[Severity: Critical]
Can this regression lead to an out-of-bounds dma write?
If the subsequent request specifies a larger resolution, vsi->fb.y.size here
will be larger than the actually allocated vb2 capture buffer for the current
request.
Because this calculated c-plane dma address is not validated against the
physical capacity of the underlying vb2 buffer, the hardware could perform an
out-of-bounds write when it processes this offset. Userspace could potentially
trigger this by queuing sequential requests with different resolutions.
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1791619742.git.kyrie.wu@mediatek.com?part=13
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v8 14/14] media: mediatek: decoder: support av1 extend vsi
2026-10-10 8:35 [PATCH v8 00/14] media: mediatek: vcodec: support video decoder in mt8196 Kyrie Wu
` (12 preceding siblings ...)
2026-10-10 8:36 ` [PATCH v8 13/14] media: mediatek: decoder: fill av1 buffer size with picinfo Kyrie Wu
@ 2026-10-10 8:36 ` Kyrie Wu
2026-10-10 8:51 ` sashiko-bot
13 siblings, 1 reply; 24+ messages in thread
From: Kyrie Wu @ 2026-10-10 8:36 UTC (permalink / raw)
To: Tiffany Lin, Andrew-CT Chen, Yunfei Dong, Mauro Carvalho Chehab,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Kyrie Wu, Nicolas Dufresne,
Ricardo Ribalda, Kees Cook, Hans Verkuil, Fei Shao, Haoxiang Li,
Chen-Yu Tsai, Laurent Pinchart, Tomasz Figa, Philipp Zabel,
Benjamin Gaignard, Qianfeng Rong, Irui Wang, Jacopo Mondi, Fan Wu,
linux-media, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek
Cc: Sakari Ailus
The driver can't access tile buffer address for extend architecture,
set tile group information in vcp and share it with kernel.
Signed-off-by: Kyrie Wu <kyrie.wu@mediatek.com>
---
.../vcodec/decoder/vdec/vdec_av1_req_lat_if.c | 59 ++++++++++++++++---
1 file changed, 52 insertions(+), 7 deletions(-)
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
index 2d6e08e7..664adde3 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
@@ -25,6 +25,9 @@
#define AV1_INVALID_IDX -1
+#define AV1_NON_EXT_VSI_SIZE 0xD50
+#define AV1_TILE_SIZE 64
+
#define AV1_DIV_ROUND_UP_POW2(value, n) \
({ \
typeof(n) _n = n; \
@@ -641,6 +644,8 @@ struct vdec_av1_slice_fb {
* @frame: current frame info
* @state: status after decode done
* @cur_lst_tile_id: tile id for large scale
+ * @tile_group: tile group info
+ * @reserved: reserved
*/
struct vdec_av1_slice_vsi {
/* lat */
@@ -665,6 +670,8 @@ struct vdec_av1_slice_vsi {
struct vdec_av1_slice_frame frame;
struct vdec_av1_slice_state state;
u32 cur_lst_tile_id;
+ struct vdec_av1_slice_tile_group tile_group;
+ unsigned int reserved[4];
};
/**
@@ -1403,17 +1410,29 @@ static void vdec_av1_slice_setup_uh(struct vdec_av1_slice_instance *instance,
vdec_av1_slice_setup_tile(frame, &ctrl_fh->tile_info);
}
+static
+struct vdec_av1_slice_tile_group *vdec_av1_get_tile_group(struct vdec_av1_slice_instance *instance,
+ struct vdec_av1_slice_vsi *vsi)
+{
+ if (IS_VDEC_SUPPORT_EXT(instance->ctx->dev->dec_capability))
+ return &vsi->tile_group;
+ else
+ return &instance->tile_group;
+}
+
static int vdec_av1_slice_setup_tile_group(struct vdec_av1_slice_instance *instance,
struct vdec_av1_slice_vsi *vsi)
{
struct v4l2_ctrl_av1_tile_group_entry *ctrl_tge;
- struct vdec_av1_slice_tile_group *tile_group = &instance->tile_group;
+ struct vdec_av1_slice_tile_group *tile_group;
struct vdec_av1_slice_uncompressed_header *uh = &vsi->frame.uh;
struct vdec_av1_slice_tile *tile = &uh->tile;
struct v4l2_ctrl *ctrl;
u32 tge_size;
int i;
+ tile_group = vdec_av1_get_tile_group(instance, vsi);
+
ctrl = v4l2_ctrl_find(&instance->ctx->ctrl_hdl, V4L2_CID_STATELESS_AV1_TILE_GROUP_ENTRY);
if (!ctrl)
return -EINVAL;
@@ -1608,6 +1627,15 @@ static int vdec_av1_slice_setup_pfc(struct vdec_av1_slice_instance *instance,
return ret;
}
+static u32 vdec_av1_get_tiles_num(struct vdec_av1_slice_instance *instance,
+ struct vdec_av1_slice_vsi *vsi)
+{
+ if (IS_VDEC_SUPPORT_EXT(instance->ctx->dev->dec_capability))
+ return vsi->tile_group.num_tiles;
+ else
+ return instance->tile_group.num_tiles;
+}
+
static void vdec_av1_slice_setup_lat_buffer(struct vdec_av1_slice_instance *instance,
struct vdec_av1_slice_vsi *vsi,
struct mtk_vcodec_mem *bs,
@@ -1648,12 +1676,18 @@ static void vdec_av1_slice_setup_lat_buffer(struct vdec_av1_slice_instance *inst
vsi->tile.buf = instance->tile.dma_addr;
vsi->tile.size = instance->tile.size;
- memcpy(lat_buf->tile_addr.va, instance->tile.va, 64 * instance->tile_group.num_tiles);
vsi->cdf_table.buf = instance->cdf_table.dma_addr;
vsi->cdf_table.size = instance->cdf_table.size;
vsi->iq_table.buf = instance->iq_table.dma_addr;
vsi->iq_table.size = instance->iq_table.size;
+
+ /* lat_buf is used to share hardware decoder syntax between lat and core,
+ * there isn't only one. But there is only one tile.va for each instance.
+ * Need to copy tile information to lat_buf every time.
+ */
+ memcpy(lat_buf->tile_addr.va, instance->tile.va,
+ AV1_TILE_SIZE * vdec_av1_get_tiles_num(instance, vsi));
}
static void vdec_av1_slice_setup_seg_buffer(struct vdec_av1_slice_instance *instance,
@@ -1676,7 +1710,7 @@ static void vdec_av1_slice_setup_tile_buffer(struct vdec_av1_slice_instance *ins
struct vdec_av1_slice_vsi *vsi,
struct mtk_vcodec_mem *bs)
{
- struct vdec_av1_slice_tile_group *tile_group = &instance->tile_group;
+ struct vdec_av1_slice_tile_group *tile_group;
struct vdec_av1_slice_uncompressed_header *uh = &vsi->frame.uh;
struct vdec_av1_slice_tile *tile = &uh->tile;
u32 tile_num, tile_row, tile_col;
@@ -1687,6 +1721,8 @@ static void vdec_av1_slice_setup_tile_buffer(struct vdec_av1_slice_instance *ins
u32 *tile_info_buf = instance->tile.va;
u64 pa = (u64)bs->dma_addr;
+ tile_group = vdec_av1_get_tile_group(instance, vsi);
+
if (uh->disable_cdf_update == 0)
allow_update_cdf = 1;
@@ -1909,7 +1945,7 @@ static int vdec_av1_slice_init(struct mtk_vcodec_dec_ctx *ctx)
struct vdec_av1_slice_instance *instance;
struct vdec_av1_slice_init_vsi *vsi;
enum mtk_vcodec_fw_type fw_type = ctx->dev->fw_handler->type;
- int ret;
+ int ret, vsi_size = AV1_NON_EXT_VSI_SIZE;
instance = kzalloc_obj(*instance);
if (!instance)
@@ -1943,9 +1979,18 @@ static int vdec_av1_slice_init(struct mtk_vcodec_dec_ctx *ctx)
goto error_vsi;
}
- if (vsi->vsi_size != sizeof(struct vdec_av1_slice_vsi))
- mtk_vdec_err(ctx, "remote vsi size 0x%x mismatch! expected: 0x%zx\n",
- vsi->vsi_size, sizeof(struct vdec_av1_slice_vsi));
+ if (IS_VDEC_SUPPORT_EXT(ctx->dev->dec_capability)) {
+ vsi_size = sizeof(struct vdec_av1_slice_vsi);
+ vsi->iq_table_size = AV1_IQ_TABLE_SIZE;
+ vsi->cdf_table_size = AV1_CDF_SIZE;
+ }
+
+ if (vsi->vsi_size != vsi_size) {
+ mtk_vdec_err(ctx, "remote vsi size 0x%x mismatch! expected: 0x%x\n",
+ vsi->vsi_size, vsi_size);
+ ret = -EINVAL;
+ goto error_vsi;
+ }
instance->irq_enabled = 1;
instance->inneracing_mode = IS_VDEC_INNER_RACING(instance->ctx->dev->dec_capability);
^ permalink raw reply related [flat|nested] 24+ messages in thread* Re: [PATCH v8 14/14] media: mediatek: decoder: support av1 extend vsi
2026-10-10 8:36 ` [PATCH v8 14/14] media: mediatek: decoder: support av1 extend vsi Kyrie Wu
@ 2026-10-10 8:51 ` sashiko-bot
0 siblings, 0 replies; 24+ messages in thread
From: sashiko-bot @ 2026-10-10 8:51 UTC (permalink / raw)
To: Kyrie Wu; +Cc: media-ci, conor+dt, robh, devicetree
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] media: Out-of-bounds write to VPU firmware memory due to oversized memcpy
--
commit 482da53176ed6a6bd943ba24dd065fc679bf7158
Author: Kyrie Wu <kyrie.wu@mediatek.com>
media: mediatek: decoder: support av1 extend vsi
The patch expands the AV1 VSI state structure to include tile group
information to support the extended architecture. It also updates the
initialization routine to allow smaller shared memory buffers when
running on non-extended firmware.
> diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
> index 14639098fd463..1ef561dae18c7 100644
> --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
> +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
> @@ -665,6 +670,8 @@ struct vdec_av1_slice_vsi {
> struct vdec_av1_slice_frame frame;
> struct vdec_av1_slice_state state;
> u32 cur_lst_tile_id;
> + struct vdec_av1_slice_tile_group tile_group;
> + unsigned int reserved[4];
> };
[ ... ]
> @@ -1942,9 +1978,18 @@ static int vdec_av1_slice_init(struct mtk_vcodec_dec_ctx *ctx)
> goto error_vsi;
> }
>
> - if (vsi->vsi_size != sizeof(struct vdec_av1_slice_vsi))
> - mtk_vdec_err(ctx, "remote vsi size 0x%x mismatch! expected: 0x%zx\n",
> - vsi->vsi_size, sizeof(struct vdec_av1_slice_vsi));
> + if (IS_VDEC_SUPPORT_EXT(ctx->dev->dec_capability)) {
> + vsi_size = sizeof(struct vdec_av1_slice_vsi);
> + vsi->iq_table_size = AV1_IQ_TABLE_SIZE;
> + vsi->cdf_table_size = AV1_CDF_SIZE;
> + }
> +
> + if (vsi->vsi_size != vsi_size) {
> + mtk_vdec_err(ctx, "remote vsi size 0x%x mismatch! expected: 0x%x\n",
> + vsi->vsi_size, vsi_size);
> + ret = -EINVAL;
> + goto error_vsi;
> + }
[Severity: Critical]
This correctly enforces that older firmware provides a 0xD50-byte buffer for
the non-extended architecture.
However, since struct vdec_av1_slice_vsi was expanded with tile_group,
does vdec_av1_slice_vsi_to_remote() now copy too much data?
vdec_av1_slice_vsi_to_remote() {
memcpy(remote_vsi, vsi, sizeof(*vsi));
}
Since sizeof(*vsi) evaluates to the new, enlarged compile-time size of the
struct, will this unconditionally write ~7.5KB of data into the 0xD50
(3.4KB) firmware buffer when decoding on non-extended hardware?
Could this out-of-bounds write corrupt adjacent data in the VPU shared
memory or firmware SRAM?
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1791619742.git.kyrie.wu@mediatek.com?part=14
^ permalink raw reply [flat|nested] 24+ messages in thread