0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/firmware.h>
0010 #include <linux/of_platform.h>
0011 #include <linux/of_address.h>
0012 #include <linux/of_irq.h>
0013 #include <linux/pm_domain.h>
0014
0015 #include <linux/module.h>
0016 #include <sound/sof.h>
0017 #include <sound/sof/xtensa.h>
0018 #include <linux/firmware/imx/ipc.h>
0019 #include <linux/firmware/imx/dsp.h>
0020
0021 #include <linux/firmware/imx/svc/misc.h>
0022 #include <dt-bindings/firmware/imx/rsrc.h>
0023 #include "../ops.h"
0024 #include "../sof-of-dev.h"
0025 #include "imx-common.h"
0026
0027
0028 #define IRAM_OFFSET 0x10000
0029 #define IRAM_SIZE (2 * 1024)
0030 #define DRAM0_OFFSET 0x0
0031 #define DRAM0_SIZE (32 * 1024)
0032 #define DRAM1_OFFSET 0x8000
0033 #define DRAM1_SIZE (32 * 1024)
0034 #define SYSRAM_OFFSET 0x18000
0035 #define SYSRAM_SIZE (256 * 1024)
0036 #define SYSROM_OFFSET 0x58000
0037 #define SYSROM_SIZE (192 * 1024)
0038
0039 #define RESET_VECTOR_VADDR 0x596f8000
0040
0041 #define MBOX_OFFSET 0x800000
0042 #define MBOX_SIZE 0x1000
0043
0044
0045 static struct clk_bulk_data imx8_dsp_clks[] = {
0046 { .id = "ipg" },
0047 { .id = "ocram" },
0048 { .id = "core" },
0049 };
0050
0051 struct imx8_priv {
0052 struct device *dev;
0053 struct snd_sof_dev *sdev;
0054
0055
0056 struct imx_dsp_ipc *dsp_ipc;
0057 struct platform_device *ipc_dev;
0058
0059
0060 struct imx_sc_ipc *sc_ipc;
0061
0062
0063 int num_domains;
0064 struct device **pd_dev;
0065 struct device_link **link;
0066
0067 struct imx_clocks *clks;
0068 };
0069
0070 static int imx8_get_mailbox_offset(struct snd_sof_dev *sdev)
0071 {
0072 return MBOX_OFFSET;
0073 }
0074
0075 static int imx8_get_window_offset(struct snd_sof_dev *sdev, u32 id)
0076 {
0077 return MBOX_OFFSET;
0078 }
0079
0080 static void imx8_dsp_handle_reply(struct imx_dsp_ipc *ipc)
0081 {
0082 struct imx8_priv *priv = imx_dsp_get_data(ipc);
0083 unsigned long flags;
0084
0085 spin_lock_irqsave(&priv->sdev->ipc_lock, flags);
0086 snd_sof_ipc_process_reply(priv->sdev, 0);
0087 spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags);
0088 }
0089
0090 static void imx8_dsp_handle_request(struct imx_dsp_ipc *ipc)
0091 {
0092 struct imx8_priv *priv = imx_dsp_get_data(ipc);
0093 u32 p;
0094
0095
0096 sof_mailbox_read(priv->sdev, priv->sdev->debug_box.offset + 4, &p, sizeof(p));
0097
0098
0099 if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC)
0100 snd_sof_dsp_panic(priv->sdev, p, true);
0101 else
0102 snd_sof_ipc_msgs_rx(priv->sdev);
0103 }
0104
0105 static struct imx_dsp_ops dsp_ops = {
0106 .handle_reply = imx8_dsp_handle_reply,
0107 .handle_request = imx8_dsp_handle_request,
0108 };
0109
0110 static int imx8_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
0111 {
0112 struct imx8_priv *priv = sdev->pdata->hw_pdata;
0113
0114 sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data,
0115 msg->msg_size);
0116 imx_dsp_ring_doorbell(priv->dsp_ipc, 0);
0117
0118 return 0;
0119 }
0120
0121
0122
0123
0124 static int imx8x_run(struct snd_sof_dev *sdev)
0125 {
0126 struct imx8_priv *dsp_priv = sdev->pdata->hw_pdata;
0127 int ret;
0128
0129 ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP,
0130 IMX_SC_C_OFS_SEL, 1);
0131 if (ret < 0) {
0132 dev_err(sdev->dev, "Error system address offset source select\n");
0133 return ret;
0134 }
0135
0136 ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP,
0137 IMX_SC_C_OFS_AUDIO, 0x80);
0138 if (ret < 0) {
0139 dev_err(sdev->dev, "Error system address offset of AUDIO\n");
0140 return ret;
0141 }
0142
0143 ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP,
0144 IMX_SC_C_OFS_PERIPH, 0x5A);
0145 if (ret < 0) {
0146 dev_err(sdev->dev, "Error system address offset of PERIPH %d\n",
0147 ret);
0148 return ret;
0149 }
0150
0151 ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP,
0152 IMX_SC_C_OFS_IRQ, 0x51);
0153 if (ret < 0) {
0154 dev_err(sdev->dev, "Error system address offset of IRQ\n");
0155 return ret;
0156 }
0157
0158 imx_sc_pm_cpu_start(dsp_priv->sc_ipc, IMX_SC_R_DSP, true,
0159 RESET_VECTOR_VADDR);
0160
0161 return 0;
0162 }
0163
0164 static int imx8_run(struct snd_sof_dev *sdev)
0165 {
0166 struct imx8_priv *dsp_priv = sdev->pdata->hw_pdata;
0167 int ret;
0168
0169 ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP,
0170 IMX_SC_C_OFS_SEL, 0);
0171 if (ret < 0) {
0172 dev_err(sdev->dev, "Error system address offset source select\n");
0173 return ret;
0174 }
0175
0176 imx_sc_pm_cpu_start(dsp_priv->sc_ipc, IMX_SC_R_DSP, true,
0177 RESET_VECTOR_VADDR);
0178
0179 return 0;
0180 }
0181
0182 static int imx8_probe(struct snd_sof_dev *sdev)
0183 {
0184 struct platform_device *pdev =
0185 container_of(sdev->dev, struct platform_device, dev);
0186 struct device_node *np = pdev->dev.of_node;
0187 struct device_node *res_node;
0188 struct resource *mmio;
0189 struct imx8_priv *priv;
0190 struct resource res;
0191 u32 base, size;
0192 int ret = 0;
0193 int i;
0194
0195 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
0196 if (!priv)
0197 return -ENOMEM;
0198
0199 priv->clks = devm_kzalloc(&pdev->dev, sizeof(*priv->clks), GFP_KERNEL);
0200 if (!priv->clks)
0201 return -ENOMEM;
0202
0203 sdev->num_cores = 1;
0204 sdev->pdata->hw_pdata = priv;
0205 priv->dev = sdev->dev;
0206 priv->sdev = sdev;
0207
0208
0209 priv->num_domains = of_count_phandle_with_args(np, "power-domains",
0210 "#power-domain-cells");
0211 if (priv->num_domains < 0) {
0212 dev_err(sdev->dev, "no power-domains property in %pOF\n", np);
0213 return priv->num_domains;
0214 }
0215
0216 priv->pd_dev = devm_kmalloc_array(&pdev->dev, priv->num_domains,
0217 sizeof(*priv->pd_dev), GFP_KERNEL);
0218 if (!priv->pd_dev)
0219 return -ENOMEM;
0220
0221 priv->link = devm_kmalloc_array(&pdev->dev, priv->num_domains,
0222 sizeof(*priv->link), GFP_KERNEL);
0223 if (!priv->link)
0224 return -ENOMEM;
0225
0226 for (i = 0; i < priv->num_domains; i++) {
0227 priv->pd_dev[i] = dev_pm_domain_attach_by_id(&pdev->dev, i);
0228 if (IS_ERR(priv->pd_dev[i])) {
0229 ret = PTR_ERR(priv->pd_dev[i]);
0230 goto exit_unroll_pm;
0231 }
0232 priv->link[i] = device_link_add(&pdev->dev, priv->pd_dev[i],
0233 DL_FLAG_STATELESS |
0234 DL_FLAG_PM_RUNTIME |
0235 DL_FLAG_RPM_ACTIVE);
0236 if (!priv->link[i]) {
0237 ret = -ENOMEM;
0238 dev_pm_domain_detach(priv->pd_dev[i], false);
0239 goto exit_unroll_pm;
0240 }
0241 }
0242
0243 ret = imx_scu_get_handle(&priv->sc_ipc);
0244 if (ret) {
0245 dev_err(sdev->dev, "Cannot obtain SCU handle (err = %d)\n",
0246 ret);
0247 goto exit_unroll_pm;
0248 }
0249
0250 priv->ipc_dev = platform_device_register_data(sdev->dev, "imx-dsp",
0251 PLATFORM_DEVID_NONE,
0252 pdev, sizeof(*pdev));
0253 if (IS_ERR(priv->ipc_dev)) {
0254 ret = PTR_ERR(priv->ipc_dev);
0255 goto exit_unroll_pm;
0256 }
0257
0258 priv->dsp_ipc = dev_get_drvdata(&priv->ipc_dev->dev);
0259 if (!priv->dsp_ipc) {
0260
0261 ret = -EPROBE_DEFER;
0262 dev_err(sdev->dev, "Failed to get drvdata\n");
0263 goto exit_pdev_unregister;
0264 }
0265
0266 imx_dsp_set_data(priv->dsp_ipc, priv);
0267 priv->dsp_ipc->ops = &dsp_ops;
0268
0269
0270 mmio = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0271 if (mmio) {
0272 base = mmio->start;
0273 size = resource_size(mmio);
0274 } else {
0275 dev_err(sdev->dev, "error: failed to get DSP base at idx 0\n");
0276 ret = -EINVAL;
0277 goto exit_pdev_unregister;
0278 }
0279
0280 sdev->bar[SOF_FW_BLK_TYPE_IRAM] = devm_ioremap(sdev->dev, base, size);
0281 if (!sdev->bar[SOF_FW_BLK_TYPE_IRAM]) {
0282 dev_err(sdev->dev, "failed to ioremap base 0x%x size 0x%x\n",
0283 base, size);
0284 ret = -ENODEV;
0285 goto exit_pdev_unregister;
0286 }
0287 sdev->mmio_bar = SOF_FW_BLK_TYPE_IRAM;
0288
0289 res_node = of_parse_phandle(np, "memory-region", 0);
0290 if (!res_node) {
0291 dev_err(&pdev->dev, "failed to get memory region node\n");
0292 ret = -ENODEV;
0293 goto exit_pdev_unregister;
0294 }
0295
0296 ret = of_address_to_resource(res_node, 0, &res);
0297 of_node_put(res_node);
0298 if (ret) {
0299 dev_err(&pdev->dev, "failed to get reserved region address\n");
0300 goto exit_pdev_unregister;
0301 }
0302
0303 sdev->bar[SOF_FW_BLK_TYPE_SRAM] = devm_ioremap_wc(sdev->dev, res.start,
0304 resource_size(&res));
0305 if (!sdev->bar[SOF_FW_BLK_TYPE_SRAM]) {
0306 dev_err(sdev->dev, "failed to ioremap mem 0x%x size 0x%x\n",
0307 base, size);
0308 ret = -ENOMEM;
0309 goto exit_pdev_unregister;
0310 }
0311 sdev->mailbox_bar = SOF_FW_BLK_TYPE_SRAM;
0312
0313
0314 sdev->dsp_box.offset = MBOX_OFFSET;
0315
0316
0317 priv->clks->dsp_clks = imx8_dsp_clks;
0318 priv->clks->num_dsp_clks = ARRAY_SIZE(imx8_dsp_clks);
0319
0320 ret = imx8_parse_clocks(sdev, priv->clks);
0321 if (ret < 0)
0322 goto exit_pdev_unregister;
0323
0324 ret = imx8_enable_clocks(sdev, priv->clks);
0325 if (ret < 0)
0326 goto exit_pdev_unregister;
0327
0328 return 0;
0329
0330 exit_pdev_unregister:
0331 platform_device_unregister(priv->ipc_dev);
0332 exit_unroll_pm:
0333 while (--i >= 0) {
0334 device_link_del(priv->link[i]);
0335 dev_pm_domain_detach(priv->pd_dev[i], false);
0336 }
0337
0338 return ret;
0339 }
0340
0341 static int imx8_remove(struct snd_sof_dev *sdev)
0342 {
0343 struct imx8_priv *priv = sdev->pdata->hw_pdata;
0344 int i;
0345
0346 imx8_disable_clocks(sdev, priv->clks);
0347 platform_device_unregister(priv->ipc_dev);
0348
0349 for (i = 0; i < priv->num_domains; i++) {
0350 device_link_del(priv->link[i]);
0351 dev_pm_domain_detach(priv->pd_dev[i], false);
0352 }
0353
0354 return 0;
0355 }
0356
0357
0358 static int imx8_get_bar_index(struct snd_sof_dev *sdev, u32 type)
0359 {
0360
0361 switch (type) {
0362 case SOF_FW_BLK_TYPE_IRAM:
0363 case SOF_FW_BLK_TYPE_SRAM:
0364 return type;
0365 default:
0366 return -EINVAL;
0367 }
0368 }
0369
0370 static void imx8_suspend(struct snd_sof_dev *sdev)
0371 {
0372 int i;
0373 struct imx8_priv *priv = (struct imx8_priv *)sdev->pdata->hw_pdata;
0374
0375 for (i = 0; i < DSP_MU_CHAN_NUM; i++)
0376 imx_dsp_free_channel(priv->dsp_ipc, i);
0377
0378 imx8_disable_clocks(sdev, priv->clks);
0379 }
0380
0381 static int imx8_resume(struct snd_sof_dev *sdev)
0382 {
0383 struct imx8_priv *priv = (struct imx8_priv *)sdev->pdata->hw_pdata;
0384 int ret;
0385 int i;
0386
0387 ret = imx8_enable_clocks(sdev, priv->clks);
0388 if (ret < 0)
0389 return ret;
0390
0391 for (i = 0; i < DSP_MU_CHAN_NUM; i++)
0392 imx_dsp_request_channel(priv->dsp_ipc, i);
0393
0394 return 0;
0395 }
0396
0397 static int imx8_dsp_runtime_resume(struct snd_sof_dev *sdev)
0398 {
0399 int ret;
0400 const struct sof_dsp_power_state target_dsp_state = {
0401 .state = SOF_DSP_PM_D0,
0402 };
0403
0404 ret = imx8_resume(sdev);
0405 if (ret < 0)
0406 return ret;
0407
0408 return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
0409 }
0410
0411 static int imx8_dsp_runtime_suspend(struct snd_sof_dev *sdev)
0412 {
0413 const struct sof_dsp_power_state target_dsp_state = {
0414 .state = SOF_DSP_PM_D3,
0415 };
0416
0417 imx8_suspend(sdev);
0418
0419 return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
0420 }
0421
0422 static int imx8_dsp_suspend(struct snd_sof_dev *sdev, unsigned int target_state)
0423 {
0424 const struct sof_dsp_power_state target_dsp_state = {
0425 .state = target_state,
0426 };
0427
0428 if (!pm_runtime_suspended(sdev->dev))
0429 imx8_suspend(sdev);
0430
0431 return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
0432 }
0433
0434 static int imx8_dsp_resume(struct snd_sof_dev *sdev)
0435 {
0436 int ret;
0437 const struct sof_dsp_power_state target_dsp_state = {
0438 .state = SOF_DSP_PM_D0,
0439 };
0440
0441 ret = imx8_resume(sdev);
0442 if (ret < 0)
0443 return ret;
0444
0445 if (pm_runtime_suspended(sdev->dev)) {
0446 pm_runtime_disable(sdev->dev);
0447 pm_runtime_set_active(sdev->dev);
0448 pm_runtime_mark_last_busy(sdev->dev);
0449 pm_runtime_enable(sdev->dev);
0450 pm_runtime_idle(sdev->dev);
0451 }
0452
0453 return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
0454 }
0455
0456 static struct snd_soc_dai_driver imx8_dai[] = {
0457 {
0458 .name = "esai0",
0459 .playback = {
0460 .channels_min = 1,
0461 .channels_max = 8,
0462 },
0463 .capture = {
0464 .channels_min = 1,
0465 .channels_max = 8,
0466 },
0467 },
0468 {
0469 .name = "sai1",
0470 .playback = {
0471 .channels_min = 1,
0472 .channels_max = 32,
0473 },
0474 .capture = {
0475 .channels_min = 1,
0476 .channels_max = 32,
0477 },
0478 },
0479 };
0480
0481 static int imx8_dsp_set_power_state(struct snd_sof_dev *sdev,
0482 const struct sof_dsp_power_state *target_state)
0483 {
0484 sdev->dsp_power_state = *target_state;
0485
0486 return 0;
0487 }
0488
0489
0490 static struct snd_sof_dsp_ops sof_imx8_ops = {
0491
0492 .probe = imx8_probe,
0493 .remove = imx8_remove,
0494
0495 .run = imx8_run,
0496
0497
0498 .block_read = sof_block_read,
0499 .block_write = sof_block_write,
0500
0501
0502 .mailbox_read = sof_mailbox_read,
0503 .mailbox_write = sof_mailbox_write,
0504
0505
0506 .send_msg = imx8_send_msg,
0507 .get_mailbox_offset = imx8_get_mailbox_offset,
0508 .get_window_offset = imx8_get_window_offset,
0509
0510 .ipc_msg_data = sof_ipc_msg_data,
0511 .set_stream_data_offset = sof_set_stream_data_offset,
0512
0513 .get_bar_index = imx8_get_bar_index,
0514
0515
0516 .load_firmware = snd_sof_load_firmware_memcpy,
0517
0518
0519 .dbg_dump = imx8_dump,
0520 .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
0521
0522
0523 .pcm_open = sof_stream_pcm_open,
0524 .pcm_close = sof_stream_pcm_close,
0525
0526
0527 .dsp_arch_ops = &sof_xtensa_arch_ops,
0528
0529
0530 .drv = imx8_dai,
0531 .num_drv = ARRAY_SIZE(imx8_dai),
0532
0533
0534 .hw_info = SNDRV_PCM_INFO_MMAP |
0535 SNDRV_PCM_INFO_MMAP_VALID |
0536 SNDRV_PCM_INFO_INTERLEAVED |
0537 SNDRV_PCM_INFO_PAUSE |
0538 SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
0539
0540
0541 .runtime_suspend = imx8_dsp_runtime_suspend,
0542 .runtime_resume = imx8_dsp_runtime_resume,
0543
0544 .suspend = imx8_dsp_suspend,
0545 .resume = imx8_dsp_resume,
0546
0547 .set_power_state = imx8_dsp_set_power_state,
0548 };
0549
0550
0551 static struct snd_sof_dsp_ops sof_imx8x_ops = {
0552
0553 .probe = imx8_probe,
0554 .remove = imx8_remove,
0555
0556 .run = imx8x_run,
0557
0558
0559 .block_read = sof_block_read,
0560 .block_write = sof_block_write,
0561
0562
0563 .mailbox_read = sof_mailbox_read,
0564 .mailbox_write = sof_mailbox_write,
0565
0566
0567 .send_msg = imx8_send_msg,
0568 .get_mailbox_offset = imx8_get_mailbox_offset,
0569 .get_window_offset = imx8_get_window_offset,
0570
0571 .ipc_msg_data = sof_ipc_msg_data,
0572 .set_stream_data_offset = sof_set_stream_data_offset,
0573
0574 .get_bar_index = imx8_get_bar_index,
0575
0576
0577 .load_firmware = snd_sof_load_firmware_memcpy,
0578
0579
0580 .dbg_dump = imx8_dump,
0581 .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
0582
0583
0584 .pcm_open = sof_stream_pcm_open,
0585 .pcm_close = sof_stream_pcm_close,
0586
0587
0588 .dsp_arch_ops = &sof_xtensa_arch_ops,
0589
0590
0591 .drv = imx8_dai,
0592 .num_drv = ARRAY_SIZE(imx8_dai),
0593
0594
0595 .runtime_suspend = imx8_dsp_runtime_suspend,
0596 .runtime_resume = imx8_dsp_runtime_resume,
0597
0598 .suspend = imx8_dsp_suspend,
0599 .resume = imx8_dsp_resume,
0600
0601 .set_power_state = imx8_dsp_set_power_state,
0602
0603
0604 .hw_info = SNDRV_PCM_INFO_MMAP |
0605 SNDRV_PCM_INFO_MMAP_VALID |
0606 SNDRV_PCM_INFO_INTERLEAVED |
0607 SNDRV_PCM_INFO_PAUSE |
0608 SNDRV_PCM_INFO_NO_PERIOD_WAKEUP
0609 };
0610
0611 static struct sof_dev_desc sof_of_imx8qxp_desc = {
0612 .ipc_supported_mask = BIT(SOF_IPC),
0613 .ipc_default = SOF_IPC,
0614 .default_fw_path = {
0615 [SOF_IPC] = "imx/sof",
0616 },
0617 .default_tplg_path = {
0618 [SOF_IPC] = "imx/sof-tplg",
0619 },
0620 .default_fw_filename = {
0621 [SOF_IPC] = "sof-imx8x.ri",
0622 },
0623 .nocodec_tplg_filename = "sof-imx8-nocodec.tplg",
0624 .ops = &sof_imx8x_ops,
0625 };
0626
0627 static struct sof_dev_desc sof_of_imx8qm_desc = {
0628 .ipc_supported_mask = BIT(SOF_IPC),
0629 .ipc_default = SOF_IPC,
0630 .default_fw_path = {
0631 [SOF_IPC] = "imx/sof",
0632 },
0633 .default_tplg_path = {
0634 [SOF_IPC] = "imx/sof-tplg",
0635 },
0636 .default_fw_filename = {
0637 [SOF_IPC] = "sof-imx8.ri",
0638 },
0639 .nocodec_tplg_filename = "sof-imx8-nocodec.tplg",
0640 .ops = &sof_imx8_ops,
0641 };
0642
0643 static const struct of_device_id sof_of_imx8_ids[] = {
0644 { .compatible = "fsl,imx8qxp-dsp", .data = &sof_of_imx8qxp_desc},
0645 { .compatible = "fsl,imx8qm-dsp", .data = &sof_of_imx8qm_desc},
0646 { }
0647 };
0648 MODULE_DEVICE_TABLE(of, sof_of_imx8_ids);
0649
0650
0651 static struct platform_driver snd_sof_of_imx8_driver = {
0652 .probe = sof_of_probe,
0653 .remove = sof_of_remove,
0654 .driver = {
0655 .name = "sof-audio-of-imx8",
0656 .pm = &sof_of_pm,
0657 .of_match_table = sof_of_imx8_ids,
0658 },
0659 };
0660 module_platform_driver(snd_sof_of_imx8_driver);
0661
0662 MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA);
0663 MODULE_LICENSE("Dual BSD/GPL");