190 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Copyright 2019 NXP4 * 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 handle20 * @chan_idx: index of the channel where to trigger the interrupt21 *22 * Returns non-negative value for success, negative value for error23 */24int 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}39EXPORT_SYMBOL(imx_dsp_ring_doorbell);40 41/*42 * imx_dsp_handle_rx - rx callback used by imx mailbox43 *44 * @c: mbox client45 * @msg: message received46 *47 * Users of DSP IPC will need to privde handle_reply and handle_request48 * callbacks.49 */50static 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 62struct 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}73EXPORT_SYMBOL(imx_dsp_request_channel);74 75void 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}85EXPORT_SYMBOL(imx_dsp_free_channel);86 87static 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 else100 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;129out: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 139static 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 163static void 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 178static struct platform_driver imx_dsp_driver = {179 .driver = {180 .name = "imx-dsp",181 },182 .probe = imx_dsp_probe,183 .remove_new = imx_dsp_remove,184};185builtin_platform_driver(imx_dsp_driver);186 187MODULE_AUTHOR("Daniel Baluta <daniel.baluta@nxp.com>");188MODULE_DESCRIPTION("IMX DSP IPC protocol driver");189MODULE_LICENSE("GPL v2");190