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 goto out; 122 } 123 124 dev_dbg(dev, "request mbox chan %s\n", chan_name); 125 } 126 127 return 0; 128 out: 129 for (j = 0; j < i; j++) { 130 dsp_chan = &dsp_ipc->chans[j]; 131 mbox_free_channel(dsp_chan->ch); 132 kfree(dsp_chan->name); 133 } 134 135 return ret; 136 } 137 138 static int imx_dsp_probe(struct platform_device *pdev) 139 { 140 struct device *dev = &pdev->dev; 141 struct imx_dsp_ipc *dsp_ipc; 142 int ret; 143 144 device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent); 145 146 dsp_ipc = devm_kzalloc(dev, sizeof(*dsp_ipc), GFP_KERNEL); 147 if (!dsp_ipc) 148 return -ENOMEM; 149 150 dsp_ipc->dev = dev; 151 dev_set_drvdata(dev, dsp_ipc); 152 153 ret = imx_dsp_setup_channels(dsp_ipc); 154 if (ret < 0) 155 return ret; 156 157 dev_info(dev, "NXP i.MX DSP IPC initialized\n"); 158 159 return 0; 160 } 161 162 static int imx_dsp_remove(struct platform_device *pdev) 163 { 164 struct imx_dsp_chan *dsp_chan; 165 struct imx_dsp_ipc *dsp_ipc; 166 int i; 167 168 dsp_ipc = dev_get_drvdata(&pdev->dev); 169 170 for (i = 0; i < DSP_MU_CHAN_NUM; i++) { 171 dsp_chan = &dsp_ipc->chans[i]; 172 mbox_free_channel(dsp_chan->ch); 173 kfree(dsp_chan->name); 174 } 175 176 return 0; 177 } 178 179 static struct platform_driver imx_dsp_driver = { 180 .driver = { 181 .name = "imx-dsp", 182 }, 183 .probe = imx_dsp_probe, 184 .remove = imx_dsp_remove, 185 }; 186 builtin_platform_driver(imx_dsp_driver); 187 188 MODULE_AUTHOR("Daniel Baluta <daniel.baluta@nxp.com>"); 189 MODULE_DESCRIPTION("IMX DSP IPC protocol driver"); 190 MODULE_LICENSE("GPL v2"); 191