1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
5 // Author: Daniel Baluta <daniel.baluta@nxp.com>
7 // Hardware interface for audio DSP on i.MX8
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>
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>
21 #include <linux/firmware/imx/svc/misc.h>
22 #include <dt-bindings/firmware/imx/rsrc.h>
24 #include "imx-common.h"
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)
38 #define RESET_VECTOR_VADDR 0x596f8000
40 #define MBOX_OFFSET 0x800000
41 #define MBOX_SIZE 0x1000
45 struct snd_sof_dev
*sdev
;
48 struct imx_dsp_ipc
*dsp_ipc
;
49 struct platform_device
*ipc_dev
;
51 /* System Controller IPC handler */
52 struct imx_sc_ipc
*sc_ipc
;
54 /* Power domain handling */
56 struct device
**pd_dev
;
57 struct device_link
**link
;
61 static void imx8_get_reply(struct snd_sof_dev
*sdev
)
63 struct snd_sof_ipc_msg
*msg
= sdev
->msg
;
64 struct sof_ipc_reply reply
;
68 dev_warn(sdev
->dev
, "unexpected ipc interrupt\n");
73 sof_mailbox_read(sdev
, sdev
->host_box
.offset
, &reply
, sizeof(reply
));
75 if (reply
.error
< 0) {
76 memcpy(msg
->reply_data
, &reply
, sizeof(reply
));
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
);
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
);
92 msg
->reply_error
= ret
;
95 static int imx8_get_mailbox_offset(struct snd_sof_dev
*sdev
)
100 static int imx8_get_window_offset(struct snd_sof_dev
*sdev
, u32 id
)
105 static void imx8_dsp_handle_reply(struct imx_dsp_ipc
*ipc
)
107 struct imx8_priv
*priv
= imx_dsp_get_data(ipc
);
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
);
116 static void imx8_dsp_handle_request(struct imx_dsp_ipc
*ipc
)
118 struct imx8_priv
*priv
= imx_dsp_get_data(ipc
);
119 u32 p
; /* panic code */
121 /* Read the message from the debug box. */
122 sof_mailbox_read(priv
->sdev
, priv
->sdev
->debug_box
.offset
+ 4, &p
, sizeof(p
));
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
);
128 snd_sof_ipc_msgs_rx(priv
->sdev
);
131 static struct imx_dsp_ops dsp_ops
= {
132 .handle_reply
= imx8_dsp_handle_reply
,
133 .handle_request
= imx8_dsp_handle_request
,
136 static int imx8_send_msg(struct snd_sof_dev
*sdev
, struct snd_sof_ipc_msg
*msg
)
138 struct imx8_priv
*priv
= sdev
->pdata
->hw_pdata
;
140 sof_mailbox_write(sdev
, sdev
->host_box
.offset
, msg
->msg_data
,
142 imx_dsp_ring_doorbell(priv
->dsp_ipc
, 0);
150 static int imx8x_run(struct snd_sof_dev
*sdev
)
152 struct imx8_priv
*dsp_priv
= sdev
->pdata
->hw_pdata
;
155 ret
= imx_sc_misc_set_control(dsp_priv
->sc_ipc
, IMX_SC_R_DSP
,
156 IMX_SC_C_OFS_SEL
, 1);
158 dev_err(sdev
->dev
, "Error system address offset source select\n");
162 ret
= imx_sc_misc_set_control(dsp_priv
->sc_ipc
, IMX_SC_R_DSP
,
163 IMX_SC_C_OFS_AUDIO
, 0x80);
165 dev_err(sdev
->dev
, "Error system address offset of AUDIO\n");
169 ret
= imx_sc_misc_set_control(dsp_priv
->sc_ipc
, IMX_SC_R_DSP
,
170 IMX_SC_C_OFS_PERIPH
, 0x5A);
172 dev_err(sdev
->dev
, "Error system address offset of PERIPH %d\n",
177 ret
= imx_sc_misc_set_control(dsp_priv
->sc_ipc
, IMX_SC_R_DSP
,
178 IMX_SC_C_OFS_IRQ
, 0x51);
180 dev_err(sdev
->dev
, "Error system address offset of IRQ\n");
184 imx_sc_pm_cpu_start(dsp_priv
->sc_ipc
, IMX_SC_R_DSP
, true,
190 static int imx8_run(struct snd_sof_dev
*sdev
)
192 struct imx8_priv
*dsp_priv
= sdev
->pdata
->hw_pdata
;
195 ret
= imx_sc_misc_set_control(dsp_priv
->sc_ipc
, IMX_SC_R_DSP
,
196 IMX_SC_C_OFS_SEL
, 0);
198 dev_err(sdev
->dev
, "Error system address offset source select\n");
202 imx_sc_pm_cpu_start(dsp_priv
->sc_ipc
, IMX_SC_R_DSP
, true,
208 static int imx8_probe(struct snd_sof_dev
*sdev
)
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
;
221 priv
= devm_kzalloc(&pdev
->dev
, sizeof(*priv
), GFP_KERNEL
);
225 sdev
->pdata
->hw_pdata
= priv
;
226 priv
->dev
= sdev
->dev
;
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
;
237 priv
->pd_dev
= devm_kmalloc_array(&pdev
->dev
, priv
->num_domains
,
238 sizeof(*priv
->pd_dev
), GFP_KERNEL
);
242 priv
->link
= devm_kmalloc_array(&pdev
->dev
, priv
->num_domains
,
243 sizeof(*priv
->link
), GFP_KERNEL
);
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
]);
253 priv
->link
[i
] = device_link_add(&pdev
->dev
, priv
->pd_dev
[i
],
257 if (!priv
->link
[i
]) {
259 dev_pm_domain_detach(priv
->pd_dev
[i
], false);
264 ret
= imx_scu_get_handle(&priv
->sc_ipc
);
266 dev_err(sdev
->dev
, "Cannot obtain SCU handle (err = %d)\n",
271 priv
->ipc_dev
= platform_device_register_data(sdev
->dev
, "imx-dsp",
273 pdev
, sizeof(*pdev
));
274 if (IS_ERR(priv
->ipc_dev
)) {
275 ret
= PTR_ERR(priv
->ipc_dev
);
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 */
283 dev_err(sdev
->dev
, "Failed to get drvdata\n");
284 goto exit_pdev_unregister
;
287 imx_dsp_set_data(priv
->dsp_ipc
, priv
);
288 priv
->dsp_ipc
->ops
= &dsp_ops
;
291 mmio
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
294 size
= resource_size(mmio
);
296 dev_err(sdev
->dev
, "error: failed to get DSP base at idx 0\n");
298 goto exit_pdev_unregister
;
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",
306 goto exit_pdev_unregister
;
308 sdev
->mmio_bar
= SOF_FW_BLK_TYPE_IRAM
;
310 res_node
= of_parse_phandle(np
, "memory-region", 0);
312 dev_err(&pdev
->dev
, "failed to get memory region node\n");
314 goto exit_pdev_unregister
;
317 ret
= of_address_to_resource(res_node
, 0, &res
);
319 dev_err(&pdev
->dev
, "failed to get reserved region address\n");
320 goto exit_pdev_unregister
;
323 sdev
->bar
[SOF_FW_BLK_TYPE_SRAM
] = devm_ioremap_wc(sdev
->dev
, res
.start
,
324 resource_size(&res
));
325 if (!sdev
->bar
[SOF_FW_BLK_TYPE_SRAM
]) {
326 dev_err(sdev
->dev
, "failed to ioremap mem 0x%x size 0x%x\n",
329 goto exit_pdev_unregister
;
331 sdev
->mailbox_bar
= SOF_FW_BLK_TYPE_SRAM
;
333 /* set default mailbox offset for FW ready message */
334 sdev
->dsp_box
.offset
= MBOX_OFFSET
;
338 exit_pdev_unregister
:
339 platform_device_unregister(priv
->ipc_dev
);
342 device_link_del(priv
->link
[i
]);
343 dev_pm_domain_detach(priv
->pd_dev
[i
], false);
349 static int imx8_remove(struct snd_sof_dev
*sdev
)
351 struct imx8_priv
*priv
= sdev
->pdata
->hw_pdata
;
354 platform_device_unregister(priv
->ipc_dev
);
356 for (i
= 0; i
< priv
->num_domains
; i
++) {
357 device_link_del(priv
->link
[i
]);
358 dev_pm_domain_detach(priv
->pd_dev
[i
], false);
364 /* on i.MX8 there is 1 to 1 match between type and BAR idx */
365 static int imx8_get_bar_index(struct snd_sof_dev
*sdev
, u32 type
)
370 static void imx8_ipc_msg_data(struct snd_sof_dev
*sdev
,
371 struct snd_pcm_substream
*substream
,
374 sof_mailbox_read(sdev
, sdev
->dsp_box
.offset
, p
, sz
);
377 static int imx8_ipc_pcm_params(struct snd_sof_dev
*sdev
,
378 struct snd_pcm_substream
*substream
,
379 const struct sof_ipc_pcm_params_reply
*reply
)
384 static struct snd_soc_dai_driver imx8_dai
[] = {
410 struct snd_sof_dsp_ops sof_imx8_ops
= {
411 /* probe and remove */
413 .remove
= imx8_remove
,
418 .block_read
= sof_block_read
,
419 .block_write
= sof_block_write
,
422 .read64
= sof_io_read64
,
425 .send_msg
= imx8_send_msg
,
426 .fw_ready
= sof_fw_ready
,
427 .get_mailbox_offset
= imx8_get_mailbox_offset
,
428 .get_window_offset
= imx8_get_window_offset
,
430 .ipc_msg_data
= imx8_ipc_msg_data
,
431 .ipc_pcm_params
= imx8_ipc_pcm_params
,
434 .load_module
= snd_sof_parse_module_memcpy
,
435 .get_bar_index
= imx8_get_bar_index
,
436 /* firmware loading */
437 .load_firmware
= snd_sof_load_firmware_memcpy
,
439 /* Debug information */
440 .dbg_dump
= imx8_dump
,
443 .arch_ops
= &sof_xtensa_arch_ops
,
447 .num_drv
= ARRAY_SIZE(imx8_dai
),
449 /* ALSA HW info flags */
450 .hw_info
= SNDRV_PCM_INFO_MMAP
|
451 SNDRV_PCM_INFO_MMAP_VALID
|
452 SNDRV_PCM_INFO_INTERLEAVED
|
453 SNDRV_PCM_INFO_PAUSE
|
454 SNDRV_PCM_INFO_NO_PERIOD_WAKEUP
,
456 EXPORT_SYMBOL(sof_imx8_ops
);
459 struct snd_sof_dsp_ops sof_imx8x_ops
= {
460 /* probe and remove */
462 .remove
= imx8_remove
,
467 .block_read
= sof_block_read
,
468 .block_write
= sof_block_write
,
471 .read64
= sof_io_read64
,
474 .send_msg
= imx8_send_msg
,
475 .fw_ready
= sof_fw_ready
,
476 .get_mailbox_offset
= imx8_get_mailbox_offset
,
477 .get_window_offset
= imx8_get_window_offset
,
479 .ipc_msg_data
= imx8_ipc_msg_data
,
480 .ipc_pcm_params
= imx8_ipc_pcm_params
,
483 .load_module
= snd_sof_parse_module_memcpy
,
484 .get_bar_index
= imx8_get_bar_index
,
485 /* firmware loading */
486 .load_firmware
= snd_sof_load_firmware_memcpy
,
488 /* Debug information */
489 .dbg_dump
= imx8_dump
,
492 .arch_ops
= &sof_xtensa_arch_ops
,
496 .num_drv
= ARRAY_SIZE(imx8_dai
),
498 /* ALSA HW info flags */
499 .hw_info
= SNDRV_PCM_INFO_MMAP
|
500 SNDRV_PCM_INFO_MMAP_VALID
|
501 SNDRV_PCM_INFO_INTERLEAVED
|
502 SNDRV_PCM_INFO_PAUSE
|
503 SNDRV_PCM_INFO_NO_PERIOD_WAKEUP
505 EXPORT_SYMBOL(sof_imx8x_ops
);
507 MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA
);
508 MODULE_LICENSE("Dual BSD/GPL");