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 static int imx_dsp_probe(struct platform_device *pdev) 64 { 65 struct device *dev = &pdev->dev; 66 struct imx_dsp_ipc *dsp_ipc; 67 struct imx_dsp_chan *dsp_chan; 68 struct mbox_client *cl; 69 char *chan_name; 70 int ret; 71 int i, j; 72 73 device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent); 74 75 dsp_ipc = devm_kzalloc(dev, sizeof(*dsp_ipc), GFP_KERNEL); 76 if (!dsp_ipc) 77 return -ENOMEM; 78 79 for (i = 0; i < DSP_MU_CHAN_NUM; i++) { 80 if (i < 2) 81 chan_name = kasprintf(GFP_KERNEL, "txdb%d", i); 82 else 83 chan_name = kasprintf(GFP_KERNEL, "rxdb%d", i - 2); 84 85 if (!chan_name) 86 return -ENOMEM; 87 88 dsp_chan = &dsp_ipc->chans[i]; 89 cl = &dsp_chan->cl; 90 cl->dev = dev; 91 cl->tx_block = false; 92 cl->knows_txdone = true; 93 cl->rx_callback = imx_dsp_handle_rx; 94 95 dsp_chan->ipc = dsp_ipc; 96 dsp_chan->idx = i % 2; 97 dsp_chan->ch = mbox_request_channel_byname(cl, chan_name); 98 if (IS_ERR(dsp_chan->ch)) { 99 ret = PTR_ERR(dsp_chan->ch); 100 if (ret != -EPROBE_DEFER) 101 dev_err(dev, "Failed to request mbox chan %s ret %d\n", 102 chan_name, ret); 103 goto out; 104 } 105 106 dev_dbg(dev, "request mbox chan %s\n", chan_name); 107 /* chan_name is not used anymore by framework */ 108 kfree(chan_name); 109 } 110 111 dsp_ipc->dev = dev; 112 113 dev_set_drvdata(dev, dsp_ipc); 114 115 dev_info(dev, "NXP i.MX DSP IPC initialized\n"); 116 117 return devm_of_platform_populate(dev); 118 out: 119 kfree(chan_name); 120 for (j = 0; j < i; j++) { 121 dsp_chan = &dsp_ipc->chans[j]; 122 mbox_free_channel(dsp_chan->ch); 123 } 124 125 return ret; 126 } 127 128 static int imx_dsp_remove(struct platform_device *pdev) 129 { 130 struct imx_dsp_chan *dsp_chan; 131 struct imx_dsp_ipc *dsp_ipc; 132 int i; 133 134 dsp_ipc = dev_get_drvdata(&pdev->dev); 135 136 for (i = 0; i < DSP_MU_CHAN_NUM; i++) { 137 dsp_chan = &dsp_ipc->chans[i]; 138 mbox_free_channel(dsp_chan->ch); 139 } 140 141 return 0; 142 } 143 144 static struct platform_driver imx_dsp_driver = { 145 .driver = { 146 .name = "imx-dsp", 147 }, 148 .probe = imx_dsp_probe, 149 .remove = imx_dsp_remove, 150 }; 151 builtin_platform_driver(imx_dsp_driver); 152 153 MODULE_AUTHOR("Daniel Baluta <daniel.baluta@nxp.com>"); 154 MODULE_DESCRIPTION("IMX DSP IPC protocol driver"); 155 MODULE_LICENSE("GPL v2"); 156