191 lines · c
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 or4// redistributing this file, you may do so under either license.5//6// Copyright(c) 2018 Intel Corporation7//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 (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 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 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 101error:102 release_firmware(sdev->basefw.fw);103 sdev->basefw.fw = NULL;104 return ret;105 106}107EXPORT_SYMBOL(snd_sof_load_firmware_memcpy);108 109int 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}182EXPORT_SYMBOL(snd_sof_run_firmware);183 184void 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}190EXPORT_SYMBOL(snd_sof_fw_unload);191