File Index
Files and directories
Sign in
FILE

imx8.c

File preview with syntax highlighting.

linux-6.19.14/sound/soc/sof/imx/imx8.c C
  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, 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, 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
 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, 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
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(&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
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(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
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(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(sdev->dev, sizeof(*chip), GFP_KERNEL);
173	if (!chip)
174		return -ENOMEM;
175
176	chip->dap = devm_ioremap(sdev->dev, IMX8M_DAP_DEBUG, IMX8M_DAP_DEBUG_SIZE);
177	if (!chip->dap)
178		return dev_err_probe(sdev->dev, -ENODEV,
179				     "failed to ioremap DAP\n");
180
181	chip->run_stall = devm_reset_control_get_exclusive(sdev->dev, "runstall");
182	if (IS_ERR(chip->run_stall))
183		return dev_err_probe(sdev->dev, PTR_ERR(chip->run_stall),
184				     "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(regmap, SYSCTRL0, RESET_BIT, 0);
197
198	/* Reset HiFi4 DSP Debug logic: 1 debug reset, 0  out of reset*/
199	regmap_update_bits(regmap, SYSCTRL0, DEBUG_LOGIC_BIT, 0);
200
201	/* Stall HIFI4 DSP Execution: 1 stall, 0 run */
202	regmap_update_bits(regmap, SYSCTRL0, EXECUTE_BIT, 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(regmap, SYSCTRL0, PLAT_CLK_BIT, PLAT_CLK_BIT);
216
217	/* HiFi4 PBCLK clock enable: 1 enabled, 0 disabled */
218	regmap_update_bits(regmap, SYSCTRL0, PB_CLK_BIT, PB_CLK_BIT);
219
220	/* HiFi4 Clock Enable: 1 enabled, 0 disabled */
221	regmap_update_bits(regmap, SYSCTRL0, HIFI4_CLK_BIT, HIFI4_CLK_BIT);
222
223	regmap_update_bits(regmap, SYSCTRL0, RESET_BIT, RESET_BIT);
224
225	usleep_range(1, 2);
226
227	/* Stall HIFI4 DSP Execution: 1 stall, 0 not stall */
228	regmap_update_bits(regmap, SYSCTRL0, EXECUTE_BIT, EXECUTE_BIT);
229	usleep_range(1, 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(sdev->dev->of_node, "fsl,dsp-ctrl");
244	if (IS_ERR(regmap))
245		return dev_err_probe(sdev->dev, PTR_ERR(regmap),
246				     "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");
~/ Read-only file preview.