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