root/sound/soc/sof/imx/imx8.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. imx8_get_reply
  2. imx8_get_mailbox_offset
  3. imx8_get_window_offset
  4. imx8_dsp_handle_reply
  5. imx8_dsp_handle_request
  6. imx8_send_msg
  7. imx8_run
  8. imx8_probe
  9. imx8_remove
  10. imx8_get_bar_index
  11. imx8_ipc_msg_data
  12. imx8_ipc_pcm_params

   1 // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
   2 //
   3 // Copyright 2019 NXP
   4 //
   5 // Author: Daniel Baluta <daniel.baluta@nxp.com>
   6 //
   7 // Hardware interface for audio DSP on i.MX8
   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 #include <linux/pm_domain.h>
  14 
  15 #include <linux/module.h>
  16 #include <sound/sof.h>
  17 #include <sound/sof/xtensa.h>
  18 #include <linux/firmware/imx/ipc.h>
  19 #include <linux/firmware/imx/dsp.h>
  20 
  21 #include <linux/firmware/imx/svc/misc.h>
  22 #include <dt-bindings/firmware/imx/rsrc.h>
  23 #include "../ops.h"
  24 
  25 /* DSP memories */
  26 #define IRAM_OFFSET             0x10000
  27 #define IRAM_SIZE               (2 * 1024)
  28 #define DRAM0_OFFSET            0x0
  29 #define DRAM0_SIZE              (32 * 1024)
  30 #define DRAM1_OFFSET            0x8000
  31 #define DRAM1_SIZE              (32 * 1024)
  32 #define SYSRAM_OFFSET           0x18000
  33 #define SYSRAM_SIZE             (256 * 1024)
  34 #define SYSROM_OFFSET           0x58000
  35 #define SYSROM_SIZE             (192 * 1024)
  36 
  37 #define RESET_VECTOR_VADDR      0x596f8000
  38 
  39 #define MBOX_OFFSET     0x800000
  40 #define MBOX_SIZE       0x1000
  41 
  42 struct imx8_priv {
  43         struct device *dev;
  44         struct snd_sof_dev *sdev;
  45 
  46         /* DSP IPC handler */
  47         struct imx_dsp_ipc *dsp_ipc;
  48         struct platform_device *ipc_dev;
  49 
  50         /* System Controller IPC handler */
  51         struct imx_sc_ipc *sc_ipc;
  52 
  53         /* Power domain handling */
  54         int num_domains;
  55         struct device **pd_dev;
  56         struct device_link **link;
  57 
  58 };
  59 
  60 static void imx8_get_reply(struct snd_sof_dev *sdev)
  61 {
  62         struct snd_sof_ipc_msg *msg = sdev->msg;
  63         struct sof_ipc_reply reply;
  64         int ret = 0;
  65 
  66         if (!msg) {
  67                 dev_warn(sdev->dev, "unexpected ipc interrupt\n");
  68                 return;
  69         }
  70 
  71         /* get reply */
  72         sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply));
  73 
  74         if (reply.error < 0) {
  75                 memcpy(msg->reply_data, &reply, sizeof(reply));
  76                 ret = reply.error;
  77         } else {
  78                 /* reply has correct size? */
  79                 if (reply.hdr.size != msg->reply_size) {
  80                         dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n",
  81                                 msg->reply_size, reply.hdr.size);
  82                         ret = -EINVAL;
  83                 }
  84 
  85                 /* read the message */
  86                 if (msg->reply_size > 0)
  87                         sof_mailbox_read(sdev, sdev->host_box.offset,
  88                                          msg->reply_data, msg->reply_size);
  89         }
  90 
  91         msg->reply_error = ret;
  92 }
  93 
  94 static int imx8_get_mailbox_offset(struct snd_sof_dev *sdev)
  95 {
  96         return MBOX_OFFSET;
  97 }
  98 
  99 static int imx8_get_window_offset(struct snd_sof_dev *sdev, u32 id)
 100 {
 101         return MBOX_OFFSET;
 102 }
 103 
 104 static void imx8_dsp_handle_reply(struct imx_dsp_ipc *ipc)
 105 {
 106         struct imx8_priv *priv = imx_dsp_get_data(ipc);
 107         unsigned long flags;
 108 
 109         spin_lock_irqsave(&priv->sdev->ipc_lock, flags);
 110         imx8_get_reply(priv->sdev);
 111         snd_sof_ipc_reply(priv->sdev, 0);
 112         spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags);
 113 }
 114 
 115 static void imx8_dsp_handle_request(struct imx_dsp_ipc *ipc)
 116 {
 117         struct imx8_priv *priv = imx_dsp_get_data(ipc);
 118 
 119         snd_sof_ipc_msgs_rx(priv->sdev);
 120 }
 121 
 122 struct imx_dsp_ops dsp_ops = {
 123         .handle_reply           = imx8_dsp_handle_reply,
 124         .handle_request         = imx8_dsp_handle_request,
 125 };
 126 
 127 static int imx8_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
 128 {
 129         struct imx8_priv *priv = (struct imx8_priv *)sdev->private;
 130 
 131         sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data,
 132                           msg->msg_size);
 133         imx_dsp_ring_doorbell(priv->dsp_ipc, 0);
 134 
 135         return 0;
 136 }
 137 
 138 /*
 139  * DSP control.
 140  */
 141 static int imx8_run(struct snd_sof_dev *sdev)
 142 {
 143         struct imx8_priv *dsp_priv = (struct imx8_priv *)sdev->private;
 144         int ret;
 145 
 146         ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP,
 147                                       IMX_SC_C_OFS_SEL, 1);
 148         if (ret < 0) {
 149                 dev_err(sdev->dev, "Error system address offset source select\n");
 150                 return ret;
 151         }
 152 
 153         ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP,
 154                                       IMX_SC_C_OFS_AUDIO, 0x80);
 155         if (ret < 0) {
 156                 dev_err(sdev->dev, "Error system address offset of AUDIO\n");
 157                 return ret;
 158         }
 159 
 160         ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP,
 161                                       IMX_SC_C_OFS_PERIPH, 0x5A);
 162         if (ret < 0) {
 163                 dev_err(sdev->dev, "Error system address offset of PERIPH %d\n",
 164                         ret);
 165                 return ret;
 166         }
 167 
 168         ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP,
 169                                       IMX_SC_C_OFS_IRQ, 0x51);
 170         if (ret < 0) {
 171                 dev_err(sdev->dev, "Error system address offset of IRQ\n");
 172                 return ret;
 173         }
 174 
 175         imx_sc_pm_cpu_start(dsp_priv->sc_ipc, IMX_SC_R_DSP, true,
 176                             RESET_VECTOR_VADDR);
 177 
 178         return 0;
 179 }
 180 
 181 static int imx8_probe(struct snd_sof_dev *sdev)
 182 {
 183         struct platform_device *pdev =
 184                 container_of(sdev->dev, struct platform_device, dev);
 185         struct device_node *np = pdev->dev.of_node;
 186         struct device_node *res_node;
 187         struct resource *mmio;
 188         struct imx8_priv *priv;
 189         struct resource res;
 190         u32 base, size;
 191         int ret = 0;
 192         int i;
 193 
 194         priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
 195         if (!priv)
 196                 return -ENOMEM;
 197 
 198         sdev->private = priv;
 199         priv->dev = sdev->dev;
 200         priv->sdev = sdev;
 201 
 202         /* power up device associated power domains */
 203         priv->num_domains = of_count_phandle_with_args(np, "power-domains",
 204                                                        "#power-domain-cells");
 205         if (priv->num_domains < 0) {
 206                 dev_err(sdev->dev, "no power-domains property in %pOF\n", np);
 207                 return priv->num_domains;
 208         }
 209 
 210         priv->pd_dev = devm_kmalloc_array(&pdev->dev, priv->num_domains,
 211                                           sizeof(*priv->pd_dev), GFP_KERNEL);
 212         if (!priv->pd_dev)
 213                 return -ENOMEM;
 214 
 215         priv->link = devm_kmalloc_array(&pdev->dev, priv->num_domains,
 216                                         sizeof(*priv->link), GFP_KERNEL);
 217         if (!priv->link)
 218                 return -ENOMEM;
 219 
 220         for (i = 0; i < priv->num_domains; i++) {
 221                 priv->pd_dev[i] = dev_pm_domain_attach_by_id(&pdev->dev, i);
 222                 if (IS_ERR(priv->pd_dev[i])) {
 223                         ret = PTR_ERR(priv->pd_dev[i]);
 224                         goto exit_unroll_pm;
 225                 }
 226                 priv->link[i] = device_link_add(&pdev->dev, priv->pd_dev[i],
 227                                                 DL_FLAG_STATELESS |
 228                                                 DL_FLAG_PM_RUNTIME |
 229                                                 DL_FLAG_RPM_ACTIVE);
 230                 if (!priv->link[i]) {
 231                         ret = -ENOMEM;
 232                         dev_pm_domain_detach(priv->pd_dev[i], false);
 233                         goto exit_unroll_pm;
 234                 }
 235         }
 236 
 237         ret = imx_scu_get_handle(&priv->sc_ipc);
 238         if (ret) {
 239                 dev_err(sdev->dev, "Cannot obtain SCU handle (err = %d)\n",
 240                         ret);
 241                 goto exit_unroll_pm;
 242         }
 243 
 244         priv->ipc_dev = platform_device_register_data(sdev->dev, "imx-dsp",
 245                                                       PLATFORM_DEVID_NONE,
 246                                                       pdev, sizeof(*pdev));
 247         if (IS_ERR(priv->ipc_dev)) {
 248                 ret = PTR_ERR(priv->ipc_dev);
 249                 goto exit_unroll_pm;
 250         }
 251 
 252         priv->dsp_ipc = dev_get_drvdata(&priv->ipc_dev->dev);
 253         if (!priv->dsp_ipc) {
 254                 /* DSP IPC driver not probed yet, try later */
 255                 ret = -EPROBE_DEFER;
 256                 dev_err(sdev->dev, "Failed to get drvdata\n");
 257                 goto exit_pdev_unregister;
 258         }
 259 
 260         imx_dsp_set_data(priv->dsp_ipc, priv);
 261         priv->dsp_ipc->ops = &dsp_ops;
 262 
 263         /* DSP base */
 264         mmio = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 265         if (mmio) {
 266                 base = mmio->start;
 267                 size = resource_size(mmio);
 268         } else {
 269                 dev_err(sdev->dev, "error: failed to get DSP base at idx 0\n");
 270                 ret = -EINVAL;
 271                 goto exit_pdev_unregister;
 272         }
 273 
 274         sdev->bar[SOF_FW_BLK_TYPE_IRAM] = devm_ioremap(sdev->dev, base, size);
 275         if (!sdev->bar[SOF_FW_BLK_TYPE_IRAM]) {
 276                 dev_err(sdev->dev, "failed to ioremap base 0x%x size 0x%x\n",
 277                         base, size);
 278                 ret = -ENODEV;
 279                 goto exit_pdev_unregister;
 280         }
 281         sdev->mmio_bar = SOF_FW_BLK_TYPE_IRAM;
 282 
 283         res_node = of_parse_phandle(np, "memory-region", 0);
 284         if (!res_node) {
 285                 dev_err(&pdev->dev, "failed to get memory region node\n");
 286                 ret = -ENODEV;
 287                 goto exit_pdev_unregister;
 288         }
 289 
 290         ret = of_address_to_resource(res_node, 0, &res);
 291         if (ret) {
 292                 dev_err(&pdev->dev, "failed to get reserved region address\n");
 293                 goto exit_pdev_unregister;
 294         }
 295 
 296         sdev->bar[SOF_FW_BLK_TYPE_SRAM] = devm_ioremap_wc(sdev->dev, res.start,
 297                                                           res.end - res.start +
 298                                                           1);
 299         if (!sdev->bar[SOF_FW_BLK_TYPE_SRAM]) {
 300                 dev_err(sdev->dev, "failed to ioremap mem 0x%x size 0x%x\n",
 301                         base, size);
 302                 ret = -ENOMEM;
 303                 goto exit_pdev_unregister;
 304         }
 305         sdev->mailbox_bar = SOF_FW_BLK_TYPE_SRAM;
 306 
 307         /* set default mailbox offset for FW ready message */
 308         sdev->dsp_box.offset = MBOX_OFFSET;
 309 
 310         return 0;
 311 
 312 exit_pdev_unregister:
 313         platform_device_unregister(priv->ipc_dev);
 314 exit_unroll_pm:
 315         while (--i >= 0) {
 316                 device_link_del(priv->link[i]);
 317                 dev_pm_domain_detach(priv->pd_dev[i], false);
 318         }
 319 
 320         return ret;
 321 }
 322 
 323 static int imx8_remove(struct snd_sof_dev *sdev)
 324 {
 325         struct imx8_priv *priv = (struct imx8_priv *)sdev->private;
 326         int i;
 327 
 328         platform_device_unregister(priv->ipc_dev);
 329 
 330         for (i = 0; i < priv->num_domains; i++) {
 331                 device_link_del(priv->link[i]);
 332                 dev_pm_domain_detach(priv->pd_dev[i], false);
 333         }
 334 
 335         return 0;
 336 }
 337 
 338 /* on i.MX8 there is 1 to 1 match between type and BAR idx */
 339 static int imx8_get_bar_index(struct snd_sof_dev *sdev, u32 type)
 340 {
 341         return type;
 342 }
 343 
 344 static void imx8_ipc_msg_data(struct snd_sof_dev *sdev,
 345                               struct snd_pcm_substream *substream,
 346                               void *p, size_t sz)
 347 {
 348         sof_mailbox_read(sdev, sdev->dsp_box.offset, p, sz);
 349 }
 350 
 351 static int imx8_ipc_pcm_params(struct snd_sof_dev *sdev,
 352                                struct snd_pcm_substream *substream,
 353                                const struct sof_ipc_pcm_params_reply *reply)
 354 {
 355         return 0;
 356 }
 357 
 358 static struct snd_soc_dai_driver imx8_dai[] = {
 359 {
 360         .name = "esai-port",
 361 },
 362 };
 363 
 364 /* i.MX8  ops */
 365 struct snd_sof_dsp_ops sof_imx8_ops = {
 366         /* probe and remove */
 367         .probe          = imx8_probe,
 368         .remove         = imx8_remove,
 369         /* DSP core boot */
 370         .run            = imx8_run,
 371 
 372         /* Block IO */
 373         .block_read     = sof_block_read,
 374         .block_write    = sof_block_write,
 375 
 376         /* ipc */
 377         .send_msg       = imx8_send_msg,
 378         .fw_ready       = sof_fw_ready,
 379         .get_mailbox_offset     = imx8_get_mailbox_offset,
 380         .get_window_offset      = imx8_get_window_offset,
 381 
 382         .ipc_msg_data   = imx8_ipc_msg_data,
 383         .ipc_pcm_params = imx8_ipc_pcm_params,
 384 
 385         /* module loading */
 386         .load_module    = snd_sof_parse_module_memcpy,
 387         .get_bar_index  = imx8_get_bar_index,
 388         /* firmware loading */
 389         .load_firmware  = snd_sof_load_firmware_memcpy,
 390 
 391         /* DAI drivers */
 392         .drv = imx8_dai,
 393         .num_drv = 1, /* we have only 1 ESAI interface on i.MX8 */
 394 };
 395 EXPORT_SYMBOL(sof_imx8_ops);
 396 
 397 MODULE_LICENSE("Dual BSD/GPL");

/* [<][>][^][v][top][bottom][index][help] */