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