|
9 | 9 | #include <linux/firmware.h> |
10 | 10 | #include <linux/module.h> |
11 | 11 | #include <linux/slab.h> |
| 12 | +#include <sound/hdaudio_ext.h> |
12 | 13 | #include "avs.h" |
| 14 | +#include "cldma.h" |
13 | 15 | #include "messages.h" |
14 | 16 | #include "registers.h" |
15 | 17 |
|
| 18 | +#define AVS_ROM_STS_MASK 0xFF |
| 19 | +#define AVS_ROM_INIT_DONE 0x1 |
| 20 | +#define SKL_ROM_BASEFW_ENTERED 0xF |
| 21 | +#define AVS_ROM_INIT_POLLING_US 5 |
| 22 | +#define SKL_ROM_INIT_TIMEOUT_US 1000000 |
| 23 | + |
| 24 | +#define AVS_FW_INIT_POLLING_US 500 |
| 25 | +#define AVS_FW_INIT_TIMEOUT_US 3000000 |
16 | 26 | #define AVS_FW_INIT_TIMEOUT_MS 3000 |
17 | 27 |
|
| 28 | +#define AVS_CLDMA_START_DELAY_MS 100 |
| 29 | + |
18 | 30 | #define AVS_ROOT_DIR "intel/avs" |
19 | 31 | #define AVS_BASEFW_FILENAME "dsp_basefw.bin" |
20 | 32 | #define AVS_EXT_MANIFEST_MAGIC 0x31454124 |
@@ -111,6 +123,144 @@ static int avs_fw_manifest_strip_verify(struct avs_dev *adev, struct firmware *f |
111 | 123 | return 0; |
112 | 124 | } |
113 | 125 |
|
| 126 | +int avs_cldma_load_basefw(struct avs_dev *adev, struct firmware *fw) |
| 127 | +{ |
| 128 | + struct hda_cldma *cl = &code_loader; |
| 129 | + unsigned int reg; |
| 130 | + int ret; |
| 131 | + |
| 132 | + ret = avs_dsp_op(adev, power, AVS_MAIN_CORE_MASK, true); |
| 133 | + if (ret < 0) |
| 134 | + return ret; |
| 135 | + |
| 136 | + ret = avs_dsp_op(adev, reset, AVS_MAIN_CORE_MASK, false); |
| 137 | + if (ret < 0) |
| 138 | + return ret; |
| 139 | + |
| 140 | + ret = hda_cldma_reset(cl); |
| 141 | + if (ret < 0) { |
| 142 | + dev_err(adev->dev, "cldma reset failed: %d\n", ret); |
| 143 | + return ret; |
| 144 | + } |
| 145 | + hda_cldma_setup(cl); |
| 146 | + |
| 147 | + ret = avs_dsp_op(adev, stall, AVS_MAIN_CORE_MASK, false); |
| 148 | + if (ret < 0) |
| 149 | + return ret; |
| 150 | + |
| 151 | + reinit_completion(&adev->fw_ready); |
| 152 | + avs_dsp_op(adev, int_control, true); |
| 153 | + |
| 154 | + /* await ROM init */ |
| 155 | + ret = snd_hdac_adsp_readl_poll(adev, AVS_FW_REG_STATUS(adev), reg, |
| 156 | + (reg & AVS_ROM_INIT_DONE) == AVS_ROM_INIT_DONE, |
| 157 | + AVS_ROM_INIT_POLLING_US, SKL_ROM_INIT_TIMEOUT_US); |
| 158 | + if (ret < 0) { |
| 159 | + dev_err(adev->dev, "rom init timeout: %d\n", ret); |
| 160 | + avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK); |
| 161 | + return ret; |
| 162 | + } |
| 163 | + |
| 164 | + hda_cldma_set_data(cl, (void *)fw->data, fw->size); |
| 165 | + /* transfer firmware */ |
| 166 | + hda_cldma_transfer(cl, 0); |
| 167 | + ret = snd_hdac_adsp_readl_poll(adev, AVS_FW_REG_STATUS(adev), reg, |
| 168 | + (reg & AVS_ROM_STS_MASK) == SKL_ROM_BASEFW_ENTERED, |
| 169 | + AVS_FW_INIT_POLLING_US, AVS_FW_INIT_TIMEOUT_US); |
| 170 | + hda_cldma_stop(cl); |
| 171 | + if (ret < 0) { |
| 172 | + dev_err(adev->dev, "transfer fw failed: %d\n", ret); |
| 173 | + avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK); |
| 174 | + return ret; |
| 175 | + } |
| 176 | + |
| 177 | + return 0; |
| 178 | +} |
| 179 | + |
| 180 | +int avs_cldma_load_library(struct avs_dev *adev, struct firmware *lib, u32 id) |
| 181 | +{ |
| 182 | + struct hda_cldma *cl = &code_loader; |
| 183 | + int ret; |
| 184 | + |
| 185 | + hda_cldma_set_data(cl, (void *)lib->data, lib->size); |
| 186 | + /* transfer modules manifest */ |
| 187 | + hda_cldma_transfer(cl, msecs_to_jiffies(AVS_CLDMA_START_DELAY_MS)); |
| 188 | + |
| 189 | + /* DMA id ignored as there is only ever one code-loader DMA */ |
| 190 | + ret = avs_ipc_load_library(adev, 0, id); |
| 191 | + hda_cldma_stop(cl); |
| 192 | + |
| 193 | + if (ret) { |
| 194 | + ret = AVS_IPC_RET(ret); |
| 195 | + dev_err(adev->dev, "transfer lib %d failed: %d\n", id, ret); |
| 196 | + } |
| 197 | + |
| 198 | + return ret; |
| 199 | +} |
| 200 | + |
| 201 | +static int avs_cldma_load_module(struct avs_dev *adev, struct avs_module_entry *mentry) |
| 202 | +{ |
| 203 | + struct hda_cldma *cl = &code_loader; |
| 204 | + const struct firmware *mod; |
| 205 | + char *mod_name; |
| 206 | + int ret; |
| 207 | + |
| 208 | + mod_name = kasprintf(GFP_KERNEL, "%s/%s/dsp_mod_%pUL.bin", AVS_ROOT_DIR, |
| 209 | + adev->spec->name, mentry->uuid.b); |
| 210 | + if (!mod_name) |
| 211 | + return -ENOMEM; |
| 212 | + |
| 213 | + ret = avs_request_firmware(adev, &mod, mod_name); |
| 214 | + kfree(mod_name); |
| 215 | + if (ret < 0) |
| 216 | + return ret; |
| 217 | + |
| 218 | + hda_cldma_set_data(cl, (void *)mod->data, mod->size); |
| 219 | + hda_cldma_transfer(cl, msecs_to_jiffies(AVS_CLDMA_START_DELAY_MS)); |
| 220 | + ret = avs_ipc_load_modules(adev, &mentry->module_id, 1); |
| 221 | + hda_cldma_stop(cl); |
| 222 | + |
| 223 | + if (ret) { |
| 224 | + dev_err(adev->dev, "load module %d failed: %d\n", mentry->module_id, ret); |
| 225 | + avs_release_last_firmware(adev); |
| 226 | + return AVS_IPC_RET(ret); |
| 227 | + } |
| 228 | + |
| 229 | + return 0; |
| 230 | +} |
| 231 | + |
| 232 | +int avs_cldma_transfer_modules(struct avs_dev *adev, bool load, |
| 233 | + struct avs_module_entry *mods, u32 num_mods) |
| 234 | +{ |
| 235 | + u16 *mod_ids; |
| 236 | + int ret, i; |
| 237 | + |
| 238 | + /* Either load to DSP or unload them to free space. */ |
| 239 | + if (load) { |
| 240 | + for (i = 0; i < num_mods; i++) { |
| 241 | + ret = avs_cldma_load_module(adev, &mods[i]); |
| 242 | + if (ret) |
| 243 | + return ret; |
| 244 | + } |
| 245 | + |
| 246 | + return 0; |
| 247 | + } |
| 248 | + |
| 249 | + mod_ids = kcalloc(num_mods, sizeof(u16), GFP_KERNEL); |
| 250 | + if (!mod_ids) |
| 251 | + return -ENOMEM; |
| 252 | + |
| 253 | + for (i = 0; i < num_mods; i++) |
| 254 | + mod_ids[i] = mods[i].module_id; |
| 255 | + |
| 256 | + ret = avs_ipc_unload_modules(adev, mod_ids, num_mods); |
| 257 | + kfree(mod_ids); |
| 258 | + if (ret) |
| 259 | + return AVS_IPC_RET(ret); |
| 260 | + |
| 261 | + return 0; |
| 262 | +} |
| 263 | + |
114 | 264 | static int avs_dsp_load_basefw(struct avs_dev *adev) |
115 | 265 | { |
116 | 266 | const struct avs_fw_version *min_req; |
@@ -195,6 +345,15 @@ int avs_dsp_first_boot_firmware(struct avs_dev *adev) |
195 | 345 | { |
196 | 346 | int ret, i; |
197 | 347 |
|
| 348 | + if (avs_platattr_test(adev, CLDMA)) { |
| 349 | + ret = hda_cldma_init(&code_loader, &adev->base.core, |
| 350 | + adev->dsp_ba, AVS_CL_DEFAULT_BUFFER_SIZE); |
| 351 | + if (ret < 0) { |
| 352 | + dev_err(adev->dev, "cldma init failed: %d\n", ret); |
| 353 | + return ret; |
| 354 | + } |
| 355 | + } |
| 356 | + |
198 | 357 | ret = avs_dsp_boot_firmware(adev, true); |
199 | 358 | if (ret < 0) { |
200 | 359 | dev_err(adev->dev, "firmware boot failed: %d\n", ret); |
|
0 commit comments