1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2022 MediaTek Corporation. All rights reserved. 4 * Author: Allen-KH Cheng <allen-kh.cheng@mediatek.com> 5 */ 6 7 #include <linux/firmware/mediatek/mtk-adsp-ipc.h> 8 #include <linux/kernel.h> 9 #include <linux/mailbox_client.h> 10 #include <linux/module.h> 11 #include <linux/of_platform.h> 12 #include <linux/platform_device.h> 13 #include <linux/slab.h> 14 15 static const char * const adsp_mbox_ch_names[MTK_ADSP_MBOX_NUM] = { "rx", "tx" }; 16 17 /* 18 * mtk_adsp_ipc_send - send ipc cmd to MTK ADSP 19 * 20 * @ipc: ADSP IPC handle 21 * @idx: index of the mailbox channel 22 * @msg: IPC cmd (reply or request) 23 * 24 * Returns zero for success from mbox_send_message 25 * negative value for error 26 */ 27 int mtk_adsp_ipc_send(struct mtk_adsp_ipc *ipc, unsigned int idx, uint32_t msg) 28 { 29 struct mtk_adsp_chan *adsp_chan; 30 int ret; 31 32 if (idx >= MTK_ADSP_MBOX_NUM) 33 return -EINVAL; 34 35 adsp_chan = &ipc->chans[idx]; 36 ret = mbox_send_message(adsp_chan->ch, &msg); 37 if (ret < 0) 38 return ret; 39 40 return 0; 41 } 42 EXPORT_SYMBOL_GPL(mtk_adsp_ipc_send); 43 44 /* 45 * mtk_adsp_ipc_recv - recv callback used by MTK ADSP mailbox 46 * 47 * @c: mbox client 48 * @msg: message received 49 * 50 * Users of ADSP IPC will need to privde handle_reply and handle_request 51 * callbacks. 52 */ 53 static void mtk_adsp_ipc_recv(struct mbox_client *c, void *msg) 54 { 55 struct mtk_adsp_chan *chan = container_of(c, struct mtk_adsp_chan, cl); 56 struct device *dev = c->dev; 57 58 switch (chan->idx) { 59 case MTK_ADSP_MBOX_REPLY: 60 chan->ipc->ops->handle_reply(chan->ipc); 61 break; 62 case MTK_ADSP_MBOX_REQUEST: 63 chan->ipc->ops->handle_request(chan->ipc); 64 break; 65 default: 66 dev_err(dev, "wrong mbox chan %d\n", chan->idx); 67 break; 68 } 69 } 70 71 static int mtk_adsp_ipc_probe(struct platform_device *pdev) 72 { 73 struct device *dev = &pdev->dev; 74 struct mtk_adsp_ipc *adsp_ipc; 75 struct mtk_adsp_chan *adsp_chan; 76 struct mbox_client *cl; 77 int ret; 78 int i, j; 79 80 device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent); 81 82 adsp_ipc = devm_kzalloc(dev, sizeof(*adsp_ipc), GFP_KERNEL); 83 if (!adsp_ipc) 84 return -ENOMEM; 85 86 for (i = 0; i < MTK_ADSP_MBOX_NUM; i++) { 87 adsp_chan = &adsp_ipc->chans[i]; 88 cl = &adsp_chan->cl; 89 cl->dev = dev->parent; 90 cl->tx_block = false; 91 cl->knows_txdone = false; 92 cl->tx_prepare = NULL; 93 cl->rx_callback = mtk_adsp_ipc_recv; 94 95 adsp_chan->ipc = adsp_ipc; 96 adsp_chan->idx = i; 97 adsp_chan->ch = mbox_request_channel_byname(cl, adsp_mbox_ch_names[i]); 98 if (IS_ERR(adsp_chan->ch)) { 99 ret = PTR_ERR(adsp_chan->ch); 100 if (ret != -EPROBE_DEFER) 101 dev_err(dev, "Failed to request mbox chan %s ret %d\n", 102 adsp_mbox_ch_names[i], ret); 103 104 for (j = 0; j < i; j++) { 105 adsp_chan = &adsp_ipc->chans[j]; 106 mbox_free_channel(adsp_chan->ch); 107 } 108 109 return ret; 110 } 111 } 112 113 adsp_ipc->dev = dev; 114 dev_set_drvdata(dev, adsp_ipc); 115 dev_dbg(dev, "MTK ADSP IPC initialized\n"); 116 117 return 0; 118 } 119 120 static int mtk_adsp_ipc_remove(struct platform_device *pdev) 121 { 122 struct mtk_adsp_ipc *adsp_ipc = dev_get_drvdata(&pdev->dev); 123 struct mtk_adsp_chan *adsp_chan; 124 int i; 125 126 for (i = 0; i < MTK_ADSP_MBOX_NUM; i++) { 127 adsp_chan = &adsp_ipc->chans[i]; 128 mbox_free_channel(adsp_chan->ch); 129 } 130 131 return 0; 132 } 133 134 static struct platform_driver mtk_adsp_ipc_driver = { 135 .driver = { 136 .name = "mtk-adsp-ipc", 137 }, 138 .probe = mtk_adsp_ipc_probe, 139 .remove = mtk_adsp_ipc_remove, 140 }; 141 builtin_platform_driver(mtk_adsp_ipc_driver); 142 143 MODULE_AUTHOR("Allen-KH Cheng <allen-kh.cheng@mediatek.com>"); 144 MODULE_DESCRIPTION("MTK ADSP IPC Driver"); 145 MODULE_LICENSE("GPL"); 146