brintos

brintos / linux-shallow public Read only

0
0
Text · 4.8 KiB · 0baf316 Raw
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