loader.c (5070B)
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 <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 17int 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 (plat_data->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(&plat_data->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 plat_data->fw_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 61err: 62 kfree(fw_filename); 63 64 return ret; 65} 66EXPORT_SYMBOL(snd_sof_load_firmware_raw); 67 68int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev) 69{ 70 struct snd_sof_pdata *plat_data = sdev->pdata; 71 int ret; 72 73 ret = snd_sof_load_firmware_raw(sdev); 74 if (ret < 0) 75 return ret; 76 77 /* make sure the FW header and file is valid */ 78 ret = sdev->ipc->ops->fw_loader->validate(sdev); 79 if (ret < 0) { 80 dev_err(sdev->dev, "error: invalid FW header\n"); 81 goto error; 82 } 83 84 /* prepare the DSP for FW loading */ 85 ret = snd_sof_dsp_reset(sdev); 86 if (ret < 0) { 87 dev_err(sdev->dev, "error: failed to reset DSP\n"); 88 goto error; 89 } 90 91 /* parse and load firmware modules to DSP */ 92 if (sdev->ipc->ops->fw_loader->load_fw_to_dsp) { 93 ret = sdev->ipc->ops->fw_loader->load_fw_to_dsp(sdev); 94 if (ret < 0) { 95 dev_err(sdev->dev, "Firmware loading failed\n"); 96 goto error; 97 } 98 } 99 100 return 0; 101 102error: 103 release_firmware(plat_data->fw); 104 plat_data->fw = NULL; 105 return ret; 106 107} 108EXPORT_SYMBOL(snd_sof_load_firmware_memcpy); 109 110int snd_sof_run_firmware(struct snd_sof_dev *sdev) 111{ 112 int ret; 113 114 init_waitqueue_head(&sdev->boot_wait); 115 116 /* (re-)enable dsp dump */ 117 sdev->dbg_dump_printed = false; 118 sdev->ipc_dump_printed = false; 119 120 /* create read-only fw_version debugfs to store boot version info */ 121 if (sdev->first_boot) { 122 ret = snd_sof_debugfs_buf_item(sdev, &sdev->fw_version, 123 sizeof(sdev->fw_version), 124 "fw_version", 0444); 125 /* errors are only due to memory allocation, not debugfs */ 126 if (ret < 0) { 127 dev_err(sdev->dev, "error: snd_sof_debugfs_buf_item failed\n"); 128 return ret; 129 } 130 } 131 132 /* perform pre fw run operations */ 133 ret = snd_sof_dsp_pre_fw_run(sdev); 134 if (ret < 0) { 135 dev_err(sdev->dev, "error: failed pre fw run op\n"); 136 return ret; 137 } 138 139 dev_dbg(sdev->dev, "booting DSP firmware\n"); 140 141 /* boot the firmware on the DSP */ 142 ret = snd_sof_dsp_run(sdev); 143 if (ret < 0) { 144 snd_sof_dsp_dbg_dump(sdev, "Failed to start DSP", 145 SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_PCI); 146 return ret; 147 } 148 149 /* 150 * now wait for the DSP to boot. There are 3 possible outcomes: 151 * 1. Boot wait times out indicating FW boot failure. 152 * 2. FW boots successfully and fw_ready op succeeds. 153 * 3. FW boots but fw_ready op fails. 154 */ 155 ret = wait_event_timeout(sdev->boot_wait, 156 sdev->fw_state > SOF_FW_BOOT_IN_PROGRESS, 157 msecs_to_jiffies(sdev->boot_timeout)); 158 if (ret == 0) { 159 snd_sof_dsp_dbg_dump(sdev, "Firmware boot failure due to timeout", 160 SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX | 161 SOF_DBG_DUMP_TEXT | SOF_DBG_DUMP_PCI); 162 return -EIO; 163 } 164 165 if (sdev->fw_state == SOF_FW_BOOT_READY_FAILED) 166 return -EIO; /* FW boots but fw_ready op failed */ 167 168 /* perform post fw run operations */ 169 ret = snd_sof_dsp_post_fw_run(sdev); 170 if (ret < 0) { 171 dev_err(sdev->dev, "error: failed post fw run op\n"); 172 return ret; 173 } 174 175 dev_dbg(sdev->dev, "firmware boot complete\n"); 176 sof_set_fw_state(sdev, SOF_FW_BOOT_COMPLETE); 177 178 if (sdev->first_boot && sdev->ipc->ops->fw_loader->query_fw_configuration) 179 return sdev->ipc->ops->fw_loader->query_fw_configuration(sdev); 180 181 return 0; 182} 183EXPORT_SYMBOL(snd_sof_run_firmware); 184 185void snd_sof_fw_unload(struct snd_sof_dev *sdev) 186{ 187 /* TODO: support module unloading at runtime */ 188 release_firmware(sdev->pdata->fw); 189 sdev->pdata->fw = NULL; 190} 191EXPORT_SYMBOL(snd_sof_fw_unload);