1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 2 // 3 // Copyright 2020 NXP 4 // 5 // Author: Daniel Baluta <daniel.baluta@nxp.com> 6 // 7 // Hardware interface for audio DSP on i.MX8M 8 9 #include <linux/firmware.h> 10 #include <linux/of_platform.h> 11 #include <linux/of_address.h> 12 #include <linux/of_irq.h> 13 14 #include <linux/module.h> 15 #include <sound/sof.h> 16 #include <sound/sof/xtensa.h> 17 #include <linux/firmware/imx/dsp.h> 18 19 #include "../ops.h" 20 #include "imx-common.h" 21 22 #define MBOX_OFFSET 0x800000 23 #define MBOX_SIZE 0x1000 24 25 struct imx8m_priv { 26 struct device *dev; 27 struct snd_sof_dev *sdev; 28 29 /* DSP IPC handler */ 30 struct imx_dsp_ipc *dsp_ipc; 31 struct platform_device *ipc_dev; 32 }; 33 34 static void imx8m_get_reply(struct snd_sof_dev *sdev) 35 { 36 struct snd_sof_ipc_msg *msg = sdev->msg; 37 struct sof_ipc_reply reply; 38 int ret = 0; 39 40 if (!msg) { 41 dev_warn(sdev->dev, "unexpected ipc interrupt\n"); 42 return; 43 } 44 45 /* get reply */ 46 sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); 47 48 if (reply.error < 0) { 49 memcpy(msg->reply_data, &reply, sizeof(reply)); 50 ret = reply.error; 51 } else { 52 /* reply has correct size? */ 53 if (reply.hdr.size != msg->reply_size) { 54 dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n", 55 msg->reply_size, reply.hdr.size); 56 ret = -EINVAL; 57 } 58 59 /* read the message */ 60 if (msg->reply_size > 0) 61 sof_mailbox_read(sdev, sdev->host_box.offset, 62 msg->reply_data, msg->reply_size); 63 } 64 65 msg->reply_error = ret; 66 } 67 68 static int imx8m_get_mailbox_offset(struct snd_sof_dev *sdev) 69 { 70 return MBOX_OFFSET; 71 } 72 73 static int imx8m_get_window_offset(struct snd_sof_dev *sdev, u32 id) 74 { 75 return MBOX_OFFSET; 76 } 77 78 static void imx8m_dsp_handle_reply(struct imx_dsp_ipc *ipc) 79 { 80 struct imx8m_priv *priv = imx_dsp_get_data(ipc); 81 unsigned long flags; 82 83 spin_lock_irqsave(&priv->sdev->ipc_lock, flags); 84 imx8m_get_reply(priv->sdev); 85 snd_sof_ipc_reply(priv->sdev, 0); 86 spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags); 87 } 88 89 static void imx8m_dsp_handle_request(struct imx_dsp_ipc *ipc) 90 { 91 struct imx8m_priv *priv = imx_dsp_get_data(ipc); 92 u32 p; /* Panic code */ 93 94 /* Read the message from the debug box. */ 95 sof_mailbox_read(priv->sdev, priv->sdev->debug_box.offset + 4, &p, sizeof(p)); 96 97 /* Check to see if the message is a panic code (0x0dead***) */ 98 if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) 99 snd_sof_dsp_panic(priv->sdev, p); 100 else 101 snd_sof_ipc_msgs_rx(priv->sdev); 102 } 103 104 static struct imx_dsp_ops imx8m_dsp_ops = { 105 .handle_reply = imx8m_dsp_handle_reply, 106 .handle_request = imx8m_dsp_handle_request, 107 }; 108 109 static int imx8m_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) 110 { 111 struct imx8m_priv *priv = sdev->pdata->hw_pdata; 112 113 sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, 114 msg->msg_size); 115 imx_dsp_ring_doorbell(priv->dsp_ipc, 0); 116 117 return 0; 118 } 119 120 /* 121 * DSP control. 122 */ 123 static int imx8m_run(struct snd_sof_dev *sdev) 124 { 125 /* TODO: start DSP using Audio MIX bits */ 126 return 0; 127 } 128 129 static int imx8m_probe(struct snd_sof_dev *sdev) 130 { 131 struct platform_device *pdev = 132 container_of(sdev->dev, struct platform_device, dev); 133 struct device_node *np = pdev->dev.of_node; 134 struct device_node *res_node; 135 struct resource *mmio; 136 struct imx8m_priv *priv; 137 struct resource res; 138 u32 base, size; 139 int ret = 0; 140 141 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 142 if (!priv) 143 return -ENOMEM; 144 145 sdev->pdata->hw_pdata = priv; 146 priv->dev = sdev->dev; 147 priv->sdev = sdev; 148 149 priv->ipc_dev = platform_device_register_data(sdev->dev, "imx-dsp", 150 PLATFORM_DEVID_NONE, 151 pdev, sizeof(*pdev)); 152 if (IS_ERR(priv->ipc_dev)) 153 return PTR_ERR(priv->ipc_dev); 154 155 priv->dsp_ipc = dev_get_drvdata(&priv->ipc_dev->dev); 156 if (!priv->dsp_ipc) { 157 /* DSP IPC driver not probed yet, try later */ 158 ret = -EPROBE_DEFER; 159 dev_err(sdev->dev, "Failed to get drvdata\n"); 160 goto exit_pdev_unregister; 161 } 162 163 imx_dsp_set_data(priv->dsp_ipc, priv); 164 priv->dsp_ipc->ops = &imx8m_dsp_ops; 165 166 /* DSP base */ 167 mmio = platform_get_resource(pdev, IORESOURCE_MEM, 0); 168 if (mmio) { 169 base = mmio->start; 170 size = resource_size(mmio); 171 } else { 172 dev_err(sdev->dev, "error: failed to get DSP base at idx 0\n"); 173 ret = -EINVAL; 174 goto exit_pdev_unregister; 175 } 176 177 sdev->bar[SOF_FW_BLK_TYPE_IRAM] = devm_ioremap(sdev->dev, base, size); 178 if (!sdev->bar[SOF_FW_BLK_TYPE_IRAM]) { 179 dev_err(sdev->dev, "failed to ioremap base 0x%x size 0x%x\n", 180 base, size); 181 ret = -ENODEV; 182 goto exit_pdev_unregister; 183 } 184 sdev->mmio_bar = SOF_FW_BLK_TYPE_IRAM; 185 186 res_node = of_parse_phandle(np, "memory-region", 0); 187 if (!res_node) { 188 dev_err(&pdev->dev, "failed to get memory region node\n"); 189 ret = -ENODEV; 190 goto exit_pdev_unregister; 191 } 192 193 ret = of_address_to_resource(res_node, 0, &res); 194 if (ret) { 195 dev_err(&pdev->dev, "failed to get reserved region address\n"); 196 goto exit_pdev_unregister; 197 } 198 199 sdev->bar[SOF_FW_BLK_TYPE_SRAM] = devm_ioremap_wc(sdev->dev, res.start, 200 resource_size(&res)); 201 if (!sdev->bar[SOF_FW_BLK_TYPE_SRAM]) { 202 dev_err(sdev->dev, "failed to ioremap mem 0x%x size 0x%x\n", 203 base, size); 204 ret = -ENOMEM; 205 goto exit_pdev_unregister; 206 } 207 sdev->mailbox_bar = SOF_FW_BLK_TYPE_SRAM; 208 209 /* set default mailbox offset for FW ready message */ 210 sdev->dsp_box.offset = MBOX_OFFSET; 211 212 return 0; 213 214 exit_pdev_unregister: 215 platform_device_unregister(priv->ipc_dev); 216 return ret; 217 } 218 219 static int imx8m_remove(struct snd_sof_dev *sdev) 220 { 221 struct imx8m_priv *priv = sdev->pdata->hw_pdata; 222 223 platform_device_unregister(priv->ipc_dev); 224 225 return 0; 226 } 227 228 /* on i.MX8 there is 1 to 1 match between type and BAR idx */ 229 static int imx8m_get_bar_index(struct snd_sof_dev *sdev, u32 type) 230 { 231 return type; 232 } 233 234 static void imx8m_ipc_msg_data(struct snd_sof_dev *sdev, 235 struct snd_pcm_substream *substream, 236 void *p, size_t sz) 237 { 238 sof_mailbox_read(sdev, sdev->dsp_box.offset, p, sz); 239 } 240 241 static int imx8m_ipc_pcm_params(struct snd_sof_dev *sdev, 242 struct snd_pcm_substream *substream, 243 const struct sof_ipc_pcm_params_reply *reply) 244 { 245 return 0; 246 } 247 248 static struct snd_soc_dai_driver imx8m_dai[] = { 249 { 250 .name = "sai3", 251 .playback = { 252 .channels_min = 1, 253 .channels_max = 32, 254 }, 255 .capture = { 256 .channels_min = 1, 257 .channels_max = 32, 258 }, 259 }, 260 }; 261 262 /* i.MX8 ops */ 263 struct snd_sof_dsp_ops sof_imx8m_ops = { 264 /* probe and remove */ 265 .probe = imx8m_probe, 266 .remove = imx8m_remove, 267 /* DSP core boot */ 268 .run = imx8m_run, 269 270 /* Block IO */ 271 .block_read = sof_block_read, 272 .block_write = sof_block_write, 273 274 /* Module IO */ 275 .read64 = sof_io_read64, 276 277 /* ipc */ 278 .send_msg = imx8m_send_msg, 279 .fw_ready = sof_fw_ready, 280 .get_mailbox_offset = imx8m_get_mailbox_offset, 281 .get_window_offset = imx8m_get_window_offset, 282 283 .ipc_msg_data = imx8m_ipc_msg_data, 284 .ipc_pcm_params = imx8m_ipc_pcm_params, 285 286 /* module loading */ 287 .load_module = snd_sof_parse_module_memcpy, 288 .get_bar_index = imx8m_get_bar_index, 289 /* firmware loading */ 290 .load_firmware = snd_sof_load_firmware_memcpy, 291 292 /* Debug information */ 293 .dbg_dump = imx8_dump, 294 295 /* Firmware ops */ 296 .arch_ops = &sof_xtensa_arch_ops, 297 298 /* DAI drivers */ 299 .drv = imx8m_dai, 300 .num_drv = ARRAY_SIZE(imx8m_dai), 301 302 .hw_info = SNDRV_PCM_INFO_MMAP | 303 SNDRV_PCM_INFO_MMAP_VALID | 304 SNDRV_PCM_INFO_INTERLEAVED | 305 SNDRV_PCM_INFO_PAUSE | 306 SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, 307 }; 308 EXPORT_SYMBOL(sof_imx8m_ops); 309 310 MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA); 311 MODULE_LICENSE("Dual BSD/GPL"); 312