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