1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright 2019 NXP 4 * Author: Daniel Baluta <daniel.baluta@nxp.com> 5 * 6 * Implementation of the DSP IPC interface (host side) 7 */ 8 9 #include <linux/firmware/imx/dsp.h> 10 #include <linux/kernel.h> 11 #include <linux/mailbox_client.h> 12 #include <linux/module.h> 13 #include <linux/of_platform.h> 14 #include <linux/platform_device.h> 15 #include <linux/slab.h> 16 17 /* 18 * imx_dsp_ring_doorbell - triggers an interrupt on the other side (DSP) 19 * 20 * @dsp: DSP IPC handle 21 * @chan_idx: index of the channel where to trigger the interrupt 22 * 23 * Returns non-negative value for success, negative value for error 24 */ 25 int imx_dsp_ring_doorbell(struct imx_dsp_ipc *ipc, unsigned int idx) 26 { 27 int ret; 28 struct imx_dsp_chan *dsp_chan; 29 30 if (idx >= DSP_MU_CHAN_NUM) 31 return -EINVAL; 32 33 dsp_chan = &ipc->chans[idx]; 34 ret = mbox_send_message(dsp_chan->ch, NULL); 35 if (ret < 0) 36 return ret; 37 38 return 0; 39 } 40 EXPORT_SYMBOL(imx_dsp_ring_doorbell); 41 42 /* 43 * imx_dsp_handle_rx - rx callback used by imx mailbox 44 * 45 * @c: mbox client 46 * @msg: message received 47 * 48 * Users of DSP IPC will need to privde handle_reply and handle_request 49 * callbacks. 50 */ 51 static void imx_dsp_handle_rx(struct mbox_client *c, void *msg) 52 { 53 struct imx_dsp_chan *chan = container_of(c, struct imx_dsp_chan, cl); 54 55 if (chan->idx == 0) { 56 chan->ipc->ops->handle_reply(chan->ipc); 57 } else { 58 chan->ipc->ops->handle_request(chan->ipc); 59 imx_dsp_ring_doorbell(chan->ipc, 1); 60 } 61 } 62 63 struct mbox_chan *imx_dsp_request_channel(struct imx_dsp_ipc *dsp_ipc, int idx) 64 { 65 struct imx_dsp_chan *dsp_chan; 66 67 if (idx >= DSP_MU_CHAN_NUM) 68 return ERR_PTR(-EINVAL); 69 70 dsp_chan = &dsp_ipc->chans[idx]; 71 dsp_chan->ch = mbox_request_channel_byname(&dsp_chan->cl, dsp_chan->name); 72 return dsp_chan->ch; 73 } 74 EXPORT_SYMBOL(imx_dsp_request_channel); 75 76 void imx_dsp_free_channel(struct imx_dsp_ipc *dsp_ipc, int idx) 77 { 78 struct imx_dsp_chan *dsp_chan; 79 80 if (idx >= DSP_MU_CHAN_NUM) 81 return; 82 83 dsp_chan = &dsp_ipc->chans[idx]; 84 mbox_free_channel(dsp_chan->ch); 85 } 86 EXPORT_SYMBOL(imx_dsp_free_channel); 87 88 static int imx_dsp_setup_channels(struct imx_dsp_ipc *dsp_ipc) 89 { 90 struct device *dev = dsp_ipc->dev; 91 struct imx_dsp_chan *dsp_chan; 92 struct mbox_client *cl; 93 char *chan_name; 94 int ret; 95 int i, j; 96 97 for (i = 0; i < DSP_MU_CHAN_NUM; i++) { 98 if (i < 2) 99 chan_name = kasprintf(GFP_KERNEL, "txdb%d", i); 100 else 101 chan_name = kasprintf(GFP_KERNEL, "rxdb%d", i - 2); 102 103 if (!chan_name) 104 return -ENOMEM; 105 106 dsp_chan = &dsp_ipc->chans[i]; 107 dsp_chan->name = chan_name; 108 cl = &dsp_chan->cl; 109 cl->dev = dev; 110 cl->tx_block = false; 111 cl->knows_txdone = true; 112 cl->rx_callback = imx_dsp_handle_rx; 113 114 dsp_chan->ipc = dsp_ipc; 115 dsp_chan->idx = i % 2; 116 dsp_chan->ch = mbox_request_channel_byname(cl, chan_name); 117 if (IS_ERR(dsp_chan->ch)) { 118 ret = PTR_ERR(dsp_chan->ch); 119 if (ret != -EPROBE_DEFER) 120 dev_err(dev, "Failed to request mbox chan %s ret %d\n", 121 chan_name, ret); 122 goto out; 123 } 124 125 dev_dbg(dev, "request mbox chan %s\n", chan_name); 126 } 127 128 return 0; 129 out: 130 for (j = 0; j < i; j++) { 131 dsp_chan = &dsp_ipc->chans[j]; 132 mbox_free_channel(dsp_chan->ch); 133 kfree(dsp_chan->name); 134 } 135 136 return ret; 137 } 138 139 static int imx_dsp_probe(struct platform_device *pdev) 140 { 141 struct device *dev = &pdev->dev; 142 struct imx_dsp_ipc *dsp_ipc; 143 int ret; 144 145 device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent); 146 147 dsp_ipc = devm_kzalloc(dev, sizeof(*dsp_ipc), GFP_KERNEL); 148 if (!dsp_ipc) 149 return -ENOMEM; 150 151 dsp_ipc->dev = dev; 152 dev_set_drvdata(dev, dsp_ipc); 153 154 ret = imx_dsp_setup_channels(dsp_ipc); 155 if (ret < 0) 156 return ret; 157 158 dev_info(dev, "NXP i.MX DSP IPC initialized\n"); 159 160 return 0; 161 } 162 163 static int imx_dsp_remove(struct platform_device *pdev) 164 { 165 struct imx_dsp_chan *dsp_chan; 166 struct imx_dsp_ipc *dsp_ipc; 167 int i; 168 169 dsp_ipc = dev_get_drvdata(&pdev->dev); 170 171 for (i = 0; i < DSP_MU_CHAN_NUM; i++) { 172 dsp_chan = &dsp_ipc->chans[i]; 173 mbox_free_channel(dsp_chan->ch); 174 kfree(dsp_chan->name); 175 } 176 177 return 0; 178 } 179 180 static struct platform_driver imx_dsp_driver = { 181 .driver = { 182 .name = "imx-dsp", 183 }, 184 .probe = imx_dsp_probe, 185 .remove = imx_dsp_remove, 186 }; 187 builtin_platform_driver(imx_dsp_driver); 188 189 MODULE_AUTHOR("Daniel Baluta <daniel.baluta@nxp.com>"); 190 MODULE_DESCRIPTION("IMX DSP IPC protocol driver"); 191 MODULE_LICENSE("GPL v2"); 192