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