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 7 // 8 // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com> 9 // 10 // Generic firmware loader. 11 // 12 13 #include <linux/firmware.h> 14 #include "sof-priv.h" 15 #include "ops.h" 16 17 int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev) 18 { 19 struct snd_sof_pdata *plat_data = sdev->pdata; 20 const char *fw_filename; 21 ssize_t ext_man_size; 22 int ret; 23 24 /* Don't request firmware again if firmware is already requested */ 25 if (sdev->basefw.fw) 26 return 0; 27 28 fw_filename = kasprintf(GFP_KERNEL, "%s/%s", 29 plat_data->fw_filename_prefix, 30 plat_data->fw_filename); 31 if (!fw_filename) 32 return -ENOMEM; 33 34 ret = request_firmware(&sdev->basefw.fw, fw_filename, sdev->dev); 35 36 if (ret < 0) { 37 dev_err(sdev->dev, 38 "error: sof firmware file is missing, you might need to\n"); 39 dev_err(sdev->dev, 40 " download it from https://github.com/thesofproject/sof-bin/\n"); 41 goto err; 42 } else { 43 dev_dbg(sdev->dev, "request_firmware %s successful\n", 44 fw_filename); 45 } 46 47 /* check for extended manifest */ 48 ext_man_size = sdev->ipc->ops->fw_loader->parse_ext_manifest(sdev); 49 if (ext_man_size > 0) { 50 /* when no error occurred, drop extended manifest */ 51 sdev->basefw.payload_offset = ext_man_size; 52 } else if (!ext_man_size) { 53 /* No extended manifest, so nothing to skip during FW load */ 54 dev_dbg(sdev->dev, "firmware doesn't contain extended manifest\n"); 55 } else { 56 ret = ext_man_size; 57 dev_err(sdev->dev, "error: firmware %s contains unsupported or invalid extended manifest: %d\n", 58 fw_filename, ret); 59 } 60 61 err: 62 kfree(fw_filename); 63 64 return ret; 65 } 66 EXPORT_SYMBOL(snd_sof_load_firmware_raw); 67 68 int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev) 69 { 70 int ret; 71 72 ret = snd_sof_load_firmware_raw(sdev); 73 if (ret < 0) 74 return ret; 75 76 /* make sure the FW header and file is valid */ 77 ret = sdev->ipc->ops->fw_loader->validate(sdev); 78 if (ret < 0) { 79 dev_err(sdev->dev, "error: invalid FW header\n"); 80 goto error; 81 } 82 83 /* prepare the DSP for FW loading */ 84 ret = snd_sof_dsp_reset(sdev); 85 if (ret < 0) { 86 dev_err(sdev->dev, "error: failed to reset DSP\n"); 87 goto error; 88 } 89 90 /* parse and load firmware modules to DSP */ 91 if (sdev->ipc->ops->fw_loader->load_fw_to_dsp) { 92 ret = sdev->ipc->ops->fw_loader->load_fw_to_dsp(sdev); 93 if (ret < 0) { 94 dev_err(sdev->dev, "Firmware loading failed\n"); 95 goto error; 96 } 97 } 98 99 return 0; 100 101 error: 102 release_firmware(sdev->basefw.fw); 103 sdev->basefw.fw = NULL; 104 return ret; 105 106 } 107 EXPORT_SYMBOL(snd_sof_load_firmware_memcpy); 108 109 int snd_sof_run_firmware(struct snd_sof_dev *sdev) 110 { 111 int ret; 112 113 init_waitqueue_head(&sdev->boot_wait); 114 115 /* (re-)enable dsp dump */ 116 sdev->dbg_dump_printed = false; 117 sdev->ipc_dump_printed = false; 118 119 /* create read-only fw_version debugfs to store boot version info */ 120 if (sdev->first_boot) { 121 ret = snd_sof_debugfs_buf_item(sdev, &sdev->fw_version, 122 sizeof(sdev->fw_version), 123 "fw_version", 0444); 124 /* errors are only due to memory allocation, not debugfs */ 125 if (ret < 0) { 126 dev_err(sdev->dev, "snd_sof_debugfs_buf_item failed\n"); 127 return ret; 128 } 129 } 130 131 /* perform pre fw run operations */ 132 ret = snd_sof_dsp_pre_fw_run(sdev); 133 if (ret < 0) { 134 dev_err(sdev->dev, "failed pre fw run op\n"); 135 return ret; 136 } 137 138 dev_dbg(sdev->dev, "booting DSP firmware\n"); 139 140 /* boot the firmware on the DSP */ 141 ret = snd_sof_dsp_run(sdev); 142 if (ret < 0) { 143 snd_sof_dsp_dbg_dump(sdev, "Failed to start DSP", 144 SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_PCI); 145 return ret; 146 } 147 148 /* 149 * now wait for the DSP to boot. There are 3 possible outcomes: 150 * 1. Boot wait times out indicating FW boot failure. 151 * 2. FW boots successfully and fw_ready op succeeds. 152 * 3. FW boots but fw_ready op fails. 153 */ 154 ret = wait_event_timeout(sdev->boot_wait, 155 sdev->fw_state > SOF_FW_BOOT_IN_PROGRESS, 156 msecs_to_jiffies(sdev->boot_timeout)); 157 if (ret == 0) { 158 snd_sof_dsp_dbg_dump(sdev, "Firmware boot failure due to timeout", 159 SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX | 160 SOF_DBG_DUMP_TEXT | SOF_DBG_DUMP_PCI); 161 return -EIO; 162 } 163 164 if (sdev->fw_state == SOF_FW_BOOT_READY_FAILED) 165 return -EIO; /* FW boots but fw_ready op failed */ 166 167 dev_dbg(sdev->dev, "firmware boot complete\n"); 168 sof_set_fw_state(sdev, SOF_FW_BOOT_COMPLETE); 169 170 /* perform post fw run operations */ 171 ret = snd_sof_dsp_post_fw_run(sdev); 172 if (ret < 0) { 173 dev_err(sdev->dev, "error: failed post fw run op\n"); 174 return ret; 175 } 176 177 if (sdev->ipc->ops->post_fw_boot) 178 return sdev->ipc->ops->post_fw_boot(sdev); 179 180 return 0; 181 } 182 EXPORT_SYMBOL(snd_sof_run_firmware); 183 184 void snd_sof_fw_unload(struct snd_sof_dev *sdev) 185 { 186 /* TODO: support module unloading at runtime */ 187 release_firmware(sdev->basefw.fw); 188 sdev->basefw.fw = NULL; 189 } 190 EXPORT_SYMBOL(snd_sof_fw_unload); 191
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.