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