1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 2 // 3 // This file is provided under a dual BSD/GPLv2 license. When using or 4 // redistributing this file, you may do so under either license. 5 // 6 // Copyright(c) 2018 Intel Corporation. All rights reserved. 7 // 8 // Author: Liam Girdwood <[email protected]> 9 // 10 // Generic firmware loader. 11 // 12 13 #include <linux/firmware.h> 14 #include <sound/sof.h> 15 #include <sound/sof/ext_manifest.h> 16 #include "sof-priv.h" 17 #include "ops.h" 18 19 /* generic module parser for mmaped DSPs */ 20 int snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev, 21 struct snd_sof_mod_hdr *module) 22 { 23 struct snd_sof_blk_hdr *block; 24 int count, ret; 25 u32 offset; 26 size_t remaining; 27 28 dev_dbg(sdev->dev, "new module size 0x%x blocks 0x%x type 0x%x\n", 29 module->size, module->num_blocks, module->type); 30 31 block = (struct snd_sof_blk_hdr *)((u8 *)module + sizeof(*module)); 32 33 /* module->size doesn't include header size */ 34 remaining = module->size; 35 for (count = 0; count < module->num_blocks; count++) { 36 /* check for wrap */ 37 if (remaining < sizeof(*block)) { 38 dev_err(sdev->dev, "error: not enough data remaining\n"); 39 return -EINVAL; 40 } 41 42 /* minus header size of block */ 43 remaining -= sizeof(*block); 44 45 if (block->size == 0) { 46 dev_warn(sdev->dev, 47 "warning: block %d size zero\n", count); 48 dev_warn(sdev->dev, " type 0x%x offset 0x%x\n", 49 block->type, block->offset); 50 continue; 51 } 52 53 switch (block->type) { 54 case SOF_FW_BLK_TYPE_RSRVD0: 55 case SOF_FW_BLK_TYPE_ROM...SOF_FW_BLK_TYPE_RSRVD14: 56 continue; /* not handled atm */ 57 case SOF_FW_BLK_TYPE_IRAM: 58 case SOF_FW_BLK_TYPE_DRAM: 59 case SOF_FW_BLK_TYPE_SRAM: 60 offset = block->offset; 61 break; 62 default: 63 dev_err(sdev->dev, "error: bad type 0x%x for block 0x%x\n", 64 block->type, count); 65 return -EINVAL; 66 } 67 68 dev_dbg(sdev->dev, 69 "block %d type 0x%x size 0x%x ==> offset 0x%x\n", 70 count, block->type, block->size, offset); 71 72 /* checking block->size to avoid unaligned access */ 73 if (block->size % sizeof(u32)) { 74 dev_err(sdev->dev, "error: invalid block size 0x%x\n", 75 block->size); 76 return -EINVAL; 77 } 78 ret = snd_sof_dsp_block_write(sdev, block->type, offset, 79 block + 1, block->size); 80 if (ret < 0) { 81 dev_err(sdev->dev, "error: write to block type 0x%x failed\n", 82 block->type); 83 return ret; 84 } 85 86 if (remaining < block->size) { 87 dev_err(sdev->dev, "error: not enough data remaining\n"); 88 return -EINVAL; 89 } 90 91 /* minus body size of block */ 92 remaining -= block->size; 93 /* next block */ 94 block = (struct snd_sof_blk_hdr *)((u8 *)block + sizeof(*block) 95 + block->size); 96 } 97 98 return 0; 99 } 100 EXPORT_SYMBOL(snd_sof_parse_module_memcpy); 101 102 int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev) 103 { 104 struct snd_sof_pdata *plat_data = sdev->pdata; 105 const char *fw_filename; 106 ssize_t ext_man_size; 107 int ret; 108 109 /* Don't request firmware again if firmware is already requested */ 110 if (plat_data->fw) 111 return 0; 112 113 fw_filename = kasprintf(GFP_KERNEL, "%s/%s", 114 plat_data->fw_filename_prefix, 115 plat_data->fw_filename); 116 if (!fw_filename) 117 return -ENOMEM; 118 119 ret = request_firmware(&plat_data->fw, fw_filename, sdev->dev); 120 121 if (ret < 0) { 122 dev_err(sdev->dev, 123 "error: sof firmware file is missing, you might need to\n"); 124 dev_err(sdev->dev, 125 " download it from https://github.com/thesofproject/sof-bin/\n"); 126 goto err; 127 } else { 128 dev_dbg(sdev->dev, "request_firmware %s successful\n", 129 fw_filename); 130 } 131 132 /* check for extended manifest */ 133 ext_man_size = sdev->ipc->ops->fw_loader->parse_ext_manifest(sdev); 134 if (ext_man_size > 0) { 135 /* when no error occurred, drop extended manifest */ 136 plat_data->fw_offset = ext_man_size; 137 } else if (!ext_man_size) { 138 /* No extended manifest, so nothing to skip during FW load */ 139 dev_dbg(sdev->dev, "firmware doesn't contain extended manifest\n"); 140 } else { 141 ret = ext_man_size; 142 dev_err(sdev->dev, "error: firmware %s contains unsupported or invalid extended manifest: %d\n", 143 fw_filename, ret); 144 } 145 146 err: 147 kfree(fw_filename); 148 149 return ret; 150 } 151 EXPORT_SYMBOL(snd_sof_load_firmware_raw); 152 153 int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev) 154 { 155 struct snd_sof_pdata *plat_data = sdev->pdata; 156 int ret; 157 158 ret = snd_sof_load_firmware_raw(sdev); 159 if (ret < 0) 160 return ret; 161 162 /* make sure the FW header and file is valid */ 163 ret = sdev->ipc->ops->fw_loader->validate(sdev); 164 if (ret < 0) { 165 dev_err(sdev->dev, "error: invalid FW header\n"); 166 goto error; 167 } 168 169 /* prepare the DSP for FW loading */ 170 ret = snd_sof_dsp_reset(sdev); 171 if (ret < 0) { 172 dev_err(sdev->dev, "error: failed to reset DSP\n"); 173 goto error; 174 } 175 176 /* parse and load firmware modules to DSP */ 177 if (sdev->ipc->ops->fw_loader->load_fw_to_dsp) { 178 ret = sdev->ipc->ops->fw_loader->load_fw_to_dsp(sdev); 179 if (ret < 0) { 180 dev_err(sdev->dev, "Firmware loading failed\n"); 181 goto error; 182 } 183 } 184 185 return 0; 186 187 error: 188 release_firmware(plat_data->fw); 189 plat_data->fw = NULL; 190 return ret; 191 192 } 193 EXPORT_SYMBOL(snd_sof_load_firmware_memcpy); 194 195 int snd_sof_run_firmware(struct snd_sof_dev *sdev) 196 { 197 int ret; 198 199 init_waitqueue_head(&sdev->boot_wait); 200 201 /* (re-)enable dsp dump */ 202 sdev->dbg_dump_printed = false; 203 sdev->ipc_dump_printed = false; 204 205 /* create read-only fw_version debugfs to store boot version info */ 206 if (sdev->first_boot) { 207 ret = snd_sof_debugfs_buf_item(sdev, &sdev->fw_version, 208 sizeof(sdev->fw_version), 209 "fw_version", 0444); 210 /* errors are only due to memory allocation, not debugfs */ 211 if (ret < 0) { 212 dev_err(sdev->dev, "error: snd_sof_debugfs_buf_item failed\n"); 213 return ret; 214 } 215 } 216 217 /* perform pre fw run operations */ 218 ret = snd_sof_dsp_pre_fw_run(sdev); 219 if (ret < 0) { 220 dev_err(sdev->dev, "error: failed pre fw run op\n"); 221 return ret; 222 } 223 224 dev_dbg(sdev->dev, "booting DSP firmware\n"); 225 226 /* boot the firmware on the DSP */ 227 ret = snd_sof_dsp_run(sdev); 228 if (ret < 0) { 229 snd_sof_dsp_dbg_dump(sdev, "Failed to start DSP", 230 SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_PCI); 231 return ret; 232 } 233 234 /* 235 * now wait for the DSP to boot. There are 3 possible outcomes: 236 * 1. Boot wait times out indicating FW boot failure. 237 * 2. FW boots successfully and fw_ready op succeeds. 238 * 3. FW boots but fw_ready op fails. 239 */ 240 ret = wait_event_timeout(sdev->boot_wait, 241 sdev->fw_state > SOF_FW_BOOT_IN_PROGRESS, 242 msecs_to_jiffies(sdev->boot_timeout)); 243 if (ret == 0) { 244 snd_sof_dsp_dbg_dump(sdev, "Firmware boot failure due to timeout", 245 SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX | 246 SOF_DBG_DUMP_TEXT | SOF_DBG_DUMP_PCI); 247 return -EIO; 248 } 249 250 if (sdev->fw_state == SOF_FW_BOOT_READY_FAILED) 251 return -EIO; /* FW boots but fw_ready op failed */ 252 253 /* perform post fw run operations */ 254 ret = snd_sof_dsp_post_fw_run(sdev); 255 if (ret < 0) { 256 dev_err(sdev->dev, "error: failed post fw run op\n"); 257 return ret; 258 } 259 260 dev_dbg(sdev->dev, "firmware boot complete\n"); 261 sof_set_fw_state(sdev, SOF_FW_BOOT_COMPLETE); 262 263 return 0; 264 } 265 EXPORT_SYMBOL(snd_sof_run_firmware); 266 267 void snd_sof_fw_unload(struct snd_sof_dev *sdev) 268 { 269 /* TODO: support module unloading at runtime */ 270 release_firmware(sdev->pdata->fw); 271 sdev->pdata->fw = NULL; 272 } 273 EXPORT_SYMBOL(snd_sof_fw_unload); 274