Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Copyright 2019 NXP
0004  *  Author: Daniel Baluta <daniel.baluta@nxp.com>
0005  *
0006  * Implementation of the DSP IPC interface (host side)
0007  */
0008 
0009 #include <linux/firmware/imx/dsp.h>
0010 #include <linux/kernel.h>
0011 #include <linux/mailbox_client.h>
0012 #include <linux/module.h>
0013 #include <linux/of_platform.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/slab.h>
0016 
0017 /*
0018  * imx_dsp_ring_doorbell - triggers an interrupt on the other side (DSP)
0019  *
0020  * @dsp: DSP IPC handle
0021  * @chan_idx: index of the channel where to trigger the interrupt
0022  *
0023  * Returns non-negative value for success, negative value for error
0024  */
0025 int imx_dsp_ring_doorbell(struct imx_dsp_ipc *ipc, unsigned int idx)
0026 {
0027     int ret;
0028     struct imx_dsp_chan *dsp_chan;
0029 
0030     if (idx >= DSP_MU_CHAN_NUM)
0031         return -EINVAL;
0032 
0033     dsp_chan = &ipc->chans[idx];
0034     ret = mbox_send_message(dsp_chan->ch, NULL);
0035     if (ret < 0)
0036         return ret;
0037 
0038     return 0;
0039 }
0040 EXPORT_SYMBOL(imx_dsp_ring_doorbell);
0041 
0042 /*
0043  * imx_dsp_handle_rx - rx callback used by imx mailbox
0044  *
0045  * @c: mbox client
0046  * @msg: message received
0047  *
0048  * Users of DSP IPC will need to privde handle_reply and handle_request
0049  * callbacks.
0050  */
0051 static void imx_dsp_handle_rx(struct mbox_client *c, void *msg)
0052 {
0053     struct imx_dsp_chan *chan = container_of(c, struct imx_dsp_chan, cl);
0054 
0055     if (chan->idx == 0) {
0056         chan->ipc->ops->handle_reply(chan->ipc);
0057     } else {
0058         chan->ipc->ops->handle_request(chan->ipc);
0059         imx_dsp_ring_doorbell(chan->ipc, 1);
0060     }
0061 }
0062 
0063 struct mbox_chan *imx_dsp_request_channel(struct imx_dsp_ipc *dsp_ipc, int idx)
0064 {
0065     struct imx_dsp_chan *dsp_chan;
0066 
0067     if (idx >= DSP_MU_CHAN_NUM)
0068         return ERR_PTR(-EINVAL);
0069 
0070     dsp_chan = &dsp_ipc->chans[idx];
0071     dsp_chan->ch = mbox_request_channel_byname(&dsp_chan->cl, dsp_chan->name);
0072     return dsp_chan->ch;
0073 }
0074 EXPORT_SYMBOL(imx_dsp_request_channel);
0075 
0076 void imx_dsp_free_channel(struct imx_dsp_ipc *dsp_ipc, int idx)
0077 {
0078     struct imx_dsp_chan *dsp_chan;
0079 
0080     if (idx >= DSP_MU_CHAN_NUM)
0081         return;
0082 
0083     dsp_chan = &dsp_ipc->chans[idx];
0084     mbox_free_channel(dsp_chan->ch);
0085 }
0086 EXPORT_SYMBOL(imx_dsp_free_channel);
0087 
0088 static int imx_dsp_setup_channels(struct imx_dsp_ipc *dsp_ipc)
0089 {
0090     struct device *dev = dsp_ipc->dev;
0091     struct imx_dsp_chan *dsp_chan;
0092     struct mbox_client *cl;
0093     char *chan_name;
0094     int ret;
0095     int i, j;
0096 
0097     for (i = 0; i < DSP_MU_CHAN_NUM; i++) {
0098         if (i < 2)
0099             chan_name = kasprintf(GFP_KERNEL, "txdb%d", i);
0100         else
0101             chan_name = kasprintf(GFP_KERNEL, "rxdb%d", i - 2);
0102 
0103         if (!chan_name)
0104             return -ENOMEM;
0105 
0106         dsp_chan = &dsp_ipc->chans[i];
0107         dsp_chan->name = chan_name;
0108         cl = &dsp_chan->cl;
0109         cl->dev = dev;
0110         cl->tx_block = false;
0111         cl->knows_txdone = true;
0112         cl->rx_callback = imx_dsp_handle_rx;
0113 
0114         dsp_chan->ipc = dsp_ipc;
0115         dsp_chan->idx = i % 2;
0116         dsp_chan->ch = mbox_request_channel_byname(cl, chan_name);
0117         if (IS_ERR(dsp_chan->ch)) {
0118             ret = PTR_ERR(dsp_chan->ch);
0119             if (ret != -EPROBE_DEFER)
0120                 dev_err(dev, "Failed to request mbox chan %s ret %d\n",
0121                     chan_name, ret);
0122             goto out;
0123         }
0124 
0125         dev_dbg(dev, "request mbox chan %s\n", chan_name);
0126     }
0127 
0128     return 0;
0129 out:
0130     for (j = 0; j < i; j++) {
0131         dsp_chan = &dsp_ipc->chans[j];
0132         mbox_free_channel(dsp_chan->ch);
0133         kfree(dsp_chan->name);
0134     }
0135 
0136     return ret;
0137 }
0138 
0139 static int imx_dsp_probe(struct platform_device *pdev)
0140 {
0141     struct device *dev = &pdev->dev;
0142     struct imx_dsp_ipc *dsp_ipc;
0143     int ret;
0144 
0145     device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent);
0146 
0147     dsp_ipc = devm_kzalloc(dev, sizeof(*dsp_ipc), GFP_KERNEL);
0148     if (!dsp_ipc)
0149         return -ENOMEM;
0150 
0151     dsp_ipc->dev = dev;
0152     dev_set_drvdata(dev, dsp_ipc);
0153 
0154     ret = imx_dsp_setup_channels(dsp_ipc);
0155     if (ret < 0)
0156         return ret;
0157 
0158     dev_info(dev, "NXP i.MX DSP IPC initialized\n");
0159 
0160     return 0;
0161 }
0162 
0163 static int imx_dsp_remove(struct platform_device *pdev)
0164 {
0165     struct imx_dsp_chan *dsp_chan;
0166     struct imx_dsp_ipc *dsp_ipc;
0167     int i;
0168 
0169     dsp_ipc = dev_get_drvdata(&pdev->dev);
0170 
0171     for (i = 0; i < DSP_MU_CHAN_NUM; i++) {
0172         dsp_chan = &dsp_ipc->chans[i];
0173         mbox_free_channel(dsp_chan->ch);
0174         kfree(dsp_chan->name);
0175     }
0176 
0177     return 0;
0178 }
0179 
0180 static struct platform_driver imx_dsp_driver = {
0181     .driver = {
0182         .name = "imx-dsp",
0183     },
0184     .probe = imx_dsp_probe,
0185     .remove = imx_dsp_remove,
0186 };
0187 builtin_platform_driver(imx_dsp_driver);
0188 
0189 MODULE_AUTHOR("Daniel Baluta <daniel.baluta@nxp.com>");
0190 MODULE_DESCRIPTION("IMX DSP IPC protocol driver");
0191 MODULE_LICENSE("GPL v2");