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/platform_device.h> 14 #include <linux/slab.h> 15 16 /* 17 * imx_dsp_ring_doorbell - triggers an interrupt on the other side (DSP) 18 * 19 * @dsp: DSP IPC handle 20 * @chan_idx: index of the channel where to trigger the interrupt 21 * 22 * Returns non-negative value for success, negative value for error 23 */ 24 int imx_dsp_ring_doorbell(struct imx_dsp_ipc *ipc, unsigned int idx) 25 { 26 int ret; 27 struct imx_dsp_chan *dsp_chan; 28 29 if (idx >= DSP_MU_CHAN_NUM) 30 return -EINVAL; 31 32 dsp_chan = &ipc->chans[idx]; 33 ret = mbox_send_message(dsp_chan->ch, NULL); 34 if (ret < 0) 35 return ret; 36 37 return 0; 38 } 39 EXPORT_SYMBOL(imx_dsp_ring_doorbell); 40 41 /* 42 * imx_dsp_handle_rx - rx callback used by imx mailbox 43 * 44 * @c: mbox client 45 * @msg: message received 46 * 47 * Users of DSP IPC will need to privde handle_reply and handle_request 48 * callbacks. 49 */ 50 static void imx_dsp_handle_rx(struct mbox_client *c, void *msg) 51 { 52 struct imx_dsp_chan *chan = container_of(c, struct imx_dsp_chan, cl); 53 54 if (chan->idx == 0) { 55 chan->ipc->ops->handle_reply(chan->ipc); 56 } else { 57 chan->ipc->ops->handle_request(chan->ipc); 58 imx_dsp_ring_doorbell(chan->ipc, 1); 59 } 60 } 61 62 struct mbox_chan *imx_dsp_request_channel(struct imx_dsp_ipc *dsp_ipc, int idx) 63 { 64 struct imx_dsp_chan *dsp_chan; 65 66 if (idx >= DSP_MU_CHAN_NUM) 67 return ERR_PTR(-EINVAL); 68 69 dsp_chan = &dsp_ipc->chans[idx]; 70 dsp_chan->ch = mbox_request_channel_byname(&dsp_chan->cl, dsp_chan->name); 71 return dsp_chan->ch; 72 } 73 EXPORT_SYMBOL(imx_dsp_request_channel); 74 75 void imx_dsp_free_channel(struct imx_dsp_ipc *dsp_ipc, int idx) 76 { 77 struct imx_dsp_chan *dsp_chan; 78 79 if (idx >= DSP_MU_CHAN_NUM) 80 return; 81 82 dsp_chan = &dsp_ipc->chans[idx]; 83 mbox_free_channel(dsp_chan->ch); 84 } 85 EXPORT_SYMBOL(imx_dsp_free_channel); 86 87 static int imx_dsp_setup_channels(struct imx_dsp_ipc *dsp_ipc) 88 { 89 struct device *dev = dsp_ipc->dev; 90 struct imx_dsp_chan *dsp_chan; 91 struct mbox_client *cl; 92 char *chan_name; 93 int ret; 94 int i, j; 95 96 for (i = 0; i < DSP_MU_CHAN_NUM; i++) { 97 if (i < 2) 98 chan_name = kasprintf(GFP_KERNEL, "txdb%d", i); 99 else 100 chan_name = kasprintf(GFP_KERNEL, "rxdb%d", i - 2); 101 102 if (!chan_name) 103 return -ENOMEM; 104 105 dsp_chan = &dsp_ipc->chans[i]; 106 dsp_chan->name = chan_name; 107 cl = &dsp_chan->cl; 108 cl->dev = dev; 109 cl->tx_block = false; 110 cl->knows_txdone = true; 111 cl->rx_callback = imx_dsp_handle_rx; 112 113 dsp_chan->ipc = dsp_ipc; 114 dsp_chan->idx = i % 2; 115 dsp_chan->ch = mbox_request_channel_byname(cl, chan_name); 116 if (IS_ERR(dsp_chan->ch)) { 117 ret = PTR_ERR(dsp_chan->ch); 118 if (ret != -EPROBE_DEFER) 119 dev_err(dev, "Failed to request mbox chan %s ret %d\n", 120 chan_name, ret); 121 kfree(dsp_chan->name); 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