xref: /linux/sound/soc/sof/imx/imx8.c (revision 299f489f5bad3554531f67335d1762225448ff39)
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