190 lines · 4.8 KB · C
RawDownload
// 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);