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(chan: adsp_chan->ch, mssg: &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(dev: &pdev->dev, dev2: pdev->dev.parent); |
80 | |
81 | adsp_ipc = devm_kzalloc(dev, size: 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, name: adsp_mbox_ch_names[i]); |
97 | if (IS_ERR(ptr: adsp_chan->ch)) { |
98 | ret = PTR_ERR(ptr: adsp_chan->ch); |
99 | if (ret != -EPROBE_DEFER) |
100 | dev_err(dev, "Failed to request mbox chan %s ret %d\n" , |
101 | adsp_mbox_ch_names[i], ret); |
102 | |
103 | for (j = 0; j < i; j++) { |
104 | adsp_chan = &adsp_ipc->chans[j]; |
105 | mbox_free_channel(chan: adsp_chan->ch); |
106 | } |
107 | |
108 | return ret; |
109 | } |
110 | } |
111 | |
112 | adsp_ipc->dev = dev; |
113 | dev_set_drvdata(dev, data: adsp_ipc); |
114 | dev_dbg(dev, "MTK ADSP IPC initialized\n" ); |
115 | |
116 | return 0; |
117 | } |
118 | |
119 | static void mtk_adsp_ipc_remove(struct platform_device *pdev) |
120 | { |
121 | struct mtk_adsp_ipc *adsp_ipc = dev_get_drvdata(dev: &pdev->dev); |
122 | struct mtk_adsp_chan *adsp_chan; |
123 | int i; |
124 | |
125 | for (i = 0; i < MTK_ADSP_MBOX_NUM; i++) { |
126 | adsp_chan = &adsp_ipc->chans[i]; |
127 | mbox_free_channel(chan: adsp_chan->ch); |
128 | } |
129 | } |
130 | |
131 | static struct platform_driver mtk_adsp_ipc_driver = { |
132 | .driver = { |
133 | .name = "mtk-adsp-ipc" , |
134 | }, |
135 | .probe = mtk_adsp_ipc_probe, |
136 | .remove_new = mtk_adsp_ipc_remove, |
137 | }; |
138 | builtin_platform_driver(mtk_adsp_ipc_driver); |
139 | |
140 | MODULE_AUTHOR("Allen-KH Cheng <allen-kh.cheng@mediatek.com>" ); |
141 | MODULE_DESCRIPTION("MTK ADSP IPC Driver" ); |
142 | MODULE_LICENSE("GPL" ); |
143 | |