Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)//// This file is provided under a dual BSD/GPLv2 license. When using or// redistributing this file, you may do so under either license.//// Copyright(c) 2018 Intel Corporation//// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>//// Generic firmware loader.//
#include <linux/firmware.h>#include "sof-priv.h"#include "ops.h"
int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev){ struct snd_sof_pdata *plat_data = sdev->pdata; const char *fw_filename; ssize_t ext_man_size; int ret;
/* Don't request firmware again if firmware is already requested */ if (sdev->basefw.fw) return 0;
fw_filename = kasprintf(GFP_KERNEL, "%s/%s", plat_data->fw_filename_prefix, plat_data->fw_filename); if (!fw_filename) return -ENOMEM;
ret = request_firmware(&sdev->basefw.fw, fw_filename, sdev->dev);
if (ret < 0) { dev_err(sdev->dev, "error: sof firmware file is missing, you might need to\n"); dev_err(sdev->dev, " download it from https://github.com/thesofproject/sof-bin/\n"); goto err; } else { dev_dbg(sdev->dev, "request_firmware %s successful\n", fw_filename); }
/* check for extended manifest */ ext_man_size = sdev->ipc->ops->fw_loader->parse_ext_manifest(sdev); if (ext_man_size > 0) { /* when no error occurred, drop extended manifest */ sdev->basefw.payload_offset = ext_man_size; } else if (!ext_man_size) { /* No extended manifest, so nothing to skip during FW load */ dev_dbg(sdev->dev, "firmware doesn't contain extended manifest\n"); } else { ret = ext_man_size; dev_err(sdev->dev, "error: firmware %s contains unsupported or invalid extended manifest: %d\n", fw_filename, ret); }
err: kfree(fw_filename);
return ret;}EXPORT_SYMBOL(snd_sof_load_firmware_raw);
int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev){ int ret;
ret = snd_sof_load_firmware_raw(sdev); if (ret < 0) return ret;
/* make sure the FW header and file is valid */ ret = sdev->ipc->ops->fw_loader->validate(sdev); if (ret < 0) { dev_err(sdev->dev, "error: invalid FW header\n"); goto error; }
/* prepare the DSP for FW loading */ ret = snd_sof_dsp_reset(sdev); if (ret < 0) { dev_err(sdev->dev, "error: failed to reset DSP\n"); goto error; }
/* parse and load firmware modules to DSP */ if (sdev->ipc->ops->fw_loader->load_fw_to_dsp) { ret = sdev->ipc->ops->fw_loader->load_fw_to_dsp(sdev); if (ret < 0) { dev_err(sdev->dev, "Firmware loading failed\n"); goto error; } }
return 0;
error: release_firmware(sdev->basefw.fw); sdev->basefw.fw = NULL; return ret;
}EXPORT_SYMBOL(snd_sof_load_firmware_memcpy);
int snd_sof_run_firmware(struct snd_sof_dev *sdev){ int ret;
init_waitqueue_head(&sdev->boot_wait);
/* (re-)enable dsp dump */ sdev->dbg_dump_printed = false; sdev->ipc_dump_printed = false;
/* create read-only fw_version debugfs to store boot version info */ if (sdev->first_boot) { ret = snd_sof_debugfs_buf_item(sdev, &sdev->fw_version, sizeof(sdev->fw_version), "fw_version", 0444); /* errors are only due to memory allocation, not debugfs */ if (ret < 0) { dev_err(sdev->dev, "snd_sof_debugfs_buf_item failed\n"); return ret; } }
/* perform pre fw run operations */ ret = snd_sof_dsp_pre_fw_run(sdev); if (ret < 0) { dev_err(sdev->dev, "failed pre fw run op\n"); return ret; }
dev_dbg(sdev->dev, "booting DSP firmware\n");
/* boot the firmware on the DSP */ ret = snd_sof_dsp_run(sdev); if (ret < 0) { snd_sof_dsp_dbg_dump(sdev, "Failed to start DSP", SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_PCI); return ret; }
/* * now wait for the DSP to boot. There are 3 possible outcomes: * 1. Boot wait times out indicating FW boot failure. * 2. FW boots successfully and fw_ready op succeeds. * 3. FW boots but fw_ready op fails. */ ret = wait_event_timeout(sdev->boot_wait, sdev->fw_state > SOF_FW_BOOT_IN_PROGRESS, msecs_to_jiffies(sdev->boot_timeout)); if (ret == 0) { snd_sof_dsp_dbg_dump(sdev, "Firmware boot failure due to timeout", SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_TEXT | SOF_DBG_DUMP_PCI); return -EIO; }
if (sdev->fw_state == SOF_FW_BOOT_READY_FAILED) return -EIO; /* FW boots but fw_ready op failed */
dev_dbg(sdev->dev, "firmware boot complete\n"); sof_set_fw_state(sdev, SOF_FW_BOOT_COMPLETE);
/* perform post fw run operations */ ret = snd_sof_dsp_post_fw_run(sdev); if (ret < 0) { dev_err(sdev->dev, "error: failed post fw run op\n"); return ret; }
if (sdev->ipc->ops->post_fw_boot) return sdev->ipc->ops->post_fw_boot(sdev);
return 0;}EXPORT_SYMBOL(snd_sof_run_firmware);
void snd_sof_fw_unload(struct snd_sof_dev *sdev){ /* TODO: support module unloading at runtime */ release_firmware(sdev->basefw.fw); sdev->basefw.fw = NULL;}EXPORT_SYMBOL(snd_sof_fw_unload);