linux/sound/soc/sof/imx/imx8.c
<<
>>
Prefs
   1// SPDX-License-Identifier: (GPL-2.0-only 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#include "imx-common.h"
  25
  26/* DSP memories */
  27#define IRAM_OFFSET             0x10000
  28#define IRAM_SIZE               (2 * 1024)
  29#define DRAM0_OFFSET            0x0
  30#define DRAM0_SIZE              (32 * 1024)
  31#define DRAM1_OFFSET            0x8000
  32#define DRAM1_SIZE              (32 * 1024)
  33#define SYSRAM_OFFSET           0x18000
  34#define SYSRAM_SIZE             (256 * 1024)
  35#define SYSROM_OFFSET           0x58000
  36#define SYSROM_SIZE             (192 * 1024)
  37
  38#define RESET_VECTOR_VADDR      0x596f8000
  39
  40#define MBOX_OFFSET     0x800000
  41#define MBOX_SIZE       0x1000
  42
  43struct imx8_priv {
  44        struct device *dev;
  45        struct snd_sof_dev *sdev;
  46
  47        /* DSP IPC handler */
  48        struct imx_dsp_ipc *dsp_ipc;
  49        struct platform_device *ipc_dev;
  50
  51        /* System Controller IPC handler */
  52        struct imx_sc_ipc *sc_ipc;
  53
  54        /* Power domain handling */
  55        int num_domains;
  56        struct device **pd_dev;
  57        struct device_link **link;
  58
  59};
  60
  61static void imx8_get_reply(struct snd_sof_dev *sdev)
  62{
  63        struct snd_sof_ipc_msg *msg = sdev->msg;
  64        struct sof_ipc_reply reply;
  65        int ret = 0;
  66
  67        if (!msg) {
  68                dev_warn(sdev->dev, "unexpected ipc interrupt\n");
  69                return;
  70        }
  71
  72        /* get reply */
  73        sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply));
  74
  75        if (reply.error < 0) {
  76                memcpy(msg->reply_data, &reply, sizeof(reply));
  77                ret = reply.error;
  78        } else {
  79                /* reply has correct size? */
  80                if (reply.hdr.size != msg->reply_size) {
  81                        dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n",
  82                                msg->reply_size, reply.hdr.size);
  83                        ret = -EINVAL;
  84                }
  85
  86                /* read the message */
  87                if (msg->reply_size > 0)
  88                        sof_mailbox_read(sdev, sdev->host_box.offset,
  89                                         msg->reply_data, msg->reply_size);
  90        }
  91
  92        msg->reply_error = ret;
  93}
  94
  95static int imx8_get_mailbox_offset(struct snd_sof_dev *sdev)
  96{
  97        return MBOX_OFFSET;
  98}
  99
 100static int imx8_get_window_offset(struct snd_sof_dev *sdev, u32 id)
 101{
 102        return MBOX_OFFSET;
 103}
 104
 105static void imx8_dsp_handle_reply(struct imx_dsp_ipc *ipc)
 106{
 107        struct imx8_priv *priv = imx_dsp_get_data(ipc);
 108        unsigned long flags;
 109
 110        spin_lock_irqsave(&priv->sdev->ipc_lock, flags);
 111        imx8_get_reply(priv->sdev);
 112        snd_sof_ipc_reply(priv->sdev, 0);
 113        spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags);
 114}
 115
 116static void imx8_dsp_handle_request(struct imx_dsp_ipc *ipc)
 117{
 118        struct imx8_priv *priv = imx_dsp_get_data(ipc);
 119        u32 p; /* panic code */
 120
 121        /* Read the message from the debug box. */
 122        sof_mailbox_read(priv->sdev, priv->sdev->debug_box.offset + 4, &p, sizeof(p));
 123
 124        /* Check to see if the message is a panic code (0x0dead***) */
 125        if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC)
 126                snd_sof_dsp_panic(priv->sdev, p);
 127        else
 128                snd_sof_ipc_msgs_rx(priv->sdev);
 129}
 130
 131static struct imx_dsp_ops dsp_ops = {
 132        .handle_reply           = imx8_dsp_handle_reply,
 133        .handle_request         = imx8_dsp_handle_request,
 134};
 135
 136static int imx8_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
 137{
 138        struct imx8_priv *priv = sdev->pdata->hw_pdata;
 139
 140        sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data,
 141                          msg->msg_size);
 142        imx_dsp_ring_doorbell(priv->dsp_ipc, 0);
 143
 144        return 0;
 145}
 146
 147/*
 148 * DSP control.
 149 */
 150static int imx8x_run(struct snd_sof_dev *sdev)
 151{
 152        struct imx8_priv *dsp_priv = sdev->pdata->hw_pdata;
 153        int ret;
 154
 155        ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP,
 156                                      IMX_SC_C_OFS_SEL, 1);
 157        if (ret < 0) {
 158                dev_err(sdev->dev, "Error system address offset source select\n");
 159                return ret;
 160        }
 161
 162        ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP,
 163                                      IMX_SC_C_OFS_AUDIO, 0x80);
 164        if (ret < 0) {
 165                dev_err(sdev->dev, "Error system address offset of AUDIO\n");
 166                return ret;
 167        }
 168
 169        ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP,
 170                                      IMX_SC_C_OFS_PERIPH, 0x5A);
 171        if (ret < 0) {
 172                dev_err(sdev->dev, "Error system address offset of PERIPH %d\n",
 173                        ret);
 174                return ret;
 175        }
 176
 177        ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP,
 178                                      IMX_SC_C_OFS_IRQ, 0x51);
 179        if (ret < 0) {
 180                dev_err(sdev->dev, "Error system address offset of IRQ\n");
 181                return ret;
 182        }
 183
 184        imx_sc_pm_cpu_start(dsp_priv->sc_ipc, IMX_SC_R_DSP, true,
 185                            RESET_VECTOR_VADDR);
 186
 187        return 0;
 188}
 189
 190static int imx8_run(struct snd_sof_dev *sdev)
 191{
 192        struct imx8_priv *dsp_priv = sdev->pdata->hw_pdata;
 193        int ret;
 194
 195        ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP,
 196                                      IMX_SC_C_OFS_SEL, 0);
 197        if (ret < 0) {
 198                dev_err(sdev->dev, "Error system address offset source select\n");
 199                return ret;
 200        }
 201
 202        imx_sc_pm_cpu_start(dsp_priv->sc_ipc, IMX_SC_R_DSP, true,
 203                            RESET_VECTOR_VADDR);
 204
 205        return 0;
 206}
 207
 208static int imx8_probe(struct snd_sof_dev *sdev)
 209{
 210        struct platform_device *pdev =
 211                container_of(sdev->dev, struct platform_device, dev);
 212        struct device_node *np = pdev->dev.of_node;
 213        struct device_node *res_node;
 214        struct resource *mmio;
 215        struct imx8_priv *priv;
 216        struct resource res;
 217        u32 base, size;
 218        int ret = 0;
 219        int i;
 220
 221        priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
 222        if (!priv)
 223                return -ENOMEM;
 224
 225        sdev->pdata->hw_pdata = priv;
 226        priv->dev = sdev->dev;
 227        priv->sdev = sdev;
 228
 229        /* power up device associated power domains */
 230        priv->num_domains = of_count_phandle_with_args(np, "power-domains",
 231                                                       "#power-domain-cells");
 232        if (priv->num_domains < 0) {
 233                dev_err(sdev->dev, "no power-domains property in %pOF\n", np);
 234                return priv->num_domains;
 235        }
 236
 237        priv->pd_dev = devm_kmalloc_array(&pdev->dev, priv->num_domains,
 238                                          sizeof(*priv->pd_dev), GFP_KERNEL);
 239        if (!priv->pd_dev)
 240                return -ENOMEM;
 241
 242        priv->link = devm_kmalloc_array(&pdev->dev, priv->num_domains,
 243                                        sizeof(*priv->link), GFP_KERNEL);
 244        if (!priv->link)
 245                return -ENOMEM;
 246
 247        for (i = 0; i < priv->num_domains; i++) {
 248                priv->pd_dev[i] = dev_pm_domain_attach_by_id(&pdev->dev, i);
 249                if (IS_ERR(priv->pd_dev[i])) {
 250                        ret = PTR_ERR(priv->pd_dev[i]);
 251                        goto exit_unroll_pm;
 252                }
 253                priv->link[i] = device_link_add(&pdev->dev, priv->pd_dev[i],
 254                                                DL_FLAG_STATELESS |
 255                                                DL_FLAG_PM_RUNTIME |
 256                                                DL_FLAG_RPM_ACTIVE);
 257                if (!priv->link[i]) {
 258                        ret = -ENOMEM;
 259                        dev_pm_domain_detach(priv->pd_dev[i], false);
 260                        goto exit_unroll_pm;
 261                }
 262        }
 263
 264        ret = imx_scu_get_handle(&priv->sc_ipc);
 265        if (ret) {
 266                dev_err(sdev->dev, "Cannot obtain SCU handle (err = %d)\n",
 267                        ret);
 268                goto exit_unroll_pm;
 269        }
 270
 271        priv->ipc_dev = platform_device_register_data(sdev->dev, "imx-dsp",
 272                                                      PLATFORM_DEVID_NONE,
 273                                                      pdev, sizeof(*pdev));
 274        if (IS_ERR(priv->ipc_dev)) {
 275                ret = PTR_ERR(priv->ipc_dev);
 276                goto exit_unroll_pm;
 277        }
 278
 279        priv->dsp_ipc = dev_get_drvdata(&priv->ipc_dev->dev);
 280        if (!priv->dsp_ipc) {
 281                /* DSP IPC driver not probed yet, try later */
 282                ret = -EPROBE_DEFER;
 283                dev_err(sdev->dev, "Failed to get drvdata\n");
 284                goto exit_pdev_unregister;
 285        }
 286
 287        imx_dsp_set_data(priv->dsp_ipc, priv);
 288        priv->dsp_ipc->ops = &dsp_ops;
 289
 290        /* DSP base */
 291        mmio = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 292        if (mmio) {
 293                base = mmio->start;
 294                size = resource_size(mmio);
 295        } else {
 296                dev_err(sdev->dev, "error: failed to get DSP base at idx 0\n");
 297                ret = -EINVAL;
 298                goto exit_pdev_unregister;
 299        }
 300
 301        sdev->bar[SOF_FW_BLK_TYPE_IRAM] = devm_ioremap(sdev->dev, base, size);
 302        if (!sdev->bar[SOF_FW_BLK_TYPE_IRAM]) {
 303                dev_err(sdev->dev, "failed to ioremap base 0x%x size 0x%x\n",
 304                        base, size);
 305                ret = -ENODEV;
 306                goto exit_pdev_unregister;
 307        }
 308        sdev->mmio_bar = SOF_FW_BLK_TYPE_IRAM;
 309
 310        res_node = of_parse_phandle(np, "memory-region", 0);
 311        if (!res_node) {
 312                dev_err(&pdev->dev, "failed to get memory region node\n");
 313                ret = -ENODEV;
 314                goto exit_pdev_unregister;
 315        }
 316
 317        ret = of_address_to_resource(res_node, 0, &res);
 318        of_node_put(res_node);
 319        if (ret) {
 320                dev_err(&pdev->dev, "failed to get reserved region address\n");
 321                goto exit_pdev_unregister;
 322        }
 323
 324        sdev->bar[SOF_FW_BLK_TYPE_SRAM] = devm_ioremap_wc(sdev->dev, res.start,
 325                                                          resource_size(&res));
 326        if (!sdev->bar[SOF_FW_BLK_TYPE_SRAM]) {
 327                dev_err(sdev->dev, "failed to ioremap mem 0x%x size 0x%x\n",
 328                        base, size);
 329                ret = -ENOMEM;
 330                goto exit_pdev_unregister;
 331        }
 332        sdev->mailbox_bar = SOF_FW_BLK_TYPE_SRAM;
 333
 334        /* set default mailbox offset for FW ready message */
 335        sdev->dsp_box.offset = MBOX_OFFSET;
 336
 337        return 0;
 338
 339exit_pdev_unregister:
 340        platform_device_unregister(priv->ipc_dev);
 341exit_unroll_pm:
 342        while (--i >= 0) {
 343                device_link_del(priv->link[i]);
 344                dev_pm_domain_detach(priv->pd_dev[i], false);
 345        }
 346
 347        return ret;
 348}
 349
 350static int imx8_remove(struct snd_sof_dev *sdev)
 351{
 352        struct imx8_priv *priv = sdev->pdata->hw_pdata;
 353        int i;
 354
 355        platform_device_unregister(priv->ipc_dev);
 356
 357        for (i = 0; i < priv->num_domains; i++) {
 358                device_link_del(priv->link[i]);
 359                dev_pm_domain_detach(priv->pd_dev[i], false);
 360        }
 361
 362        return 0;
 363}
 364
 365/* on i.MX8 there is 1 to 1 match between type and BAR idx */
 366static int imx8_get_bar_index(struct snd_sof_dev *sdev, u32 type)
 367{
 368        return type;
 369}
 370
 371static void imx8_ipc_msg_data(struct snd_sof_dev *sdev,
 372                              struct snd_pcm_substream *substream,
 373                              void *p, size_t sz)
 374{
 375        sof_mailbox_read(sdev, sdev->dsp_box.offset, p, sz);
 376}
 377
 378static int imx8_ipc_pcm_params(struct snd_sof_dev *sdev,
 379                               struct snd_pcm_substream *substream,
 380                               const struct sof_ipc_pcm_params_reply *reply)
 381{
 382        return 0;
 383}
 384
 385static struct snd_soc_dai_driver imx8_dai[] = {
 386{
 387        .name = "esai0",
 388        .playback = {
 389                .channels_min = 1,
 390                .channels_max = 8,
 391        },
 392        .capture = {
 393                .channels_min = 1,
 394                .channels_max = 8,
 395        },
 396},
 397{
 398        .name = "sai1",
 399        .playback = {
 400                .channels_min = 1,
 401                .channels_max = 32,
 402        },
 403        .capture = {
 404                .channels_min = 1,
 405                .channels_max = 32,
 406        },
 407},
 408};
 409
 410/* i.MX8 ops */
 411struct snd_sof_dsp_ops sof_imx8_ops = {
 412        /* probe and remove */
 413        .probe          = imx8_probe,
 414        .remove         = imx8_remove,
 415        /* DSP core boot */
 416        .run            = imx8_run,
 417
 418        /* Block IO */
 419        .block_read     = sof_block_read,
 420        .block_write    = sof_block_write,
 421
 422        /* Module IO */
 423        .read64 = sof_io_read64,
 424
 425        /* ipc */
 426        .send_msg       = imx8_send_msg,
 427        .fw_ready       = sof_fw_ready,
 428        .get_mailbox_offset     = imx8_get_mailbox_offset,
 429        .get_window_offset      = imx8_get_window_offset,
 430
 431        .ipc_msg_data   = imx8_ipc_msg_data,
 432        .ipc_pcm_params = imx8_ipc_pcm_params,
 433
 434        /* module loading */
 435        .load_module    = snd_sof_parse_module_memcpy,
 436        .get_bar_index  = imx8_get_bar_index,
 437        /* firmware loading */
 438        .load_firmware  = snd_sof_load_firmware_memcpy,
 439
 440        /* Debug information */
 441        .dbg_dump = imx8_dump,
 442
 443        /* Firmware ops */
 444        .arch_ops = &sof_xtensa_arch_ops,
 445
 446        /* DAI drivers */
 447        .drv = imx8_dai,
 448        .num_drv = ARRAY_SIZE(imx8_dai),
 449
 450        /* ALSA HW info flags */
 451        .hw_info =      SNDRV_PCM_INFO_MMAP |
 452                        SNDRV_PCM_INFO_MMAP_VALID |
 453                        SNDRV_PCM_INFO_INTERLEAVED |
 454                        SNDRV_PCM_INFO_PAUSE |
 455                        SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
 456};
 457EXPORT_SYMBOL(sof_imx8_ops);
 458
 459/* i.MX8X ops */
 460struct snd_sof_dsp_ops sof_imx8x_ops = {
 461        /* probe and remove */
 462        .probe          = imx8_probe,
 463        .remove         = imx8_remove,
 464        /* DSP core boot */
 465        .run            = imx8x_run,
 466
 467        /* Block IO */
 468        .block_read     = sof_block_read,
 469        .block_write    = sof_block_write,
 470
 471        /* Module IO */
 472        .read64 = sof_io_read64,
 473
 474        /* ipc */
 475        .send_msg       = imx8_send_msg,
 476        .fw_ready       = sof_fw_ready,
 477        .get_mailbox_offset     = imx8_get_mailbox_offset,
 478        .get_window_offset      = imx8_get_window_offset,
 479
 480        .ipc_msg_data   = imx8_ipc_msg_data,
 481        .ipc_pcm_params = imx8_ipc_pcm_params,
 482
 483        /* module loading */
 484        .load_module    = snd_sof_parse_module_memcpy,
 485        .get_bar_index  = imx8_get_bar_index,
 486        /* firmware loading */
 487        .load_firmware  = snd_sof_load_firmware_memcpy,
 488
 489        /* Debug information */
 490        .dbg_dump = imx8_dump,
 491
 492        /* Firmware ops */
 493        .arch_ops = &sof_xtensa_arch_ops,
 494
 495        /* DAI drivers */
 496        .drv = imx8_dai,
 497        .num_drv = ARRAY_SIZE(imx8_dai),
 498
 499        /* ALSA HW info flags */
 500        .hw_info =      SNDRV_PCM_INFO_MMAP |
 501                        SNDRV_PCM_INFO_MMAP_VALID |
 502                        SNDRV_PCM_INFO_INTERLEAVED |
 503                        SNDRV_PCM_INFO_PAUSE |
 504                        SNDRV_PCM_INFO_NO_PERIOD_WAKEUP
 505};
 506EXPORT_SYMBOL(sof_imx8x_ops);
 507
 508MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA);
 509MODULE_LICENSE("Dual BSD/GPL");
 510