1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
2//
3// Copyright 2019-2025 NXP
4//
5// Author: Daniel Baluta <daniel.baluta@nxp.com>
6//
7// Hardware interface for audio DSP on i.MX8
8
9#include <dt-bindings/firmware/imx/rsrc.h>
10
11#include <linux/arm-smccc.h>
12#include <linux/firmware/imx/svc/misc.h>
13#include <linux/mfd/syscon.h>
14#include <linux/reset.h>
15
16#include "imx-common.h"
17
18/* imx8/imx8x macros */
19#define RESET_VECTOR_VADDR 0x596f8000
20
21/* imx8m macros */
22#define IMX8M_DAP_DEBUG 0x28800000
23#define IMX8M_DAP_DEBUG_SIZE (64 * 1024)
24#define IMX8M_DAP_PWRCTL (0x4000 + 0x3020)
25#define IMX8M_PWRCTL_CORERESET BIT(16)
26
27/* imx8ulp macros */
28#define FSL_SIP_HIFI_XRDC 0xc200000e
29#define SYSCTRL0 0x8
30#define EXECUTE_BIT BIT(13)
31#define RESET_BIT BIT(16)
32#define HIFI4_CLK_BIT BIT(17)
33#define PB_CLK_BIT BIT(18)
34#define PLAT_CLK_BIT BIT(19)
35#define DEBUG_LOGIC_BIT BIT(25)
36
37struct imx8m_chip_data {
38 void __iomem *dap;
39 struct regmap *regmap;
40 struct reset_control *run_stall;
41};
42
43static int imx8_shutdown(struct snd_sof_dev *sdev)
44{
45 /*
46 * Force the DSP to stall. After the firmware image is loaded,
47 * the stall will be removed during run() by a matching
48 * imx_sc_pm_cpu_start() call.
49 */
50 imx_sc_pm_cpu_start(get_chip_pdata(sdev), IMX_SC_R_DSP, enable: false,
51 RESET_VECTOR_VADDR);
52
53 return 0;
54}
55
56/*
57 * DSP control.
58 */
59static int imx8x_run(struct snd_sof_dev *sdev)
60{
61 int ret;
62
63 ret = imx_sc_misc_set_control(get_chip_pdata(sdev), IMX_SC_R_DSP,
64 IMX_SC_C_OFS_SEL, val: 1);
65 if (ret < 0) {
66 dev_err(sdev->dev, "Error system address offset source select\n");
67 return ret;
68 }
69
70 ret = imx_sc_misc_set_control(get_chip_pdata(sdev), IMX_SC_R_DSP,
71 IMX_SC_C_OFS_AUDIO, val: 0x80);
72 if (ret < 0) {
73 dev_err(sdev->dev, "Error system address offset of AUDIO\n");
74 return ret;
75 }
76
77 ret = imx_sc_misc_set_control(get_chip_pdata(sdev), IMX_SC_R_DSP,
78 IMX_SC_C_OFS_PERIPH, val: 0x5A);
79 if (ret < 0) {
80 dev_err(sdev->dev, "Error system address offset of PERIPH %d\n",
81 ret);
82 return ret;
83 }
84
85 ret = imx_sc_misc_set_control(get_chip_pdata(sdev), IMX_SC_R_DSP,
86 IMX_SC_C_OFS_IRQ, val: 0x51);
87 if (ret < 0) {
88 dev_err(sdev->dev, "Error system address offset of IRQ\n");
89 return ret;
90 }
91
92 imx_sc_pm_cpu_start(get_chip_pdata(sdev), IMX_SC_R_DSP, enable: true,
93 RESET_VECTOR_VADDR);
94
95 return 0;
96}
97
98static int imx8_run(struct snd_sof_dev *sdev)
99{
100 int ret;
101
102 ret = imx_sc_misc_set_control(get_chip_pdata(sdev), IMX_SC_R_DSP,
103 IMX_SC_C_OFS_SEL, val: 0);
104 if (ret < 0) {
105 dev_err(sdev->dev, "Error system address offset source select\n");
106 return ret;
107 }
108
109 imx_sc_pm_cpu_start(get_chip_pdata(sdev), IMX_SC_R_DSP, enable: true,
110 RESET_VECTOR_VADDR);
111
112 return 0;
113}
114
115static int imx8_probe(struct snd_sof_dev *sdev)
116{
117 struct imx_sc_ipc *sc_ipc_handle;
118 struct imx_common_data *common;
119 int ret;
120
121 common = sdev->pdata->hw_pdata;
122
123 ret = imx_scu_get_handle(ipc: &sc_ipc_handle);
124 if (ret < 0)
125 return dev_err_probe(dev: sdev->dev, err: ret,
126 fmt: "failed to fetch SC IPC handle\n");
127
128 common->chip_pdata = sc_ipc_handle;
129
130 return 0;
131}
132
133static int imx8m_reset(struct snd_sof_dev *sdev)
134{
135 struct imx8m_chip_data *chip;
136 u32 pwrctl;
137
138 chip = get_chip_pdata(sdev);
139
140 /* put DSP into reset and stall */
141 pwrctl = readl(addr: chip->dap + IMX8M_DAP_PWRCTL);
142 pwrctl |= IMX8M_PWRCTL_CORERESET;
143 writel(val: pwrctl, addr: chip->dap + IMX8M_DAP_PWRCTL);
144
145 /* keep reset asserted for 10 cycles */
146 usleep_range(min: 1, max: 2);
147
148 reset_control_assert(rstc: chip->run_stall);
149
150 /* take the DSP out of reset and keep stalled for FW loading */
151 pwrctl = readl(addr: chip->dap + IMX8M_DAP_PWRCTL);
152 pwrctl &= ~IMX8M_PWRCTL_CORERESET;
153 writel(val: pwrctl, addr: chip->dap + IMX8M_DAP_PWRCTL);
154
155 return 0;
156}
157
158static int imx8m_run(struct snd_sof_dev *sdev)
159{
160 struct imx8m_chip_data *chip = get_chip_pdata(sdev);
161
162 return reset_control_deassert(rstc: chip->run_stall);
163}
164
165static int imx8m_probe(struct snd_sof_dev *sdev)
166{
167 struct imx_common_data *common;
168 struct imx8m_chip_data *chip;
169
170 common = sdev->pdata->hw_pdata;
171
172 chip = devm_kzalloc(dev: sdev->dev, size: sizeof(*chip), GFP_KERNEL);
173 if (!chip)
174 return -ENOMEM;
175
176 chip->dap = devm_ioremap(dev: sdev->dev, IMX8M_DAP_DEBUG, IMX8M_DAP_DEBUG_SIZE);
177 if (!chip->dap)
178 return dev_err_probe(dev: sdev->dev, err: -ENODEV,
179 fmt: "failed to ioremap DAP\n");
180
181 chip->run_stall = devm_reset_control_get_exclusive(dev: sdev->dev, id: "runstall");
182 if (IS_ERR(ptr: chip->run_stall))
183 return dev_err_probe(dev: sdev->dev, err: PTR_ERR(ptr: chip->run_stall),
184 fmt: "failed to get dsp runstall reset control\n");
185
186 common->chip_pdata = chip;
187
188 return 0;
189}
190
191static int imx8ulp_run(struct snd_sof_dev *sdev)
192{
193 struct regmap *regmap = get_chip_pdata(sdev);
194
195 /* Controls the HiFi4 DSP Reset: 1 in reset, 0 out of reset */
196 regmap_update_bits(map: regmap, SYSCTRL0, RESET_BIT, val: 0);
197
198 /* Reset HiFi4 DSP Debug logic: 1 debug reset, 0 out of reset*/
199 regmap_update_bits(map: regmap, SYSCTRL0, DEBUG_LOGIC_BIT, val: 0);
200
201 /* Stall HIFI4 DSP Execution: 1 stall, 0 run */
202 regmap_update_bits(map: regmap, SYSCTRL0, EXECUTE_BIT, val: 0);
203
204 return 0;
205}
206
207static int imx8ulp_reset(struct snd_sof_dev *sdev)
208{
209 struct arm_smccc_res smc_res;
210 struct regmap *regmap;
211
212 regmap = get_chip_pdata(sdev);
213
214 /* HiFi4 Platform Clock Enable: 1 enabled, 0 disabled */
215 regmap_update_bits(map: regmap, SYSCTRL0, PLAT_CLK_BIT, PLAT_CLK_BIT);
216
217 /* HiFi4 PBCLK clock enable: 1 enabled, 0 disabled */
218 regmap_update_bits(map: regmap, SYSCTRL0, PB_CLK_BIT, PB_CLK_BIT);
219
220 /* HiFi4 Clock Enable: 1 enabled, 0 disabled */
221 regmap_update_bits(map: regmap, SYSCTRL0, HIFI4_CLK_BIT, HIFI4_CLK_BIT);
222
223 regmap_update_bits(map: regmap, SYSCTRL0, RESET_BIT, RESET_BIT);
224
225 usleep_range(min: 1, max: 2);
226
227 /* Stall HIFI4 DSP Execution: 1 stall, 0 not stall */
228 regmap_update_bits(map: regmap, SYSCTRL0, EXECUTE_BIT, EXECUTE_BIT);
229 usleep_range(min: 1, max: 2);
230
231 arm_smccc_smc(FSL_SIP_HIFI_XRDC, 0, 0, 0, 0, 0, 0, 0, &smc_res);
232
233 return smc_res.a0;
234}
235
236static int imx8ulp_probe(struct snd_sof_dev *sdev)
237{
238 struct imx_common_data *common;
239 struct regmap *regmap;
240
241 common = sdev->pdata->hw_pdata;
242
243 regmap = syscon_regmap_lookup_by_phandle(np: sdev->dev->of_node, property: "fsl,dsp-ctrl");
244 if (IS_ERR(ptr: regmap))
245 return dev_err_probe(dev: sdev->dev, err: PTR_ERR(ptr: regmap),
246 fmt: "failed to fetch dsp ctrl regmap\n");
247
248 common->chip_pdata = regmap;
249
250 return 0;
251}
252
253static struct snd_soc_dai_driver imx8_dai[] = {
254 IMX_SOF_DAI_DRV_ENTRY_BIDIR("esai0", 1, 8),
255 IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai1", 1, 32),
256};
257
258static struct snd_soc_dai_driver imx8m_dai[] = {
259 IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai1", 1, 32),
260 IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai2", 1, 32),
261 IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai3", 1, 32),
262 IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai5", 1, 32),
263 IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai6", 1, 32),
264 IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai7", 1, 32),
265 IMX_SOF_DAI_DRV_ENTRY("micfil", 0, 0, 1, 8),
266};
267
268static struct snd_soc_dai_driver imx8ulp_dai[] = {
269 IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai5", 1, 32),
270 IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai6", 1, 32),
271};
272
273static struct snd_sof_dsp_ops sof_imx8_ops;
274
275static int imx8_ops_init(struct snd_sof_dev *sdev)
276{
277 /* first copy from template */
278 memcpy(&sof_imx8_ops, &sof_imx_ops, sizeof(sof_imx_ops));
279
280 /* then set common imx8 ops */
281 sof_imx8_ops.dbg_dump = imx8_dump;
282 sof_imx8_ops.dsp_arch_ops = &sof_xtensa_arch_ops;
283 sof_imx8_ops.debugfs_add_region_item =
284 snd_sof_debugfs_add_region_item_iomem;
285
286 /* ... and finally set DAI driver */
287 sof_imx8_ops.drv = get_chip_info(sdev)->drv;
288 sof_imx8_ops.num_drv = get_chip_info(sdev)->num_drv;
289
290 return 0;
291}
292
293static const struct imx_chip_ops imx8_chip_ops = {
294 .probe = imx8_probe,
295 .core_kick = imx8_run,
296 .core_shutdown = imx8_shutdown,
297};
298
299static const struct imx_chip_ops imx8x_chip_ops = {
300 .probe = imx8_probe,
301 .core_kick = imx8x_run,
302 .core_shutdown = imx8_shutdown,
303};
304
305static const struct imx_chip_ops imx8m_chip_ops = {
306 .probe = imx8m_probe,
307 .core_kick = imx8m_run,
308 .core_reset = imx8m_reset,
309};
310
311static const struct imx_chip_ops imx8ulp_chip_ops = {
312 .probe = imx8ulp_probe,
313 .core_kick = imx8ulp_run,
314 .core_reset = imx8ulp_reset,
315};
316
317static struct imx_memory_info imx8_memory_regions[] = {
318 { .name = "iram", .reserved = false },
319 { .name = "sram", .reserved = true },
320 { }
321};
322
323static struct imx_memory_info imx8m_memory_regions[] = {
324 { .name = "iram", .reserved = false },
325 { .name = "sram", .reserved = true },
326 { }
327};
328
329static struct imx_memory_info imx8ulp_memory_regions[] = {
330 { .name = "iram", .reserved = false },
331 { .name = "sram", .reserved = true },
332 { }
333};
334
335static const struct imx_chip_info imx8_chip_info = {
336 .ipc_info = {
337 .has_panic_code = true,
338 .boot_mbox_offset = 0x800000,
339 .window_offset = 0x800000,
340 },
341 .memory = imx8_memory_regions,
342 .drv = imx8_dai,
343 .num_drv = ARRAY_SIZE(imx8_dai),
344 .ops = &imx8_chip_ops,
345};
346
347static const struct imx_chip_info imx8x_chip_info = {
348 .ipc_info = {
349 .has_panic_code = true,
350 .boot_mbox_offset = 0x800000,
351 .window_offset = 0x800000,
352 },
353 .memory = imx8_memory_regions,
354 .drv = imx8_dai,
355 .num_drv = ARRAY_SIZE(imx8_dai),
356 .ops = &imx8x_chip_ops,
357};
358
359static const struct imx_chip_info imx8m_chip_info = {
360 .ipc_info = {
361 .has_panic_code = true,
362 .boot_mbox_offset = 0x800000,
363 .window_offset = 0x800000,
364 },
365 .memory = imx8m_memory_regions,
366 .drv = imx8m_dai,
367 .num_drv = ARRAY_SIZE(imx8m_dai),
368 .ops = &imx8m_chip_ops,
369};
370
371static const struct imx_chip_info imx8ulp_chip_info = {
372 .ipc_info = {
373 .has_panic_code = true,
374 .boot_mbox_offset = 0x800000,
375 .window_offset = 0x800000,
376 },
377 .has_dma_reserved = true,
378 .memory = imx8ulp_memory_regions,
379 .drv = imx8ulp_dai,
380 .num_drv = ARRAY_SIZE(imx8ulp_dai),
381 .ops = &imx8ulp_chip_ops,
382};
383
384static struct snd_sof_of_mach sof_imx8_machs[] = {
385 {
386 .compatible = "fsl,imx8qxp-mek",
387 .sof_tplg_filename = "sof-imx8-wm8960.tplg",
388 .drv_name = "asoc-audio-graph-card2",
389 },
390 {
391 .compatible = "fsl,imx8qxp-mek-wcpu",
392 .sof_tplg_filename = "sof-imx8-wm8962.tplg",
393 .drv_name = "asoc-audio-graph-card2",
394 },
395 {
396 .compatible = "fsl,imx8qm-mek",
397 .sof_tplg_filename = "sof-imx8-wm8960.tplg",
398 .drv_name = "asoc-audio-graph-card2",
399 },
400 {
401 .compatible = "fsl,imx8qm-mek-revd",
402 .sof_tplg_filename = "sof-imx8-wm8962.tplg",
403 .drv_name = "asoc-audio-graph-card2",
404 },
405 {
406 .compatible = "fsl,imx8qxp-mek-bb",
407 .sof_tplg_filename = "sof-imx8-cs42888.tplg",
408 .drv_name = "asoc-audio-graph-card2",
409 },
410 {
411 .compatible = "fsl,imx8qm-mek-bb",
412 .sof_tplg_filename = "sof-imx8-cs42888.tplg",
413 .drv_name = "asoc-audio-graph-card2",
414 },
415 {
416 .compatible = "fsl,imx8mp-evk",
417 .sof_tplg_filename = "sof-imx8mp-wm8960.tplg",
418 .drv_name = "asoc-audio-graph-card2",
419 },
420 {
421 .compatible = "fsl,imx8mp-evk-revb4",
422 .sof_tplg_filename = "sof-imx8mp-wm8962.tplg",
423 .drv_name = "asoc-audio-graph-card2",
424 },
425 {
426 .compatible = "fsl,imx8ulp-evk",
427 .sof_tplg_filename = "sof-imx8ulp-btsco.tplg",
428 .drv_name = "asoc-audio-graph-card2",
429 },
430 {}
431};
432
433IMX_SOF_DEV_DESC(imx8, sof_imx8_machs, &imx8_chip_info, &sof_imx8_ops, imx8_ops_init);
434IMX_SOF_DEV_DESC(imx8x, sof_imx8_machs, &imx8x_chip_info, &sof_imx8_ops, imx8_ops_init);
435IMX_SOF_DEV_DESC(imx8m, sof_imx8_machs, &imx8m_chip_info, &sof_imx8_ops, imx8_ops_init);
436IMX_SOF_DEV_DESC(imx8ulp, sof_imx8_machs, &imx8ulp_chip_info, &sof_imx8_ops, imx8_ops_init);
437
438static const struct of_device_id sof_of_imx8_ids[] = {
439 {
440 .compatible = "fsl,imx8qxp-dsp",
441 .data = &IMX_SOF_DEV_DESC_NAME(imx8x),
442 },
443 {
444 .compatible = "fsl,imx8qm-dsp",
445 .data = &IMX_SOF_DEV_DESC_NAME(imx8),
446 },
447 {
448 .compatible = "fsl,imx8mp-dsp",
449 .data = &IMX_SOF_DEV_DESC_NAME(imx8m),
450 },
451 {
452 .compatible = "fsl,imx8ulp-dsp",
453 .data = &IMX_SOF_DEV_DESC_NAME(imx8ulp),
454 },
455 { }
456};
457MODULE_DEVICE_TABLE(of, sof_of_imx8_ids);
458
459/* DT driver definition */
460static struct platform_driver snd_sof_of_imx8_driver = {
461 .probe = sof_of_probe,
462 .remove = sof_of_remove,
463 .driver = {
464 .name = "sof-audio-of-imx8",
465 .pm = pm_ptr(&sof_of_pm),
466 .of_match_table = sof_of_imx8_ids,
467 },
468};
469module_platform_driver(snd_sof_of_imx8_driver);
470
471MODULE_LICENSE("Dual BSD/GPL");
472MODULE_DESCRIPTION("SOF support for IMX8 platforms");
473MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA");
474

source code of linux/sound/soc/sof/imx/imx8.c