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
37 struct imx8m_chip_data {
38 void __iomem *dap;
39 struct regmap *regmap;
40 struct reset_control *run_stall;
41 };
42
imx8_shutdown(struct snd_sof_dev * sdev)43 static 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, false,
51 RESET_VECTOR_VADDR);
52
53 return 0;
54 }
55
56 /*
57 * DSP control.
58 */
imx8x_run(struct snd_sof_dev * sdev)59 static 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, 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, 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, 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, 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, true,
93 RESET_VECTOR_VADDR);
94
95 return 0;
96 }
97
imx8_run(struct snd_sof_dev * sdev)98 static 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, 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, true,
110 RESET_VECTOR_VADDR);
111
112 return 0;
113 }
114
imx8_probe(struct snd_sof_dev * sdev)115 static 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(&sc_ipc_handle);
124 if (ret < 0)
125 return dev_err_probe(sdev->dev, ret,
126 "failed to fetch SC IPC handle\n");
127
128 common->chip_pdata = sc_ipc_handle;
129
130 return 0;
131 }
132
imx8m_reset(struct snd_sof_dev * sdev)133 static 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(chip->dap + IMX8M_DAP_PWRCTL);
142 pwrctl |= IMX8M_PWRCTL_CORERESET;
143 writel(pwrctl, chip->dap + IMX8M_DAP_PWRCTL);
144
145 /* keep reset asserted for 10 cycles */
146 usleep_range(1, 2);
147
148 reset_control_assert(chip->run_stall);
149
150 /* take the DSP out of reset and keep stalled for FW loading */
151 pwrctl = readl(chip->dap + IMX8M_DAP_PWRCTL);
152 pwrctl &= ~IMX8M_PWRCTL_CORERESET;
153 writel(pwrctl, chip->dap + IMX8M_DAP_PWRCTL);
154
155 return 0;
156 }
157
imx8m_run(struct snd_sof_dev * sdev)158 static 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(chip->run_stall);
163 }
164
imx8m_probe(struct snd_sof_dev * sdev)165 static 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(sdev->dev, sizeof(*chip), GFP_KERNEL);
173 if (!chip)
174 return dev_err_probe(sdev->dev, -ENOMEM,
175 "failed to allocate chip data\n");
176
177 chip->dap = devm_ioremap(sdev->dev, IMX8M_DAP_DEBUG, IMX8M_DAP_DEBUG_SIZE);
178 if (!chip->dap)
179 return dev_err_probe(sdev->dev, -ENODEV,
180 "failed to ioremap DAP\n");
181
182 chip->run_stall = devm_reset_control_get_exclusive(sdev->dev, "runstall");
183 if (IS_ERR(chip->run_stall))
184 return dev_err_probe(sdev->dev, PTR_ERR(chip->run_stall),
185 "failed to get dsp runstall reset control\n");
186
187 common->chip_pdata = chip;
188
189 return 0;
190 }
191
imx8ulp_run(struct snd_sof_dev * sdev)192 static int imx8ulp_run(struct snd_sof_dev *sdev)
193 {
194 struct regmap *regmap = get_chip_pdata(sdev);
195
196 /* Controls the HiFi4 DSP Reset: 1 in reset, 0 out of reset */
197 regmap_update_bits(regmap, SYSCTRL0, RESET_BIT, 0);
198
199 /* Reset HiFi4 DSP Debug logic: 1 debug reset, 0 out of reset*/
200 regmap_update_bits(regmap, SYSCTRL0, DEBUG_LOGIC_BIT, 0);
201
202 /* Stall HIFI4 DSP Execution: 1 stall, 0 run */
203 regmap_update_bits(regmap, SYSCTRL0, EXECUTE_BIT, 0);
204
205 return 0;
206 }
207
imx8ulp_reset(struct snd_sof_dev * sdev)208 static int imx8ulp_reset(struct snd_sof_dev *sdev)
209 {
210 struct arm_smccc_res smc_res;
211 struct regmap *regmap;
212
213 regmap = get_chip_pdata(sdev);
214
215 /* HiFi4 Platform Clock Enable: 1 enabled, 0 disabled */
216 regmap_update_bits(regmap, SYSCTRL0, PLAT_CLK_BIT, PLAT_CLK_BIT);
217
218 /* HiFi4 PBCLK clock enable: 1 enabled, 0 disabled */
219 regmap_update_bits(regmap, SYSCTRL0, PB_CLK_BIT, PB_CLK_BIT);
220
221 /* HiFi4 Clock Enable: 1 enabled, 0 disabled */
222 regmap_update_bits(regmap, SYSCTRL0, HIFI4_CLK_BIT, HIFI4_CLK_BIT);
223
224 regmap_update_bits(regmap, SYSCTRL0, RESET_BIT, RESET_BIT);
225
226 usleep_range(1, 2);
227
228 /* Stall HIFI4 DSP Execution: 1 stall, 0 not stall */
229 regmap_update_bits(regmap, SYSCTRL0, EXECUTE_BIT, EXECUTE_BIT);
230 usleep_range(1, 2);
231
232 arm_smccc_smc(FSL_SIP_HIFI_XRDC, 0, 0, 0, 0, 0, 0, 0, &smc_res);
233
234 return smc_res.a0;
235 }
236
imx8ulp_probe(struct snd_sof_dev * sdev)237 static int imx8ulp_probe(struct snd_sof_dev *sdev)
238 {
239 struct imx_common_data *common;
240 struct regmap *regmap;
241
242 common = sdev->pdata->hw_pdata;
243
244 regmap = syscon_regmap_lookup_by_phandle(sdev->dev->of_node, "fsl,dsp-ctrl");
245 if (IS_ERR(regmap))
246 return dev_err_probe(sdev->dev, PTR_ERR(regmap),
247 "failed to fetch dsp ctrl regmap\n");
248
249 common->chip_pdata = regmap;
250
251 return 0;
252 }
253
254 static struct snd_soc_dai_driver imx8_dai[] = {
255 IMX_SOF_DAI_DRV_ENTRY_BIDIR("esai0", 1, 8),
256 IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai1", 1, 32),
257 };
258
259 static struct snd_soc_dai_driver imx8m_dai[] = {
260 IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai1", 1, 32),
261 IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai2", 1, 32),
262 IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai3", 1, 32),
263 IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai5", 1, 32),
264 IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai6", 1, 32),
265 IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai7", 1, 32),
266 IMX_SOF_DAI_DRV_ENTRY("micfil", 0, 0, 1, 8),
267 };
268
269 static struct snd_soc_dai_driver imx8ulp_dai[] = {
270 IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai5", 1, 32),
271 IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai6", 1, 32),
272 };
273
274 static struct snd_sof_dsp_ops sof_imx8_ops;
275
imx8_ops_init(struct snd_sof_dev * sdev)276 static int imx8_ops_init(struct snd_sof_dev *sdev)
277 {
278 /* first copy from template */
279 memcpy(&sof_imx8_ops, &sof_imx_ops, sizeof(sof_imx_ops));
280
281 /* then set common imx8 ops */
282 sof_imx8_ops.dbg_dump = imx8_dump;
283 sof_imx8_ops.dsp_arch_ops = &sof_xtensa_arch_ops;
284 sof_imx8_ops.debugfs_add_region_item =
285 snd_sof_debugfs_add_region_item_iomem;
286
287 /* ... and finally set DAI driver */
288 sof_imx8_ops.drv = get_chip_info(sdev)->drv;
289 sof_imx8_ops.num_drv = get_chip_info(sdev)->num_drv;
290
291 return 0;
292 }
293
294 static const struct imx_chip_ops imx8_chip_ops = {
295 .probe = imx8_probe,
296 .core_kick = imx8_run,
297 .core_shutdown = imx8_shutdown,
298 };
299
300 static const struct imx_chip_ops imx8x_chip_ops = {
301 .probe = imx8_probe,
302 .core_kick = imx8x_run,
303 .core_shutdown = imx8_shutdown,
304 };
305
306 static const struct imx_chip_ops imx8m_chip_ops = {
307 .probe = imx8m_probe,
308 .core_kick = imx8m_run,
309 .core_reset = imx8m_reset,
310 };
311
312 static const struct imx_chip_ops imx8ulp_chip_ops = {
313 .probe = imx8ulp_probe,
314 .core_kick = imx8ulp_run,
315 .core_reset = imx8ulp_reset,
316 };
317
318 static struct imx_memory_info imx8_memory_regions[] = {
319 { .name = "iram", .reserved = false },
320 { .name = "sram", .reserved = true },
321 { }
322 };
323
324 static struct imx_memory_info imx8m_memory_regions[] = {
325 { .name = "iram", .reserved = false },
326 { .name = "sram", .reserved = true },
327 { }
328 };
329
330 static struct imx_memory_info imx8ulp_memory_regions[] = {
331 { .name = "iram", .reserved = false },
332 { .name = "sram", .reserved = true },
333 { }
334 };
335
336 static const struct imx_chip_info imx8_chip_info = {
337 .ipc_info = {
338 .has_panic_code = true,
339 .boot_mbox_offset = 0x800000,
340 .window_offset = 0x800000,
341 },
342 .memory = imx8_memory_regions,
343 .drv = imx8_dai,
344 .num_drv = ARRAY_SIZE(imx8_dai),
345 .ops = &imx8_chip_ops,
346 };
347
348 static const struct imx_chip_info imx8x_chip_info = {
349 .ipc_info = {
350 .has_panic_code = true,
351 .boot_mbox_offset = 0x800000,
352 .window_offset = 0x800000,
353 },
354 .memory = imx8_memory_regions,
355 .drv = imx8_dai,
356 .num_drv = ARRAY_SIZE(imx8_dai),
357 .ops = &imx8x_chip_ops,
358 };
359
360 static const struct imx_chip_info imx8m_chip_info = {
361 .ipc_info = {
362 .has_panic_code = true,
363 .boot_mbox_offset = 0x800000,
364 .window_offset = 0x800000,
365 },
366 .memory = imx8m_memory_regions,
367 .drv = imx8m_dai,
368 .num_drv = ARRAY_SIZE(imx8m_dai),
369 .ops = &imx8m_chip_ops,
370 };
371
372 static const struct imx_chip_info imx8ulp_chip_info = {
373 .ipc_info = {
374 .has_panic_code = true,
375 .boot_mbox_offset = 0x800000,
376 .window_offset = 0x800000,
377 },
378 .has_dma_reserved = true,
379 .memory = imx8ulp_memory_regions,
380 .drv = imx8ulp_dai,
381 .num_drv = ARRAY_SIZE(imx8ulp_dai),
382 .ops = &imx8ulp_chip_ops,
383 };
384
385 static struct snd_sof_of_mach sof_imx8_machs[] = {
386 {
387 .compatible = "fsl,imx8qxp-mek",
388 .sof_tplg_filename = "sof-imx8-wm8960.tplg",
389 .drv_name = "asoc-audio-graph-card2",
390 },
391 {
392 .compatible = "fsl,imx8qxp-mek-wcpu",
393 .sof_tplg_filename = "sof-imx8-wm8962.tplg",
394 .drv_name = "asoc-audio-graph-card2",
395 },
396 {
397 .compatible = "fsl,imx8qm-mek",
398 .sof_tplg_filename = "sof-imx8-wm8960.tplg",
399 .drv_name = "asoc-audio-graph-card2",
400 },
401 {
402 .compatible = "fsl,imx8qm-mek-revd",
403 .sof_tplg_filename = "sof-imx8-wm8962.tplg",
404 .drv_name = "asoc-audio-graph-card2",
405 },
406 {
407 .compatible = "fsl,imx8qxp-mek-bb",
408 .sof_tplg_filename = "sof-imx8-cs42888.tplg",
409 .drv_name = "asoc-audio-graph-card2",
410 },
411 {
412 .compatible = "fsl,imx8qm-mek-bb",
413 .sof_tplg_filename = "sof-imx8-cs42888.tplg",
414 .drv_name = "asoc-audio-graph-card2",
415 },
416 {
417 .compatible = "fsl,imx8mp-evk",
418 .sof_tplg_filename = "sof-imx8mp-wm8960.tplg",
419 .drv_name = "asoc-audio-graph-card2",
420 },
421 {
422 .compatible = "fsl,imx8mp-evk-revb4",
423 .sof_tplg_filename = "sof-imx8mp-wm8962.tplg",
424 .drv_name = "asoc-audio-graph-card2",
425 },
426 {
427 .compatible = "fsl,imx8ulp-evk",
428 .sof_tplg_filename = "sof-imx8ulp-btsco.tplg",
429 .drv_name = "asoc-audio-graph-card2",
430 },
431 {}
432 };
433
434 IMX_SOF_DEV_DESC(imx8, sof_imx8_machs, &imx8_chip_info, &sof_imx8_ops, imx8_ops_init);
435 IMX_SOF_DEV_DESC(imx8x, sof_imx8_machs, &imx8x_chip_info, &sof_imx8_ops, imx8_ops_init);
436 IMX_SOF_DEV_DESC(imx8m, sof_imx8_machs, &imx8m_chip_info, &sof_imx8_ops, imx8_ops_init);
437 IMX_SOF_DEV_DESC(imx8ulp, sof_imx8_machs, &imx8ulp_chip_info, &sof_imx8_ops, imx8_ops_init);
438
439 static const struct of_device_id sof_of_imx8_ids[] = {
440 {
441 .compatible = "fsl,imx8qxp-dsp",
442 .data = &IMX_SOF_DEV_DESC_NAME(imx8x),
443 },
444 {
445 .compatible = "fsl,imx8qm-dsp",
446 .data = &IMX_SOF_DEV_DESC_NAME(imx8),
447 },
448 {
449 .compatible = "fsl,imx8mp-dsp",
450 .data = &IMX_SOF_DEV_DESC_NAME(imx8m),
451 },
452 {
453 .compatible = "fsl,imx8ulp-dsp",
454 .data = &IMX_SOF_DEV_DESC_NAME(imx8ulp),
455 },
456 { }
457 };
458 MODULE_DEVICE_TABLE(of, sof_of_imx8_ids);
459
460 /* DT driver definition */
461 static struct platform_driver snd_sof_of_imx8_driver = {
462 .probe = sof_of_probe,
463 .remove = sof_of_remove,
464 .driver = {
465 .name = "sof-audio-of-imx8",
466 .pm = pm_ptr(&sof_of_pm),
467 .of_match_table = sof_of_imx8_ids,
468 },
469 };
470 module_platform_driver(snd_sof_of_imx8_driver);
471
472 MODULE_LICENSE("Dual BSD/GPL");
473 MODULE_DESCRIPTION("SOF support for IMX8 platforms");
474 MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA");
475