firmware: mediatek: Add adsp ipc protocol interface
authorTingHan Shen <tinghan.shen@mediatek.com>
Thu, 12 May 2022 08:22:13 +0000 (16:22 +0800)
committerMark Brown <broonie@kernel.org>
Mon, 16 May 2022 11:58:11 +0000 (12:58 +0100)
Some of mediatek processors contain
the Tensilica HiFix DSP for audio processing.

The communication between Host CPU and DSP firmware is
taking place using a shared memory area for message passing.

ADSP IPC protocol offers (send/recv) interfaces using
mediatek-mailbox APIs.

We use two mbox channels to implement a request-reply protocol.

Signed-off-by: Allen-KH Cheng <allen-kh.cheng@mediatek.com>
Signed-off-by: TingHan Shen <tinghan.shen@mediatek.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: Curtis Malainey <cujomalainey@chromium.org>
Reviewed-by: Tzung-Bi Shih <tzungbi@google.com>
Reviewed-by: YC Hung <yc.hung@mediatek.com>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Link: https://lore.kernel.org/r/20220512082215.3018-2-tinghan.shen@mediatek.com
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/firmware/Kconfig
drivers/firmware/Makefile
drivers/firmware/mtk-adsp-ipc.c [new file with mode: 0644]
include/linux/firmware/mediatek/mtk-adsp-ipc.h [new file with mode: 0644]

index e5cfb01..f7bc830 100644 (file)
@@ -203,6 +203,15 @@ config INTEL_STRATIX10_RSU
 
          Say Y here if you want Intel RSU support.
 
+config MTK_ADSP_IPC
+       tristate "MTK ADSP IPC Protocol driver"
+       depends on MTK_ADSP_MBOX
+       help
+         Say yes here to add support for the MediaTek ADSP IPC
+         between host AP (Linux) and the firmware running on ADSP.
+         ADSP exists on some mtk processors.
+         Client might use shared memory to exchange information with ADSP.
+
 config QCOM_SCM
        tristate
 
index 4e58cb4..1be0e82 100644 (file)
@@ -15,6 +15,7 @@ obj-$(CONFIG_INTEL_STRATIX10_RSU)     += stratix10-rsu.o
 obj-$(CONFIG_ISCSI_IBFT_FIND)  += iscsi_ibft_find.o
 obj-$(CONFIG_ISCSI_IBFT)       += iscsi_ibft.o
 obj-$(CONFIG_FIRMWARE_MEMMAP)  += memmap.o
+obj-$(CONFIG_MTK_ADSP_IPC)     += mtk-adsp-ipc.o
 obj-$(CONFIG_RASPBERRYPI_FIRMWARE) += raspberrypi.o
 obj-$(CONFIG_FW_CFG_SYSFS)     += qemu_fw_cfg.o
 obj-$(CONFIG_QCOM_SCM)         += qcom-scm.o
diff --git a/drivers/firmware/mtk-adsp-ipc.c b/drivers/firmware/mtk-adsp-ipc.c
new file mode 100644 (file)
index 0000000..cb255a9
--- /dev/null
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 MediaTek Corporation. All rights reserved.
+ * Author: Allen-KH Cheng <allen-kh.cheng@mediatek.com>
+ */
+
+#include <linux/firmware/mediatek/mtk-adsp-ipc.h>
+#include <linux/kernel.h>
+#include <linux/mailbox_client.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+/*
+ * mtk_adsp_ipc_send - send ipc cmd to MTK ADSP
+ *
+ * @ipc: ADSP IPC handle
+ * @idx: index of the mailbox channel
+ * @msg: IPC cmd (reply or request)
+ *
+ * Returns zero for success from mbox_send_message
+ * negative value for error
+ */
+int mtk_adsp_ipc_send(struct mtk_adsp_ipc *ipc, unsigned int idx, uint32_t msg)
+{
+       struct mtk_adsp_chan *adsp_chan;
+       int ret;
+
+       if (idx >= MTK_ADSP_MBOX_NUM)
+               return -EINVAL;
+
+       adsp_chan = &ipc->chans[idx];
+       ret = mbox_send_message(adsp_chan->ch, &msg);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_adsp_ipc_send);
+
+/*
+ * mtk_adsp_ipc_recv - recv callback used by MTK ADSP mailbox
+ *
+ * @c: mbox client
+ * @msg: message received
+ *
+ * Users of ADSP IPC will need to privde handle_reply and handle_request
+ * callbacks.
+ */
+static void mtk_adsp_ipc_recv(struct mbox_client *c, void *msg)
+{
+       struct mtk_adsp_chan *chan = container_of(c, struct mtk_adsp_chan, cl);
+       struct device *dev = c->dev;
+
+       switch (chan->idx) {
+       case MTK_ADSP_MBOX_REPLY:
+               chan->ipc->ops->handle_reply(chan->ipc);
+               break;
+       case MTK_ADSP_MBOX_REQUEST:
+               chan->ipc->ops->handle_request(chan->ipc);
+               break;
+       default:
+               dev_err(dev, "wrong mbox chan %d\n", chan->idx);
+               break;
+       }
+}
+
+static int mtk_adsp_ipc_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct mtk_adsp_ipc *adsp_ipc;
+       struct mtk_adsp_chan *adsp_chan;
+       struct mbox_client *cl;
+       char *chan_name;
+       int ret;
+       int i, j;
+
+       device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent);
+
+       adsp_ipc = devm_kzalloc(dev, sizeof(*adsp_ipc), GFP_KERNEL);
+       if (!adsp_ipc)
+               return -ENOMEM;
+
+       for (i = 0; i < MTK_ADSP_MBOX_NUM; i++) {
+               chan_name = kasprintf(GFP_KERNEL, "mbox%d", i);
+               if (!chan_name) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+
+               adsp_chan = &adsp_ipc->chans[i];
+               cl = &adsp_chan->cl;
+               cl->dev = dev->parent;
+               cl->tx_block = false;
+               cl->knows_txdone = false;
+               cl->tx_prepare = NULL;
+               cl->rx_callback = mtk_adsp_ipc_recv;
+
+               adsp_chan->ipc = adsp_ipc;
+               adsp_chan->idx = i;
+               adsp_chan->ch = mbox_request_channel_byname(cl, chan_name);
+               if (IS_ERR(adsp_chan->ch)) {
+                       ret = PTR_ERR(adsp_chan->ch);
+                       if (ret != -EPROBE_DEFER)
+                               dev_err(dev, "Failed to request mbox chan %d ret %d\n",
+                                       i, ret);
+                       goto out_free;
+               }
+
+               dev_dbg(dev, "request mbox chan %s\n", chan_name);
+               kfree(chan_name);
+       }
+
+       adsp_ipc->dev = dev;
+       dev_set_drvdata(dev, adsp_ipc);
+       dev_dbg(dev, "MTK ADSP IPC initialized\n");
+
+       return 0;
+
+out_free:
+       kfree(chan_name);
+out:
+       for (j = 0; j < i; j++) {
+               adsp_chan = &adsp_ipc->chans[j];
+               mbox_free_channel(adsp_chan->ch);
+       }
+
+       return ret;
+}
+
+static int mtk_adsp_ipc_remove(struct platform_device *pdev)
+{
+       struct mtk_adsp_ipc *adsp_ipc = dev_get_drvdata(&pdev->dev);
+       struct mtk_adsp_chan *adsp_chan;
+       int i;
+
+       for (i = 0; i < MTK_ADSP_MBOX_NUM; i++) {
+               adsp_chan = &adsp_ipc->chans[i];
+               mbox_free_channel(adsp_chan->ch);
+       }
+
+       return 0;
+}
+
+static struct platform_driver mtk_adsp_ipc_driver = {
+       .driver = {
+               .name = "mtk-adsp-ipc",
+       },
+       .probe = mtk_adsp_ipc_probe,
+       .remove = mtk_adsp_ipc_remove,
+};
+builtin_platform_driver(mtk_adsp_ipc_driver);
+
+MODULE_AUTHOR("Allen-KH Cheng <allen-kh.cheng@mediatek.com>");
+MODULE_DESCRIPTION("MTK ADSP IPC Driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/firmware/mediatek/mtk-adsp-ipc.h b/include/linux/firmware/mediatek/mtk-adsp-ipc.h
new file mode 100644 (file)
index 0000000..28fd313
--- /dev/null
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ */
+
+#ifndef MTK_ADSP_IPC_H
+#define MTK_ADSP_IPC_H
+
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/mailbox_controller.h>
+#include <linux/mailbox_client.h>
+
+#define MTK_ADSP_IPC_REQ 0
+#define MTK_ADSP_IPC_RSP 1
+#define MTK_ADSP_IPC_OP_REQ 0x1
+#define MTK_ADSP_IPC_OP_RSP 0x2
+
+enum {
+       MTK_ADSP_MBOX_REPLY,
+       MTK_ADSP_MBOX_REQUEST,
+       MTK_ADSP_MBOX_NUM,
+};
+
+struct mtk_adsp_ipc;
+
+struct mtk_adsp_ipc_ops {
+       void (*handle_reply)(struct mtk_adsp_ipc *ipc);
+       void (*handle_request)(struct mtk_adsp_ipc *ipc);
+};
+
+struct mtk_adsp_chan {
+       struct mtk_adsp_ipc *ipc;
+       struct mbox_client cl;
+       struct mbox_chan *ch;
+       char *name;
+       int idx;
+};
+
+struct mtk_adsp_ipc {
+       struct mtk_adsp_chan chans[MTK_ADSP_MBOX_NUM];
+       struct device *dev;
+       struct mtk_adsp_ipc_ops *ops;
+       void *private_data;
+};
+
+static inline void mtk_adsp_ipc_set_data(struct mtk_adsp_ipc *ipc, void *data)
+{
+       if (!ipc)
+               return;
+
+       ipc->private_data = data;
+}
+
+static inline void *mtk_adsp_ipc_get_data(struct mtk_adsp_ipc *ipc)
+{
+       if (!ipc)
+               return NULL;
+
+       return ipc->private_data;
+}
+
+int mtk_adsp_ipc_send(struct mtk_adsp_ipc *ipc, unsigned int idx, uint32_t op);
+
+#endif /* MTK_ADSP_IPC_H */