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 25struct 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 34static 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 68static int imx8m_get_mailbox_offset(struct snd_sof_dev *sdev) 69{ 70 return MBOX_OFFSET; 71} 72 73static int imx8m_get_window_offset(struct snd_sof_dev *sdev, u32 id) 74{ 75 return MBOX_OFFSET; 76} 77 78static 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 89static 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 104static struct imx_dsp_ops imx8m_dsp_ops = { 105 .handle_reply = imx8m_dsp_handle_reply, 106 .handle_request = imx8m_dsp_handle_request, 107}; 108 109static 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 */ 123static int imx8m_run(struct snd_sof_dev *sdev) 124{ 125 /* TODO: start DSP using Audio MIX bits */ 126 return 0; 127} 128 129static 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 of_node_put(res_node); 195 if (ret) { 196 dev_err(&pdev->dev, "failed to get reserved region address\n"); 197 goto exit_pdev_unregister; 198 } 199 200 sdev->bar[SOF_FW_BLK_TYPE_SRAM] = devm_ioremap_wc(sdev->dev, res.start, 201 resource_size(&res)); 202 if (!sdev->bar[SOF_FW_BLK_TYPE_SRAM]) { 203 dev_err(sdev->dev, "failed to ioremap mem 0x%x size 0x%x\n", 204 base, size); 205 ret = -ENOMEM; 206 goto exit_pdev_unregister; 207 } 208 sdev->mailbox_bar = SOF_FW_BLK_TYPE_SRAM; 209 210 /* set default mailbox offset for FW ready message */ 211 sdev->dsp_box.offset = MBOX_OFFSET; 212 213 return 0; 214 215exit_pdev_unregister: 216 platform_device_unregister(priv->ipc_dev); 217 return ret; 218} 219 220static int imx8m_remove(struct snd_sof_dev *sdev) 221{ 222 struct imx8m_priv *priv = sdev->pdata->hw_pdata; 223 224 platform_device_unregister(priv->ipc_dev); 225 226 return 0; 227} 228 229/* on i.MX8 there is 1 to 1 match between type and BAR idx */ 230static int imx8m_get_bar_index(struct snd_sof_dev *sdev, u32 type) 231{ 232 return type; 233} 234 235static void imx8m_ipc_msg_data(struct snd_sof_dev *sdev, 236 struct snd_pcm_substream *substream, 237 void *p, size_t sz) 238{ 239 sof_mailbox_read(sdev, sdev->dsp_box.offset, p, sz); 240} 241 242static int imx8m_ipc_pcm_params(struct snd_sof_dev *sdev, 243 struct snd_pcm_substream *substream, 244 const struct sof_ipc_pcm_params_reply *reply) 245{ 246 return 0; 247} 248 249static struct snd_soc_dai_driver imx8m_dai[] = { 250{ 251 .name = "sai3", 252 .playback = { 253 .channels_min = 1, 254 .channels_max = 32, 255 }, 256 .capture = { 257 .channels_min = 1, 258 .channels_max = 32, 259 }, 260}, 261}; 262 263/* i.MX8 ops */ 264struct snd_sof_dsp_ops sof_imx8m_ops = { 265 /* probe and remove */ 266 .probe = imx8m_probe, 267 .remove = imx8m_remove, 268 /* DSP core boot */ 269 .run = imx8m_run, 270 271 /* Block IO */ 272 .block_read = sof_block_read, 273 .block_write = sof_block_write, 274 275 /* Module IO */ 276 .read64 = sof_io_read64, 277 278 /* ipc */ 279 .send_msg = imx8m_send_msg, 280 .fw_ready = sof_fw_ready, 281 .get_mailbox_offset = imx8m_get_mailbox_offset, 282 .get_window_offset = imx8m_get_window_offset, 283 284 .ipc_msg_data = imx8m_ipc_msg_data, 285 .ipc_pcm_params = imx8m_ipc_pcm_params, 286 287 /* module loading */ 288 .load_module = snd_sof_parse_module_memcpy, 289 .get_bar_index = imx8m_get_bar_index, 290 /* firmware loading */ 291 .load_firmware = snd_sof_load_firmware_memcpy, 292 293 /* Debug information */ 294 .dbg_dump = imx8_dump, 295 296 /* Firmware ops */ 297 .arch_ops = &sof_xtensa_arch_ops, 298 299 /* DAI drivers */ 300 .drv = imx8m_dai, 301 .num_drv = ARRAY_SIZE(imx8m_dai), 302 303 .hw_info = SNDRV_PCM_INFO_MMAP | 304 SNDRV_PCM_INFO_MMAP_VALID | 305 SNDRV_PCM_INFO_INTERLEAVED | 306 SNDRV_PCM_INFO_PAUSE | 307 SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, 308}; 309EXPORT_SYMBOL(sof_imx8m_ops); 310 311MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA); 312MODULE_LICENSE("Dual BSD/GPL"); 313