xref: /linux/sound/soc/sof/loader.c (revision 33e02dc69afbd8f1b85a51d74d72f139ba4ca623)
1e149ca29SPierre-Louis Bossart // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
270cd5254SLiam Girdwood //
370cd5254SLiam Girdwood // This file is provided under a dual BSD/GPLv2 license.  When using or
470cd5254SLiam Girdwood // redistributing this file, you may do so under either license.
570cd5254SLiam Girdwood //
6*293ad281SPierre-Louis Bossart // Copyright(c) 2018 Intel Corporation
770cd5254SLiam Girdwood //
870cd5254SLiam Girdwood // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
970cd5254SLiam Girdwood //
1070cd5254SLiam Girdwood // Generic firmware loader.
1170cd5254SLiam Girdwood //
1270cd5254SLiam Girdwood 
1370cd5254SLiam Girdwood #include <linux/firmware.h>
14febf5da8SRander Wang #include "sof-priv.h"
1570cd5254SLiam Girdwood #include "ops.h"
1670cd5254SLiam Girdwood 
snd_sof_load_firmware_raw(struct snd_sof_dev * sdev)1770cd5254SLiam Girdwood int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev)
1870cd5254SLiam Girdwood {
1970cd5254SLiam Girdwood 	struct snd_sof_pdata *plat_data = sdev->pdata;
2070cd5254SLiam Girdwood 	const char *fw_filename;
21a80cf198SKarol Trzcinski 	ssize_t ext_man_size;
2270cd5254SLiam Girdwood 	int ret;
2370cd5254SLiam Girdwood 
2470cd5254SLiam Girdwood 	/* Don't request firmware again if firmware is already requested */
254f373ccfSPeter Ujfalusi 	if (sdev->basefw.fw)
2670cd5254SLiam Girdwood 		return 0;
2770cd5254SLiam Girdwood 
2870cd5254SLiam Girdwood 	fw_filename = kasprintf(GFP_KERNEL, "%s/%s",
2970cd5254SLiam Girdwood 				plat_data->fw_filename_prefix,
3070cd5254SLiam Girdwood 				plat_data->fw_filename);
3170cd5254SLiam Girdwood 	if (!fw_filename)
3270cd5254SLiam Girdwood 		return -ENOMEM;
3370cd5254SLiam Girdwood 
344f373ccfSPeter Ujfalusi 	ret = request_firmware(&sdev->basefw.fw, fw_filename, sdev->dev);
3570cd5254SLiam Girdwood 
3670cd5254SLiam Girdwood 	if (ret < 0) {
3789e641aeSKai Vehmanen 		dev_err(sdev->dev,
3825766ee4SPeter Ujfalusi 			"error: sof firmware file is missing, you might need to\n");
3925766ee4SPeter Ujfalusi 		dev_err(sdev->dev,
4025766ee4SPeter Ujfalusi 			"       download it from https://github.com/thesofproject/sof-bin/\n");
41a80cf198SKarol Trzcinski 		goto err;
42490a625bSPierre-Louis Bossart 	} else {
43490a625bSPierre-Louis Bossart 		dev_dbg(sdev->dev, "request_firmware %s successful\n",
44490a625bSPierre-Louis Bossart 			fw_filename);
4570cd5254SLiam Girdwood 	}
4670cd5254SLiam Girdwood 
47a80cf198SKarol Trzcinski 	/* check for extended manifest */
48143cdcf1SPeter Ujfalusi 	ext_man_size = sdev->ipc->ops->fw_loader->parse_ext_manifest(sdev);
49a80cf198SKarol Trzcinski 	if (ext_man_size > 0) {
50a80cf198SKarol Trzcinski 		/* when no error occurred, drop extended manifest */
514f373ccfSPeter Ujfalusi 		sdev->basefw.payload_offset = ext_man_size;
52a80cf198SKarol Trzcinski 	} else if (!ext_man_size) {
53a80cf198SKarol Trzcinski 		/* No extended manifest, so nothing to skip during FW load */
54a80cf198SKarol Trzcinski 		dev_dbg(sdev->dev, "firmware doesn't contain extended manifest\n");
55a80cf198SKarol Trzcinski 	} else {
56a80cf198SKarol Trzcinski 		ret = ext_man_size;
57a80cf198SKarol Trzcinski 		dev_err(sdev->dev, "error: firmware %s contains unsupported or invalid extended manifest: %d\n",
58a80cf198SKarol Trzcinski 			fw_filename, ret);
59a80cf198SKarol Trzcinski 	}
60a80cf198SKarol Trzcinski 
61a80cf198SKarol Trzcinski err:
6270cd5254SLiam Girdwood 	kfree(fw_filename);
6370cd5254SLiam Girdwood 
6470cd5254SLiam Girdwood 	return ret;
6570cd5254SLiam Girdwood }
6670cd5254SLiam Girdwood EXPORT_SYMBOL(snd_sof_load_firmware_raw);
6770cd5254SLiam Girdwood 
snd_sof_load_firmware_memcpy(struct snd_sof_dev * sdev)6870cd5254SLiam Girdwood int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev)
6970cd5254SLiam Girdwood {
7070cd5254SLiam Girdwood 	int ret;
7170cd5254SLiam Girdwood 
7270cd5254SLiam Girdwood 	ret = snd_sof_load_firmware_raw(sdev);
7370cd5254SLiam Girdwood 	if (ret < 0)
7470cd5254SLiam Girdwood 		return ret;
7570cd5254SLiam Girdwood 
7670cd5254SLiam Girdwood 	/* make sure the FW header and file is valid */
77143cdcf1SPeter Ujfalusi 	ret = sdev->ipc->ops->fw_loader->validate(sdev);
7870cd5254SLiam Girdwood 	if (ret < 0) {
7970cd5254SLiam Girdwood 		dev_err(sdev->dev, "error: invalid FW header\n");
8070cd5254SLiam Girdwood 		goto error;
8170cd5254SLiam Girdwood 	}
8270cd5254SLiam Girdwood 
8370cd5254SLiam Girdwood 	/* prepare the DSP for FW loading */
8470cd5254SLiam Girdwood 	ret = snd_sof_dsp_reset(sdev);
8570cd5254SLiam Girdwood 	if (ret < 0) {
8670cd5254SLiam Girdwood 		dev_err(sdev->dev, "error: failed to reset DSP\n");
8770cd5254SLiam Girdwood 		goto error;
8870cd5254SLiam Girdwood 	}
8970cd5254SLiam Girdwood 
9070cd5254SLiam Girdwood 	/* parse and load firmware modules to DSP */
91143cdcf1SPeter Ujfalusi 	if (sdev->ipc->ops->fw_loader->load_fw_to_dsp) {
92143cdcf1SPeter Ujfalusi 		ret = sdev->ipc->ops->fw_loader->load_fw_to_dsp(sdev);
9370cd5254SLiam Girdwood 		if (ret < 0) {
94143cdcf1SPeter Ujfalusi 			dev_err(sdev->dev, "Firmware loading failed\n");
9570cd5254SLiam Girdwood 			goto error;
9670cd5254SLiam Girdwood 		}
97143cdcf1SPeter Ujfalusi 	}
9870cd5254SLiam Girdwood 
9970cd5254SLiam Girdwood 	return 0;
10070cd5254SLiam Girdwood 
10170cd5254SLiam Girdwood error:
1024f373ccfSPeter Ujfalusi 	release_firmware(sdev->basefw.fw);
1034f373ccfSPeter Ujfalusi 	sdev->basefw.fw = NULL;
10470cd5254SLiam Girdwood 	return ret;
10570cd5254SLiam Girdwood 
10670cd5254SLiam Girdwood }
10770cd5254SLiam Girdwood EXPORT_SYMBOL(snd_sof_load_firmware_memcpy);
10870cd5254SLiam Girdwood 
snd_sof_run_firmware(struct snd_sof_dev * sdev)10970cd5254SLiam Girdwood int snd_sof_run_firmware(struct snd_sof_dev *sdev)
11070cd5254SLiam Girdwood {
11170cd5254SLiam Girdwood 	int ret;
11270cd5254SLiam Girdwood 
11370cd5254SLiam Girdwood 	init_waitqueue_head(&sdev->boot_wait);
11470cd5254SLiam Girdwood 
1159ff90859SPeter Ujfalusi 	/* (re-)enable dsp dump */
1169ff90859SPeter Ujfalusi 	sdev->dbg_dump_printed = false;
1179ff90859SPeter Ujfalusi 	sdev->ipc_dump_printed = false;
1189ff90859SPeter Ujfalusi 
1195c9714f6SRanjani Sridharan 	/* create read-only fw_version debugfs to store boot version info */
12070cd5254SLiam Girdwood 	if (sdev->first_boot) {
12170cd5254SLiam Girdwood 		ret = snd_sof_debugfs_buf_item(sdev, &sdev->fw_version,
12270cd5254SLiam Girdwood 					       sizeof(sdev->fw_version),
1235c9714f6SRanjani Sridharan 					       "fw_version", 0444);
12470cd5254SLiam Girdwood 		/* errors are only due to memory allocation, not debugfs */
12570cd5254SLiam Girdwood 		if (ret < 0) {
126611fddf5SCurtis Malainey 			dev_err(sdev->dev, "snd_sof_debugfs_buf_item failed\n");
12770cd5254SLiam Girdwood 			return ret;
12870cd5254SLiam Girdwood 		}
12970cd5254SLiam Girdwood 	}
13070cd5254SLiam Girdwood 
13170cd5254SLiam Girdwood 	/* perform pre fw run operations */
13270cd5254SLiam Girdwood 	ret = snd_sof_dsp_pre_fw_run(sdev);
13370cd5254SLiam Girdwood 	if (ret < 0) {
134611fddf5SCurtis Malainey 		dev_err(sdev->dev, "failed pre fw run op\n");
13570cd5254SLiam Girdwood 		return ret;
13670cd5254SLiam Girdwood 	}
13770cd5254SLiam Girdwood 
13870cd5254SLiam Girdwood 	dev_dbg(sdev->dev, "booting DSP firmware\n");
13970cd5254SLiam Girdwood 
14070cd5254SLiam Girdwood 	/* boot the firmware on the DSP */
14170cd5254SLiam Girdwood 	ret = snd_sof_dsp_run(sdev);
14270cd5254SLiam Girdwood 	if (ret < 0) {
1432f148430SPeter Ujfalusi 		snd_sof_dsp_dbg_dump(sdev, "Failed to start DSP",
1442f148430SPeter Ujfalusi 				     SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_PCI);
14570cd5254SLiam Girdwood 		return ret;
14670cd5254SLiam Girdwood 	}
14770cd5254SLiam Girdwood 
1486ca5cecbSRanjani Sridharan 	/*
1496ca5cecbSRanjani Sridharan 	 * now wait for the DSP to boot. There are 3 possible outcomes:
1506ca5cecbSRanjani Sridharan 	 * 1. Boot wait times out indicating FW boot failure.
1516ca5cecbSRanjani Sridharan 	 * 2. FW boots successfully and fw_ready op succeeds.
1526ca5cecbSRanjani Sridharan 	 * 3. FW boots but fw_ready op fails.
1536ca5cecbSRanjani Sridharan 	 */
1546ca5cecbSRanjani Sridharan 	ret = wait_event_timeout(sdev->boot_wait,
1556ca5cecbSRanjani Sridharan 				 sdev->fw_state > SOF_FW_BOOT_IN_PROGRESS,
15670cd5254SLiam Girdwood 				 msecs_to_jiffies(sdev->boot_timeout));
15770cd5254SLiam Girdwood 	if (ret == 0) {
1582f148430SPeter Ujfalusi 		snd_sof_dsp_dbg_dump(sdev, "Firmware boot failure due to timeout",
1592f148430SPeter Ujfalusi 				     SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX |
16023013335SPeter Ujfalusi 				     SOF_DBG_DUMP_TEXT | SOF_DBG_DUMP_PCI);
16170cd5254SLiam Girdwood 		return -EIO;
16270cd5254SLiam Girdwood 	}
16370cd5254SLiam Girdwood 
164b2e9eb3aSPeter Ujfalusi 	if (sdev->fw_state == SOF_FW_BOOT_READY_FAILED)
1656ca5cecbSRanjani Sridharan 		return -EIO; /* FW boots but fw_ready op failed */
16670cd5254SLiam Girdwood 
1679b9db0d6SRanjani Sridharan 	dev_dbg(sdev->dev, "firmware boot complete\n");
1689b9db0d6SRanjani Sridharan 	sof_set_fw_state(sdev, SOF_FW_BOOT_COMPLETE);
1699b9db0d6SRanjani Sridharan 
17070cd5254SLiam Girdwood 	/* perform post fw run operations */
17170cd5254SLiam Girdwood 	ret = snd_sof_dsp_post_fw_run(sdev);
17270cd5254SLiam Girdwood 	if (ret < 0) {
17370cd5254SLiam Girdwood 		dev_err(sdev->dev, "error: failed post fw run op\n");
17470cd5254SLiam Girdwood 		return ret;
17570cd5254SLiam Girdwood 	}
17670cd5254SLiam Girdwood 
177ba42b8baSPeter Ujfalusi 	if (sdev->ipc->ops->post_fw_boot)
178ba42b8baSPeter Ujfalusi 		return sdev->ipc->ops->post_fw_boot(sdev);
1791dd4b999SPeter Ujfalusi 
18070cd5254SLiam Girdwood 	return 0;
18170cd5254SLiam Girdwood }
18270cd5254SLiam Girdwood EXPORT_SYMBOL(snd_sof_run_firmware);
18370cd5254SLiam Girdwood 
snd_sof_fw_unload(struct snd_sof_dev * sdev)18470cd5254SLiam Girdwood void snd_sof_fw_unload(struct snd_sof_dev *sdev)
18570cd5254SLiam Girdwood {
18670cd5254SLiam Girdwood 	/* TODO: support module unloading at runtime */
1874f373ccfSPeter Ujfalusi 	release_firmware(sdev->basefw.fw);
1884f373ccfSPeter Ujfalusi 	sdev->basefw.fw = NULL;
18970cd5254SLiam Girdwood }
19070cd5254SLiam Girdwood EXPORT_SYMBOL(snd_sof_fw_unload);
191