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