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 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 */ 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 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 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 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 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 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 -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 191 static 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 207 static 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 236 static 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 253 static 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 258 static 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 268 static 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 273 static struct snd_sof_dsp_ops sof_imx8_ops; 274 275 static 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 293 static const struct imx_chip_ops imx8_chip_ops = { 294 .probe = imx8_probe, 295 .core_kick = imx8_run, 296 .core_shutdown = imx8_shutdown, 297 }; 298 299 static const struct imx_chip_ops imx8x_chip_ops = { 300 .probe = imx8_probe, 301 .core_kick = imx8x_run, 302 .core_shutdown = imx8_shutdown, 303 }; 304 305 static const struct imx_chip_ops imx8m_chip_ops = { 306 .probe = imx8m_probe, 307 .core_kick = imx8m_run, 308 .core_reset = imx8m_reset, 309 }; 310 311 static const struct imx_chip_ops imx8ulp_chip_ops = { 312 .probe = imx8ulp_probe, 313 .core_kick = imx8ulp_run, 314 .core_reset = imx8ulp_reset, 315 }; 316 317 static struct imx_memory_info imx8_memory_regions[] = { 318 { .name = "iram", .reserved = false }, 319 { .name = "sram", .reserved = true }, 320 { } 321 }; 322 323 static struct imx_memory_info imx8m_memory_regions[] = { 324 { .name = "iram", .reserved = false }, 325 { .name = "sram", .reserved = true }, 326 { } 327 }; 328 329 static struct imx_memory_info imx8ulp_memory_regions[] = { 330 { .name = "iram", .reserved = false }, 331 { .name = "sram", .reserved = true }, 332 { } 333 }; 334 335 static 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 347 static 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 359 static 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 371 static 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 384 static 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 433 IMX_SOF_DEV_DESC(imx8, sof_imx8_machs, &imx8_chip_info, &sof_imx8_ops, imx8_ops_init); 434 IMX_SOF_DEV_DESC(imx8x, sof_imx8_machs, &imx8x_chip_info, &sof_imx8_ops, imx8_ops_init); 435 IMX_SOF_DEV_DESC(imx8m, sof_imx8_machs, &imx8m_chip_info, &sof_imx8_ops, imx8_ops_init); 436 IMX_SOF_DEV_DESC(imx8ulp, sof_imx8_machs, &imx8ulp_chip_info, &sof_imx8_ops, imx8_ops_init); 437 438 static 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 }; 457 MODULE_DEVICE_TABLE(of, sof_of_imx8_ids); 458 459 /* DT driver definition */ 460 static 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 }; 469 module_platform_driver(snd_sof_of_imx8_driver); 470 471 MODULE_LICENSE("Dual BSD/GPL"); 472 MODULE_DESCRIPTION("SOF support for IMX8 platforms"); 473 MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA"); 474