Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

firmware: imx: Add DSP IPC protocol interface

Some of i.MX8 processors (e.g i.MX8QM, i.MX8QXP) contain
the Tensilica HiFi4 DSP for advanced pre- and post-audio
processing.

The communication between Host CPU and DSP firmware is
taking place using a shared memory area for message passing
and a dedicated Messaging Unit for notifications.

DSP IPC protocol offers a doorbell interface using
imx-mailbox API.

We use 4 MU channels (2 x TXDB, 2 x RXDB) to implement a
request-reply protocol.

Connection 0 (txdb0, rxdb0):
- Host writes messasge to shared memory [SHMEM]
- Host sends a request [MU]
- DSP handles request [SHMEM]
- DSP sends reply [MU]

Connection 1 (txdb1, rxdb1):
- DSP writes a message to shared memory [SHMEM]
- DSP sends a request [MU]
- Host handles request [SHMEM]
- Host sends reply [MU]

The protocol interface will be used by a Host client to
communicate with the DSP. First client will be the i.MX8
part from Sound Open Firmware infrastructure.

The protocol offers the following interface:

On Tx:
- imx_dsp_ring_doorbell, will be called to notify the DSP
that it needs to handle a request.

On Rx:
- clients need to provide two callbacks:
.handle_reply
.handle_request
- the callbacks will be used by the protocol on
notification arrival from DSP.

Signed-off-by: Daniel Baluta <daniel.baluta@nxp.com>
Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Shawn Guo <shawnguo@kernel.org>

authored by

Daniel Baluta and committed by
Shawn Guo
ffbf23d5 73feb4d0

+234
+11
drivers/firmware/imx/Kconfig
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 + config IMX_DSP 3 + bool "IMX DSP Protocol driver" 4 + depends on IMX_MBOX 5 + help 6 + This enables DSP IPC protocol between host AP (Linux) 7 + and the firmware running on DSP. 8 + DSP exists on some i.MX8 processors (e.g i.MX8QM, i.MX8QXP). 9 + 10 + It acts like a doorbell. Client might use shared memory to 11 + exchange information with DSP side. 12 + 2 13 config IMX_SCU 3 14 bool "IMX SCU Protocol driver" 4 15 depends on IMX_MBOX
+1
drivers/firmware/imx/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 + obj-$(CONFIG_IMX_DSP) += imx-dsp.o 2 3 obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o imx-scu-irq.o 3 4 obj-$(CONFIG_IMX_SCU_PD) += scu-pd.o
+155
drivers/firmware/imx/imx-dsp.c
··· 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");
+67
include/linux/firmware/imx/dsp.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0+ */ 2 + /* 3 + * Copyright 2019 NXP 4 + * 5 + * Header file for the DSP IPC implementation 6 + */ 7 + 8 + #ifndef _IMX_DSP_IPC_H 9 + #define _IMX_DSP_IPC_H 10 + 11 + #include <linux/device.h> 12 + #include <linux/types.h> 13 + #include <linux/mailbox_client.h> 14 + 15 + #define DSP_MU_CHAN_NUM 4 16 + 17 + struct imx_dsp_chan { 18 + struct imx_dsp_ipc *ipc; 19 + struct mbox_client cl; 20 + struct mbox_chan *ch; 21 + char *name; 22 + int idx; 23 + }; 24 + 25 + struct imx_dsp_ops { 26 + void (*handle_reply)(struct imx_dsp_ipc *ipc); 27 + void (*handle_request)(struct imx_dsp_ipc *ipc); 28 + }; 29 + 30 + struct imx_dsp_ipc { 31 + /* Host <-> DSP communication uses 2 txdb and 2 rxdb channels */ 32 + struct imx_dsp_chan chans[DSP_MU_CHAN_NUM]; 33 + struct device *dev; 34 + struct imx_dsp_ops *ops; 35 + void *private_data; 36 + }; 37 + 38 + static inline void imx_dsp_set_data(struct imx_dsp_ipc *ipc, void *data) 39 + { 40 + if (!ipc) 41 + return; 42 + 43 + ipc->private_data = data; 44 + } 45 + 46 + static inline void *imx_dsp_get_data(struct imx_dsp_ipc *ipc) 47 + { 48 + if (!ipc) 49 + return NULL; 50 + 51 + return ipc->private_data; 52 + } 53 + 54 + #if IS_ENABLED(CONFIG_IMX_DSP) 55 + 56 + int imx_dsp_ring_doorbell(struct imx_dsp_ipc *dsp, unsigned int chan_idx); 57 + 58 + #else 59 + 60 + static inline int imx_dsp_ring_doorbell(struct imx_dsp_ipc *ipc, 61 + unsigned int chan_idx) 62 + { 63 + return -ENOTSUPP; 64 + } 65 + 66 + #endif 67 + #endif /* _IMX_DSP_IPC_H */