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 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 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 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 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 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