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