1 // SPDX-License-Identifier: GPL-2.0+
4 * Author: Daniel Baluta <daniel.baluta@nxp.com>
6 * Implementation of the DSP IPC interface (host side)
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>
18 * imx_dsp_ring_doorbell - triggers an interrupt on the other side (DSP)
20 * @dsp: DSP IPC handle
21 * @chan_idx: index of the channel where to trigger the interrupt
23 * Returns non-negative value for success, negative value for error
25 int imx_dsp_ring_doorbell(struct imx_dsp_ipc
*ipc
, unsigned int idx
)
28 struct imx_dsp_chan
*dsp_chan
;
30 if (idx
>= DSP_MU_CHAN_NUM
)
33 dsp_chan
= &ipc
->chans
[idx
];
34 ret
= mbox_send_message(dsp_chan
->ch
, NULL
);
40 EXPORT_SYMBOL(imx_dsp_ring_doorbell
);
43 * imx_dsp_handle_rx - rx callback used by imx mailbox
46 * @msg: message received
48 * Users of DSP IPC will need to privde handle_reply and handle_request
51 static void imx_dsp_handle_rx(struct mbox_client
*c
, void *msg
)
53 struct imx_dsp_chan
*chan
= container_of(c
, struct imx_dsp_chan
, cl
);
56 chan
->ipc
->ops
->handle_reply(chan
->ipc
);
58 chan
->ipc
->ops
->handle_request(chan
->ipc
);
59 imx_dsp_ring_doorbell(chan
->ipc
, 1);
63 struct mbox_chan
*imx_dsp_request_channel(struct imx_dsp_ipc
*dsp_ipc
, int idx
)
65 struct imx_dsp_chan
*dsp_chan
;
67 if (idx
>= DSP_MU_CHAN_NUM
)
68 return ERR_PTR(-EINVAL
);
70 dsp_chan
= &dsp_ipc
->chans
[idx
];
71 dsp_chan
->ch
= mbox_request_channel_byname(&dsp_chan
->cl
, dsp_chan
->name
);
74 EXPORT_SYMBOL(imx_dsp_request_channel
);
76 void imx_dsp_free_channel(struct imx_dsp_ipc
*dsp_ipc
, int idx
)
78 struct imx_dsp_chan
*dsp_chan
;
80 if (idx
>= DSP_MU_CHAN_NUM
)
83 dsp_chan
= &dsp_ipc
->chans
[idx
];
84 mbox_free_channel(dsp_chan
->ch
);
86 EXPORT_SYMBOL(imx_dsp_free_channel
);
88 static int imx_dsp_setup_channels(struct imx_dsp_ipc
*dsp_ipc
)
90 struct device
*dev
= dsp_ipc
->dev
;
91 struct imx_dsp_chan
*dsp_chan
;
92 struct mbox_client
*cl
;
97 for (i
= 0; i
< DSP_MU_CHAN_NUM
; i
++) {
99 chan_name
= kasprintf(GFP_KERNEL
, "txdb%d", i
);
101 chan_name
= kasprintf(GFP_KERNEL
, "rxdb%d", i
- 2);
106 dsp_chan
= &dsp_ipc
->chans
[i
];
107 dsp_chan
->name
= chan_name
;
110 cl
->tx_block
= false;
111 cl
->knows_txdone
= true;
112 cl
->rx_callback
= imx_dsp_handle_rx
;
114 dsp_chan
->ipc
= dsp_ipc
;
115 dsp_chan
->idx
= i
% 2;
116 dsp_chan
->ch
= mbox_request_channel_byname(cl
, chan_name
);
117 if (IS_ERR(dsp_chan
->ch
)) {
118 ret
= PTR_ERR(dsp_chan
->ch
);
119 if (ret
!= -EPROBE_DEFER
)
120 dev_err(dev
, "Failed to request mbox chan %s ret %d\n",
125 dev_dbg(dev
, "request mbox chan %s\n", chan_name
);
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
);
139 static int imx_dsp_probe(struct platform_device
*pdev
)
141 struct device
*dev
= &pdev
->dev
;
142 struct imx_dsp_ipc
*dsp_ipc
;
145 device_set_of_node_from_dev(&pdev
->dev
, pdev
->dev
.parent
);
147 dsp_ipc
= devm_kzalloc(dev
, sizeof(*dsp_ipc
), GFP_KERNEL
);
152 dev_set_drvdata(dev
, dsp_ipc
);
154 ret
= imx_dsp_setup_channels(dsp_ipc
);
158 dev_info(dev
, "NXP i.MX DSP IPC initialized\n");
163 static int imx_dsp_remove(struct platform_device
*pdev
)
165 struct imx_dsp_chan
*dsp_chan
;
166 struct imx_dsp_ipc
*dsp_ipc
;
169 dsp_ipc
= dev_get_drvdata(&pdev
->dev
);
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
);
180 static struct platform_driver imx_dsp_driver
= {
184 .probe
= imx_dsp_probe
,
185 .remove
= imx_dsp_remove
,
187 builtin_platform_driver(imx_dsp_driver
);
189 MODULE_AUTHOR("Daniel Baluta <daniel.baluta@nxp.com>");
190 MODULE_DESCRIPTION("IMX DSP IPC protocol driver");
191 MODULE_LICENSE("GPL v2");