mailbox: mediatek: Add mtk-vcp-mailbox driver

Add mtk-vcp-mailbox driver to support the communication with
VCP remote microprocessor.

Signed-off-by: Jjian Zhou <jjian.zhou@mediatek.com>
Reviewed-by: Chen-Yu Tsai <wenst@chromium.org>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: Jassi Brar <jassisinghbrar@gmail.com>
This commit is contained in:
Jjian Zhou 2025-10-13 14:31:36 +08:00 committed by Jassi Brar
parent c2b75a5353
commit b562abd956
4 changed files with 213 additions and 0 deletions

View file

@ -304,6 +304,15 @@ config MTK_GPUEB_MBOX
Say Y or m here if you want to support the MT8196 SoC in your kernel
build.
config MTK_VCP_MBOX
tristate "MediaTek VCP Mailbox Support"
depends on ARCH_MEDIATEK || COMPILE_TEST
help
Say yes here to add support for the MediaTek VCP mailbox driver.
The mailbox implementation provides access from the application
processor to Video Companion Processor Unit.
If unsure say N.
config ZYNQMP_IPI_MBOX
tristate "Xilinx ZynqMP IPI Mailbox"
depends on ARCH_ZYNQMP && OF

View file

@ -65,6 +65,8 @@ obj-$(CONFIG_MTK_CMDQ_MBOX) += mtk-cmdq-mailbox.o
obj-$(CONFIG_MTK_GPUEB_MBOX) += mtk-gpueb-mailbox.o
obj-$(CONFIG_MTK_VCP_MBOX) += mtk-vcp-mailbox.o
obj-$(CONFIG_ZYNQMP_IPI_MBOX) += zynqmp-ipi-mailbox.o
obj-$(CONFIG_SUN6I_MSGBOX) += sun6i-msgbox.o

View file

@ -0,0 +1,170 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2025 MediaTek Corporation. All rights reserved.
* Author: Jjian Zhou <jjian.zhou.@mediatek.com>
*/
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/mailbox_controller.h>
#include <linux/mailbox/mtk-vcp-mailbox.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
struct mtk_vcp_mbox {
struct mbox_controller mbox;
void __iomem *base;
struct device *dev;
const struct mtk_vcp_mbox_cfg *cfg;
struct mtk_ipi_info ipi_recv;
struct mbox_chan chans;
};
struct mtk_vcp_mbox_cfg {
u16 set_in;
u16 clr_out;
};
static irqreturn_t mtk_vcp_mbox_irq_thread(int irq, void *data)
{
struct mtk_vcp_mbox *priv = data;
/* get irq status */
priv->ipi_recv.irq_status = readl(priv->base + priv->cfg->clr_out);
__ioread32_copy(priv->ipi_recv.msg, priv->base,
MTK_VCP_MBOX_SLOT_MAX_SIZE / 4);
mbox_chan_received_data(&priv->chans, &priv->ipi_recv);
/* clear irq status */
writel(priv->ipi_recv.irq_status, priv->base + priv->cfg->clr_out);
return IRQ_HANDLED;
}
static struct mbox_chan *mtk_vcp_mbox_xlate(struct mbox_controller *mbox,
const struct of_phandle_args *sp)
{
if (sp->args_count)
return NULL;
return &mbox->chans[0];
}
static int mtk_vcp_mbox_send_data(struct mbox_chan *chan, void *data)
{
struct mtk_vcp_mbox *priv = chan->con_priv;
struct mtk_ipi_info *ipi_info = data;
u32 status;
if (!ipi_info->msg) {
dev_err(priv->dev, "msg buffer is NULL.\n");
return -EINVAL;
}
status = readl(priv->base + priv->cfg->set_in);
if (status & BIT(ipi_info->index)) {
dev_warn(priv->dev, "mailbox IPI %d is busy.\n", ipi_info->id);
return -EBUSY;
}
if (ipi_info->slot_ofs + ipi_info->len > MTK_VCP_MBOX_SLOT_MAX_SIZE)
return -EINVAL;
__iowrite32_copy(priv->base + ipi_info->slot_ofs, ipi_info->msg,
ipi_info->len);
writel(BIT(ipi_info->index), priv->base + priv->cfg->set_in);
return 0;
}
static bool mtk_vcp_mbox_last_tx_done(struct mbox_chan *chan)
{
struct mtk_ipi_info *ipi_info = chan->active_req;
struct mtk_vcp_mbox *priv = chan->con_priv;
return !(readl(priv->base + priv->cfg->set_in) & BIT(ipi_info->index));
}
static const struct mbox_chan_ops mtk_vcp_mbox_chan_ops = {
.send_data = mtk_vcp_mbox_send_data,
.last_tx_done = mtk_vcp_mbox_last_tx_done,
};
static int mtk_vcp_mbox_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mtk_vcp_mbox *priv;
struct mbox_controller *mbox;
int ret, irq;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = dev;
priv->chans.con_priv = priv;
mbox = &priv->mbox;
mbox->dev = dev;
mbox->ops = &mtk_vcp_mbox_chan_ops;
mbox->txdone_irq = false;
mbox->txdone_poll = true;
mbox->of_xlate = mtk_vcp_mbox_xlate;
mbox->num_chans = 1;
mbox->chans = &priv->chans;
priv->ipi_recv.msg = devm_kzalloc(dev, MTK_VCP_MBOX_SLOT_MAX_SIZE,
GFP_KERNEL);
if (!priv->ipi_recv.msg)
return -ENOMEM;
priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
priv->cfg = of_device_get_match_data(dev);
if (!priv->cfg)
return -EINVAL;
platform_set_drvdata(pdev, priv);
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
ret = devm_request_threaded_irq(dev, irq, NULL,
mtk_vcp_mbox_irq_thread, IRQF_ONESHOT,
dev_name(dev), priv);
if (ret < 0)
return ret;
return devm_mbox_controller_register(dev, &priv->mbox);
}
static const struct mtk_vcp_mbox_cfg mt8196_cfg = {
.set_in = 0x100,
.clr_out = 0x10c,
};
static const struct of_device_id mtk_vcp_mbox_of_match[] = {
{ .compatible = "mediatek,mt8196-vcp-mbox", .data = &mt8196_cfg },
{},
};
MODULE_DEVICE_TABLE(of, mtk_vcp_mbox_of_match);
static struct platform_driver mtk_vcp_mbox_driver = {
.probe = mtk_vcp_mbox_probe,
.driver = {
.name = "mtk_vcp_mbox",
.of_match_table = mtk_vcp_mbox_of_match,
},
};
module_platform_driver(mtk_vcp_mbox_driver);
MODULE_AUTHOR("Jjian Zhou <jjian.zhou@mediatek.com>");
MODULE_DESCRIPTION("MTK VCP Mailbox Controller");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,32 @@
/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
/*
* Copyright (c) 2025 MediaTek Inc.
*/
#ifndef __MTK_VCP_MAILBOX_H__
#define __MTK_VCP_MAILBOX_H__
#define MTK_VCP_MBOX_SLOT_MAX_SIZE 0x100 /* mbox max slot size */
/**
* struct mtk_ipi_info - mailbox message info for mtk-vcp-mailbox
* @msg: The share buffer between IPC and mailbox driver
* @len: Message length
* @id: This is for identification purposes and not actually used
* by the mailbox hardware.
* @index: The signal number of the mailbox message.
* @slot_ofs: Data slot offset.
* @irq_status: Captures incoming signals for the RX path.
*
* It is used between IPC with mailbox driver.
*/
struct mtk_ipi_info {
void *msg;
u32 len;
u32 id;
u32 index;
u32 slot_ofs;
u32 irq_status;
};
#endif