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 <linux/firmware/imx/svc/misc.h> 10 #include <dt-bindings/firmware/imx/rsrc.h> 11 #include "imx-common.h" 12 13 #define RESET_VECTOR_VADDR 0x596f8000 14 15 /* 16 * DSP control. 17 */ 18 static int imx8x_run(struct snd_sof_dev *sdev) 19 { 20 int ret; 21 22 ret = imx_sc_misc_set_control(get_chip_pdata(sdev), IMX_SC_R_DSP, 23 IMX_SC_C_OFS_SEL, 1); 24 if (ret < 0) { 25 dev_err(sdev->dev, "Error system address offset source select\n"); 26 return ret; 27 } 28 29 ret = imx_sc_misc_set_control(get_chip_pdata(sdev), IMX_SC_R_DSP, 30 IMX_SC_C_OFS_AUDIO, 0x80); 31 if (ret < 0) { 32 dev_err(sdev->dev, "Error system address offset of AUDIO\n"); 33 return ret; 34 } 35 36 ret = imx_sc_misc_set_control(get_chip_pdata(sdev), IMX_SC_R_DSP, 37 IMX_SC_C_OFS_PERIPH, 0x5A); 38 if (ret < 0) { 39 dev_err(sdev->dev, "Error system address offset of PERIPH %d\n", 40 ret); 41 return ret; 42 } 43 44 ret = imx_sc_misc_set_control(get_chip_pdata(sdev), IMX_SC_R_DSP, 45 IMX_SC_C_OFS_IRQ, 0x51); 46 if (ret < 0) { 47 dev_err(sdev->dev, "Error system address offset of IRQ\n"); 48 return ret; 49 } 50 51 imx_sc_pm_cpu_start(get_chip_pdata(sdev), IMX_SC_R_DSP, true, 52 RESET_VECTOR_VADDR); 53 54 return 0; 55 } 56 57 static int imx8_run(struct snd_sof_dev *sdev) 58 { 59 int ret; 60 61 ret = imx_sc_misc_set_control(get_chip_pdata(sdev), IMX_SC_R_DSP, 62 IMX_SC_C_OFS_SEL, 0); 63 if (ret < 0) { 64 dev_err(sdev->dev, "Error system address offset source select\n"); 65 return ret; 66 } 67 68 imx_sc_pm_cpu_start(get_chip_pdata(sdev), IMX_SC_R_DSP, true, 69 RESET_VECTOR_VADDR); 70 71 return 0; 72 } 73 74 static int imx8_probe(struct snd_sof_dev *sdev) 75 { 76 struct imx_sc_ipc *sc_ipc_handle; 77 struct imx_common_data *common; 78 int ret; 79 80 common = sdev->pdata->hw_pdata; 81 82 ret = imx_scu_get_handle(&sc_ipc_handle); 83 if (ret < 0) 84 return dev_err_probe(sdev->dev, ret, 85 "failed to fetch SC IPC handle\n"); 86 87 common->chip_pdata = sc_ipc_handle; 88 89 return 0; 90 } 91 92 static struct snd_soc_dai_driver imx8_dai[] = { 93 IMX_SOF_DAI_DRV_ENTRY_BIDIR("esai0", 1, 8), 94 IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai1", 1, 32), 95 }; 96 97 static struct snd_sof_dsp_ops sof_imx8_ops; 98 99 static int imx8_ops_init(struct snd_sof_dev *sdev) 100 { 101 /* first copy from template */ 102 memcpy(&sof_imx8_ops, &sof_imx_ops, sizeof(sof_imx_ops)); 103 104 /* then set common imx8 ops */ 105 sof_imx8_ops.dbg_dump = imx8_dump; 106 sof_imx8_ops.dsp_arch_ops = &sof_xtensa_arch_ops; 107 sof_imx8_ops.debugfs_add_region_item = 108 snd_sof_debugfs_add_region_item_iomem; 109 110 /* ... and finally set DAI driver */ 111 sof_imx8_ops.drv = get_chip_info(sdev)->drv; 112 sof_imx8_ops.num_drv = get_chip_info(sdev)->num_drv; 113 114 return 0; 115 } 116 117 static const struct imx_chip_ops imx8_chip_ops = { 118 .probe = imx8_probe, 119 .core_kick = imx8_run, 120 }; 121 122 static const struct imx_chip_ops imx8x_chip_ops = { 123 .probe = imx8_probe, 124 .core_kick = imx8x_run, 125 }; 126 127 static struct imx_memory_info imx8_memory_regions[] = { 128 { .name = "iram", .reserved = false }, 129 { .name = "sram", .reserved = true }, 130 { } 131 }; 132 133 static const struct imx_chip_info imx8_chip_info = { 134 .ipc_info = { 135 .has_panic_code = true, 136 .boot_mbox_offset = 0x800000, 137 .window_offset = 0x800000, 138 }, 139 .memory = imx8_memory_regions, 140 .drv = imx8_dai, 141 .num_drv = ARRAY_SIZE(imx8_dai), 142 .ops = &imx8_chip_ops, 143 }; 144 145 static const struct imx_chip_info imx8x_chip_info = { 146 .ipc_info = { 147 .has_panic_code = true, 148 .boot_mbox_offset = 0x800000, 149 .window_offset = 0x800000, 150 }, 151 .memory = imx8_memory_regions, 152 .drv = imx8_dai, 153 .num_drv = ARRAY_SIZE(imx8_dai), 154 .ops = &imx8x_chip_ops, 155 }; 156 157 static struct snd_sof_of_mach sof_imx8_machs[] = { 158 { 159 .compatible = "fsl,imx8qxp-mek", 160 .sof_tplg_filename = "sof-imx8-wm8960.tplg", 161 .drv_name = "asoc-audio-graph-card2", 162 }, 163 { 164 .compatible = "fsl,imx8qxp-mek-wcpu", 165 .sof_tplg_filename = "sof-imx8-wm8962.tplg", 166 .drv_name = "asoc-audio-graph-card2", 167 }, 168 { 169 .compatible = "fsl,imx8qm-mek", 170 .sof_tplg_filename = "sof-imx8-wm8960.tplg", 171 .drv_name = "asoc-audio-graph-card2", 172 }, 173 { 174 .compatible = "fsl,imx8qm-mek-revd", 175 .sof_tplg_filename = "sof-imx8-wm8962.tplg", 176 .drv_name = "asoc-audio-graph-card2", 177 }, 178 { 179 .compatible = "fsl,imx8qxp-mek-bb", 180 .sof_tplg_filename = "sof-imx8-cs42888.tplg", 181 .drv_name = "asoc-audio-graph-card2", 182 }, 183 { 184 .compatible = "fsl,imx8qm-mek-bb", 185 .sof_tplg_filename = "sof-imx8-cs42888.tplg", 186 .drv_name = "asoc-audio-graph-card2", 187 }, 188 189 {} 190 }; 191 192 IMX_SOF_DEV_DESC(imx8, sof_imx8_machs, &imx8_chip_info, &sof_imx8_ops, imx8_ops_init); 193 IMX_SOF_DEV_DESC(imx8x, sof_imx8_machs, &imx8x_chip_info, &sof_imx8_ops, imx8_ops_init); 194 195 static const struct of_device_id sof_of_imx8_ids[] = { 196 { 197 .compatible = "fsl,imx8qxp-dsp", 198 .data = &IMX_SOF_DEV_DESC_NAME(imx8x), 199 }, 200 { 201 .compatible = "fsl,imx8qm-dsp", 202 .data = &IMX_SOF_DEV_DESC_NAME(imx8), 203 }, 204 { } 205 }; 206 MODULE_DEVICE_TABLE(of, sof_of_imx8_ids); 207 208 /* DT driver definition */ 209 static struct platform_driver snd_sof_of_imx8_driver = { 210 .probe = sof_of_probe, 211 .remove = sof_of_remove, 212 .driver = { 213 .name = "sof-audio-of-imx8", 214 .pm = &sof_of_pm, 215 .of_match_table = sof_of_imx8_ids, 216 }, 217 }; 218 module_platform_driver(snd_sof_of_imx8_driver); 219 220 MODULE_LICENSE("Dual BSD/GPL"); 221 MODULE_DESCRIPTION("SOF support for IMX8 platforms"); 222 MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA"); 223