public inbox for linux-arm-kernel@lists.infradead.org
 help / color / mirror / Atom feed
From: Nicolas Dufresne <nicolas.dufresne@collabora.com>
To: "Yunfei Dong" <yunfei.dong@mediatek.com>,
	"Nícolas F . R . A . Prado" <nfraprado@collabora.com>,
	"Sebastian Fricke" <sebastian.fricke@collabora.com>,
	"Hans Verkuil" <hverkuil-cisco@xs4all.nl>,
	"AngeloGioacchino Del Regno"
	<angelogioacchino.delregno@collabora.com>,
	"Benjamin Gaignard" <benjamin.gaignard@collabora.com>,
	"Nathan Hebert" <nhebert@chromium.org>,
	"Daniel Almeida" <daniel.almeida@collabora.com>
Cc: Hsin-Yi Wang <hsinyi@chromium.org>,
	Fritz Koenig <frkoenig@chromium.org>,
	 Daniel Vetter <daniel@ffwll.ch>,
	Steve Cho <stevecho@chromium.org>,
	linux-media@vger.kernel.org,  devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org,
	 linux-arm-kernel@lists.infradead.org,
	linux-mediatek@lists.infradead.org,
	 Project_Global_Chrome_Upstream_Group@mediatek.com
Subject: Re: [PATCH v4 01/14] media: mediatek: vcodec: add driver to support vcp
Date: Tue, 28 Apr 2026 16:29:20 -0400	[thread overview]
Message-ID: <466588ec8c4c3e62adb62ec7e1ddcd760cceb89e.camel@collabora.com> (raw)
In-Reply-To: <20260328051630.7937-2-yunfei.dong@mediatek.com>

[-- Attachment #1: Type: text/plain, Size: 26306 bytes --]

Le samedi 28 mars 2026 à 13:16 +0800, Yunfei Dong a écrit :
> 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: Yunfei Dong <yunfei.dong@mediatek.com>
> ---
>  .../media/platform/mediatek/vcodec/Kconfig    |   4 +
>  .../platform/mediatek/vcodec/common/Makefile  |   4 +
>  .../mediatek/vcodec/common/mtk_vcodec_fw.c    |   3 +
>  .../mediatek/vcodec/common/mtk_vcodec_fw.h    |   1 +
>  .../vcodec/common/mtk_vcodec_fw_priv.h        |  12 +
>  .../vcodec/common/mtk_vcodec_fw_vcp.c         | 505 ++++++++++++++++++
>  .../vcodec/common/mtk_vcodec_fw_vcp.h         | 139 +++++
>  7 files changed, 668 insertions(+)
>  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 bc8292232530..d23dad5c78ce 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 d0479914dfb3..2f68692e8c98 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 08949b08fbc6..fc547afa4ebf 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,7 @@
>  #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"
>  
>  struct mtk_vcodec_fw *mtk_vcodec_fw_select(void *priv, enum mtk_vcodec_fw_type type,
>  					   enum mtk_vcodec_fw_use fw_use)
> @@ -19,6 +20,8 @@ struct mtk_vcodec_fw *mtk_vcodec_fw_select(void *priv, enum mtk_vcodec_fw_type t
>  		return mtk_vcodec_fw_vpu_init(priv, fw_use);
>  	case SCP:
>  		return mtk_vcodec_fw_scp_init(priv, fw_use);
> +	case VCP:
> +		return mtk_vcodec_fw_vcp_init(priv, fw_use);

This applies to the entire series. Do you have rational toward using the switch
and if/else approach over the entire series ? The ops pattern is much more
command for multi-platform driver. Using this as an example, you'd have a
vpu_init ops, in your pdata, and simply call this without the runtime switches
and conditions.

Please apply changes according to this comment for the entire series, keeping
mtk_vcodec_enc_drv.h a free from VCP/SCP specific code as possible.

Nicolas

>  	default:
>  		dev_err(&plat_dev->dev, "Invalid vcodec fw type");
>  		return ERR_PTR(-EINVAL);
> 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 300363a40158..c1642fb09b42 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/common/mtk_vcodec_fw_priv.h b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_priv.h
> index 99603accd82e..0a2a9b010244 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_vcp.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c
> new file mode 100644
> index 000000000000..9fee52fed181
> --- /dev/null
> +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c
> @@ -0,0 +1,505 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2025 MediaTek Inc.
> + * Author: Yunfei Dong <yunfei.dong@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 struct mutex *mtk_vcodec_vcp_get_ipi_lock(struct mtk_vcp *vcp, u32 ipi_id)
> +{
> +	return &vcp->ipi_desc[ipi_id].lock;
> +}
> +
> +static void mtk_vcodec_vcp_ipi_lock(struct mtk_vcp *vcp, u32 ipi_id)
> +{
> +	struct mutex *lock = mtk_vcodec_vcp_get_ipi_lock(vcp, ipi_id);
> +
> +	if (!lock)
> +		return;
> +
> +	mutex_lock(lock);
> +}
> +
> +static void mtk_vcodec_vcp_ipi_unlock(struct mtk_vcp *vcp, u32 ipi_id)
> +{
> +	struct mutex *lock = mtk_vcodec_vcp_get_ipi_lock(vcp, ipi_id);
> +
> +	if (!lock)
> +		return;
> +
> +	lockdep_assert_held(lock);
> +	mutex_unlock(lock);
> +}
> +
> +static spinlock_t *mtk_vcodec_vcp_get_msg_queue_lock(struct mtk_vcodec_fw *fw)
> +{
> +	return &fw->vcp->msg_queue.lock;
> +}
> +
> +static void mtk_vcodec_vcp_msq_queue_lock(struct mtk_vcodec_fw *fw, unsigned long *flags)
> +{
> +	spinlock_t *lock = mtk_vcodec_vcp_get_msg_queue_lock(fw);
> +
> +	if (!lock)
> +		return;
> +
> +	spin_lock_irqsave(lock, *flags);
> +}
> +
> +static void mtk_vcodec_vcp_msq_queue_unlock(struct mtk_vcodec_fw *fw, unsigned long *flags)
> +{
> +	spinlock_t *lock = mtk_vcodec_vcp_get_msg_queue_lock(fw);
> +
> +	if (!lock)
> +		return;
> +
> +	spin_unlock_irqrestore(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(ipi_id >= VCP_IPI_MAX) || 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);
> +		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;
> +		}
> +
> +		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);
> +
> +		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);
> +			return -EINVAL;
> +		}
> +
> +		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;
> +
> +	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 = &vcp->msg_signaled[id];
> +	wait_queue_head_t *msg_wq = &vcp->msg_wq[id];
> +	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 (!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(feature_id)) {
> +		if (time_after(jiffies, timeout_jiffies)) {
> +			vcp->ipi_id_ack[id] = -EINVAL;
> +			ret = -EINVAL;
> +			goto error;
> +		}
> +		mdelay(1);
> +	}
> +
> +	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]);
> +
> +	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_timeout(*msg_wq, *msg_signaled, msecs_to_jiffies(IPI_TIMEOUT_MS));
> +	if (!ret || retry_cnt > 5) {
> +		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) {
> +		dev_err(&fw->pdev->dev, "wait ipi ack err (%d)\n", vcp->ipi_id_ack[id]);
> +		retry_cnt++;
> +		goto wait_ack;
> +	} 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]);
> +
> +	*msg_signaled = false;
> +	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 int check_vcp_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_err(dev, "find mtk-vcp driver failed, need to reload.");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +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(check_vcp_loaded(fw), "mtk-vcp")) {
> +		if (++retry > retry_cnt) {
> +			dev_err(dev, "failed to load mtk-vcp module");
> +			return -EPROBE_DEFER;
> +		}
> +		msleep(1);
> +	}
> +
> +	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 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;
> +
> +	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);
> +		return -EINVAL;
> +	}
> +
> +	fw->vcp->vcp_notify.notifier_call = mtk_vcodec_vcp_notifier;
> +	fw->vcp->vcp_notify.priority = 1;
> +	vcp_device->ops->vcp_register_notify(feature_id, &fw->vcp->vcp_notify);
> +
> +	if (!fw->vcp->is_register_done) {
> +		ret = vcp_device->ops->vcp_register_feature(vcp_device, feature_id);
> +		if (ret < 0) {
> +			dev_err(&fw->pdev->dev, "%d register to vcp fail(%d)\n", feature_id, ret);
> +			return -EINVAL;
> +		}
> +
> +		fw->vcp->is_register_done = true;
> +	}
> +
> +	fw->vcp->is_init_done = true;
> +
> +	mutex_init(&fw->vcp->ipi_desc[ipi_id].lock);
> +	mutex_init(&fw->vcp->ipi_mutex);
> +
> +	kthread_run(mtk_vcodec_vcp_msg_process_thread, fw, "vcp_vdec_msq_thread");
> +
> +	fw->vcp->vsi_addr = vcp_device->ops->vcp_get_mem_virt(mem_id);
> +	fw->vcp->vsi_core_addr = fw->vcp->vsi_addr + VCODEC_VSI_LEN;
> +	fw->vcp->vsi_size = vcp_device->ops->vcp_get_mem_size(mem_id);
> +	fw->vcp->iova_addr = vcp_device->ops->vcp_get_mem_iova(mem_id);
> +
> +	init_waitqueue_head(&fw->vcp->msg_wq[VCP_IPI_LAT_DECODER]);
> +	init_waitqueue_head(&fw->vcp->msg_wq[VCP_IPI_CORE_DECODER]);
> +
> +	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;
> +}
> +
> +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, feature_id;
> +
> +	if (!fw->vcp->vcp_device) {
> +		dev_err(dev, "vcp device is null\n");
> +		return;
> +	}
> +
> +	if (!fw->vcp->is_register_done)
> +		return;
> +
> +	feature_id = VDEC_FEATURE_ID;
> +	ret = vcp_device->ops->vcp_deregister_feature(vcp_device, VDEC_FEATURE_ID);
> +	if (ret < 0) {
> +		dev_err(dev, "deregister feature_id(%d) fail(%d)\n", feature_id, ret);
> +		return;
> +	}
> +
> +	fw->vcp->is_register_done = false;
> +
> +}
> +
> +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;
> +}
> 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 000000000000..fada786124d5
> --- /dev/null
> +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.h
> @@ -0,0 +1,139 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2025 MediaTek Inc.
> + * Author: Yunfei Dong <yunfei.dong@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;
> +	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 {
> +	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
> + *
> + * @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
> + * @is_register_done: register vcodec to vcp
> + */
> +struct mtk_vcp {
> +	bool is_init_done;
> +
> +	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];
> +	bool 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;
> +
> +	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;
> +	bool is_register_done;
> +};
> +
> +#endif

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

  reply	other threads:[~2026-04-28 20:29 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-28  5:16 [PATCH v4 00/14] media: mediatek: vcodec: support video decoder in mt8196 Yunfei Dong
2026-03-28  5:16 ` [PATCH v4 01/14] media: mediatek: vcodec: add driver to support vcp Yunfei Dong
2026-04-28 20:29   ` Nicolas Dufresne [this message]
2026-03-28  5:16 ` [PATCH v4 02/14] media: mediatek: vcodec: add driver to support vcp encoder Yunfei Dong
2026-03-28  5:16 ` [PATCH v4 03/14] media: mediatek: vcodec: get different firmware ipi id Yunfei Dong
2026-03-28  5:16 ` [PATCH v4 04/14] media: mediatek: vcodec: get share memory address Yunfei Dong
2026-03-28  5:16 ` [PATCH v4 05/14] media: mediatek: vcodec: define MT8196 vcodec levels Yunfei Dong
2026-03-28  5:16 ` [PATCH v4 06/14] media: mediatek: vcodec: support vcp architecture Yunfei Dong
2026-03-28  5:16 ` [PATCH v4 07/14] media: mediatek: vcodec: support 36bit iova address Yunfei Dong
2026-03-28  5:16 ` [PATCH v4 08/14] media: mediatek: vcodec: clean xpc status Yunfei Dong
2026-03-28  5:16 ` [PATCH v4 09/14] media: mediatek: vcodec: add debug information Yunfei Dong
2026-03-28  5:16 ` [PATCH v4 10/14] media: mediatek: vcodec: send share memory address to vcp Yunfei Dong
2026-03-28  5:16 ` [PATCH v4 11/14] dt-bindings: media: mediatek: vcodec: add decoder dt-bindings for mt8196 Yunfei Dong
2026-03-28 12:22   ` Krzysztof Kozlowski
2026-03-28  5:16 ` [PATCH v4 12/14] media: mediatek: vcodec: add decoder compatible to support mt8196 Yunfei Dong
2026-03-28  5:16 ` [PATCH v4 13/14] media: mediatek: decoder: fill av1 buffer size with picinfo Yunfei Dong
2026-03-28  5:16 ` [PATCH v4 14/14] media: mediatek: decoder: support av1 extend vsi Yunfei Dong
2026-04-28 20:24 ` [PATCH v4 00/14] media: mediatek: vcodec: support video decoder in mt8196 Nicolas Dufresne

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=466588ec8c4c3e62adb62ec7e1ddcd760cceb89e.camel@collabora.com \
    --to=nicolas.dufresne@collabora.com \
    --cc=Project_Global_Chrome_Upstream_Group@mediatek.com \
    --cc=angelogioacchino.delregno@collabora.com \
    --cc=benjamin.gaignard@collabora.com \
    --cc=daniel.almeida@collabora.com \
    --cc=daniel@ffwll.ch \
    --cc=devicetree@vger.kernel.org \
    --cc=frkoenig@chromium.org \
    --cc=hsinyi@chromium.org \
    --cc=hverkuil-cisco@xs4all.nl \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-media@vger.kernel.org \
    --cc=linux-mediatek@lists.infradead.org \
    --cc=nfraprado@collabora.com \
    --cc=nhebert@chromium.org \
    --cc=sebastian.fricke@collabora.com \
    --cc=stevecho@chromium.org \
    --cc=yunfei.dong@mediatek.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox