19db69df4STingHan Shen // SPDX-License-Identifier: GPL-2.0 29db69df4STingHan Shen /* 39db69df4STingHan Shen * Copyright (c) 2022 MediaTek Corporation. All rights reserved. 49db69df4STingHan Shen * Author: Allen-KH Cheng <allen-kh.cheng@mediatek.com> 59db69df4STingHan Shen */ 69db69df4STingHan Shen 79db69df4STingHan Shen #include <linux/firmware/mediatek/mtk-adsp-ipc.h> 89db69df4STingHan Shen #include <linux/kernel.h> 99db69df4STingHan Shen #include <linux/mailbox_client.h> 109db69df4STingHan Shen #include <linux/module.h> 119db69df4STingHan Shen #include <linux/of_platform.h> 129db69df4STingHan Shen #include <linux/platform_device.h> 139db69df4STingHan Shen #include <linux/slab.h> 149db69df4STingHan Shen 15*74bbdd63STinghan Shen static const char * const adsp_mbox_ch_names[MTK_ADSP_MBOX_NUM] = { "rx", "tx" }; 16*74bbdd63STinghan Shen 179db69df4STingHan Shen /* 189db69df4STingHan Shen * mtk_adsp_ipc_send - send ipc cmd to MTK ADSP 199db69df4STingHan Shen * 209db69df4STingHan Shen * @ipc: ADSP IPC handle 219db69df4STingHan Shen * @idx: index of the mailbox channel 229db69df4STingHan Shen * @msg: IPC cmd (reply or request) 239db69df4STingHan Shen * 249db69df4STingHan Shen * Returns zero for success from mbox_send_message 259db69df4STingHan Shen * negative value for error 269db69df4STingHan Shen */ 279db69df4STingHan Shen int mtk_adsp_ipc_send(struct mtk_adsp_ipc *ipc, unsigned int idx, uint32_t msg) 289db69df4STingHan Shen { 299db69df4STingHan Shen struct mtk_adsp_chan *adsp_chan; 309db69df4STingHan Shen int ret; 319db69df4STingHan Shen 329db69df4STingHan Shen if (idx >= MTK_ADSP_MBOX_NUM) 339db69df4STingHan Shen return -EINVAL; 349db69df4STingHan Shen 359db69df4STingHan Shen adsp_chan = &ipc->chans[idx]; 369db69df4STingHan Shen ret = mbox_send_message(adsp_chan->ch, &msg); 379db69df4STingHan Shen if (ret < 0) 389db69df4STingHan Shen return ret; 399db69df4STingHan Shen 409db69df4STingHan Shen return 0; 419db69df4STingHan Shen } 429db69df4STingHan Shen EXPORT_SYMBOL_GPL(mtk_adsp_ipc_send); 439db69df4STingHan Shen 449db69df4STingHan Shen /* 459db69df4STingHan Shen * mtk_adsp_ipc_recv - recv callback used by MTK ADSP mailbox 469db69df4STingHan Shen * 479db69df4STingHan Shen * @c: mbox client 489db69df4STingHan Shen * @msg: message received 499db69df4STingHan Shen * 509db69df4STingHan Shen * Users of ADSP IPC will need to privde handle_reply and handle_request 519db69df4STingHan Shen * callbacks. 529db69df4STingHan Shen */ 539db69df4STingHan Shen static void mtk_adsp_ipc_recv(struct mbox_client *c, void *msg) 549db69df4STingHan Shen { 559db69df4STingHan Shen struct mtk_adsp_chan *chan = container_of(c, struct mtk_adsp_chan, cl); 569db69df4STingHan Shen struct device *dev = c->dev; 579db69df4STingHan Shen 589db69df4STingHan Shen switch (chan->idx) { 599db69df4STingHan Shen case MTK_ADSP_MBOX_REPLY: 609db69df4STingHan Shen chan->ipc->ops->handle_reply(chan->ipc); 619db69df4STingHan Shen break; 629db69df4STingHan Shen case MTK_ADSP_MBOX_REQUEST: 639db69df4STingHan Shen chan->ipc->ops->handle_request(chan->ipc); 649db69df4STingHan Shen break; 659db69df4STingHan Shen default: 669db69df4STingHan Shen dev_err(dev, "wrong mbox chan %d\n", chan->idx); 679db69df4STingHan Shen break; 689db69df4STingHan Shen } 699db69df4STingHan Shen } 709db69df4STingHan Shen 719db69df4STingHan Shen static int mtk_adsp_ipc_probe(struct platform_device *pdev) 729db69df4STingHan Shen { 739db69df4STingHan Shen struct device *dev = &pdev->dev; 749db69df4STingHan Shen struct mtk_adsp_ipc *adsp_ipc; 759db69df4STingHan Shen struct mtk_adsp_chan *adsp_chan; 769db69df4STingHan Shen struct mbox_client *cl; 779db69df4STingHan Shen int ret; 789db69df4STingHan Shen int i, j; 799db69df4STingHan Shen 809db69df4STingHan Shen device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent); 819db69df4STingHan Shen 829db69df4STingHan Shen adsp_ipc = devm_kzalloc(dev, sizeof(*adsp_ipc), GFP_KERNEL); 839db69df4STingHan Shen if (!adsp_ipc) 849db69df4STingHan Shen return -ENOMEM; 859db69df4STingHan Shen 869db69df4STingHan Shen for (i = 0; i < MTK_ADSP_MBOX_NUM; i++) { 879db69df4STingHan Shen adsp_chan = &adsp_ipc->chans[i]; 889db69df4STingHan Shen cl = &adsp_chan->cl; 899db69df4STingHan Shen cl->dev = dev->parent; 909db69df4STingHan Shen cl->tx_block = false; 919db69df4STingHan Shen cl->knows_txdone = false; 929db69df4STingHan Shen cl->tx_prepare = NULL; 939db69df4STingHan Shen cl->rx_callback = mtk_adsp_ipc_recv; 949db69df4STingHan Shen 959db69df4STingHan Shen adsp_chan->ipc = adsp_ipc; 969db69df4STingHan Shen adsp_chan->idx = i; 97*74bbdd63STinghan Shen adsp_chan->ch = mbox_request_channel_byname(cl, adsp_mbox_ch_names[i]); 989db69df4STingHan Shen if (IS_ERR(adsp_chan->ch)) { 999db69df4STingHan Shen ret = PTR_ERR(adsp_chan->ch); 1009db69df4STingHan Shen if (ret != -EPROBE_DEFER) 101*74bbdd63STinghan Shen dev_err(dev, "Failed to request mbox chan %s ret %d\n", 102*74bbdd63STinghan Shen adsp_mbox_ch_names[i], ret); 103*74bbdd63STinghan Shen 104*74bbdd63STinghan Shen for (j = 0; j < i; j++) { 105*74bbdd63STinghan Shen adsp_chan = &adsp_ipc->chans[j]; 106*74bbdd63STinghan Shen mbox_free_channel(adsp_chan->ch); 1079db69df4STingHan Shen } 1089db69df4STingHan Shen 109*74bbdd63STinghan Shen return ret; 110*74bbdd63STinghan Shen } 1119db69df4STingHan Shen } 1129db69df4STingHan Shen 1139db69df4STingHan Shen adsp_ipc->dev = dev; 1149db69df4STingHan Shen dev_set_drvdata(dev, adsp_ipc); 1159db69df4STingHan Shen dev_dbg(dev, "MTK ADSP IPC initialized\n"); 1169db69df4STingHan Shen 1179db69df4STingHan Shen return 0; 1189db69df4STingHan Shen } 1199db69df4STingHan Shen 1209db69df4STingHan Shen static int mtk_adsp_ipc_remove(struct platform_device *pdev) 1219db69df4STingHan Shen { 1229db69df4STingHan Shen struct mtk_adsp_ipc *adsp_ipc = dev_get_drvdata(&pdev->dev); 1239db69df4STingHan Shen struct mtk_adsp_chan *adsp_chan; 1249db69df4STingHan Shen int i; 1259db69df4STingHan Shen 1269db69df4STingHan Shen for (i = 0; i < MTK_ADSP_MBOX_NUM; i++) { 1279db69df4STingHan Shen adsp_chan = &adsp_ipc->chans[i]; 1289db69df4STingHan Shen mbox_free_channel(adsp_chan->ch); 1299db69df4STingHan Shen } 1309db69df4STingHan Shen 1319db69df4STingHan Shen return 0; 1329db69df4STingHan Shen } 1339db69df4STingHan Shen 1349db69df4STingHan Shen static struct platform_driver mtk_adsp_ipc_driver = { 1359db69df4STingHan Shen .driver = { 1369db69df4STingHan Shen .name = "mtk-adsp-ipc", 1379db69df4STingHan Shen }, 1389db69df4STingHan Shen .probe = mtk_adsp_ipc_probe, 1399db69df4STingHan Shen .remove = mtk_adsp_ipc_remove, 1409db69df4STingHan Shen }; 1419db69df4STingHan Shen builtin_platform_driver(mtk_adsp_ipc_driver); 1429db69df4STingHan Shen 1439db69df4STingHan Shen MODULE_AUTHOR("Allen-KH Cheng <allen-kh.cheng@mediatek.com>"); 1449db69df4STingHan Shen MODULE_DESCRIPTION("MTK ADSP IPC Driver"); 1459db69df4STingHan Shen MODULE_LICENSE("GPL"); 146