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 // 670cd5254SLiam Girdwood // Copyright(c) 2018 Intel Corporation. All rights reserved. 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 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 */ 25*4f373ccfSPeter 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 34*4f373ccfSPeter 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 */ 51*4f373ccfSPeter 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 61*4f373ccfSPeter Ujfalusi /* 62*4f373ccfSPeter Ujfalusi * Until the platform code is switched to use the new container the fw 63*4f373ccfSPeter Ujfalusi * and payload offset must be set in plat_data 64*4f373ccfSPeter Ujfalusi */ 65*4f373ccfSPeter Ujfalusi plat_data->fw = sdev->basefw.fw; 66*4f373ccfSPeter Ujfalusi plat_data->fw_offset = sdev->basefw.payload_offset; 67a80cf198SKarol Trzcinski err: 6870cd5254SLiam Girdwood kfree(fw_filename); 6970cd5254SLiam Girdwood 7070cd5254SLiam Girdwood return ret; 7170cd5254SLiam Girdwood } 7270cd5254SLiam Girdwood EXPORT_SYMBOL(snd_sof_load_firmware_raw); 7370cd5254SLiam Girdwood 7470cd5254SLiam Girdwood int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev) 7570cd5254SLiam Girdwood { 7670cd5254SLiam Girdwood struct snd_sof_pdata *plat_data = sdev->pdata; 7770cd5254SLiam Girdwood int ret; 7870cd5254SLiam Girdwood 7970cd5254SLiam Girdwood ret = snd_sof_load_firmware_raw(sdev); 8070cd5254SLiam Girdwood if (ret < 0) 8170cd5254SLiam Girdwood return ret; 8270cd5254SLiam Girdwood 8370cd5254SLiam Girdwood /* make sure the FW header and file is valid */ 84143cdcf1SPeter Ujfalusi ret = sdev->ipc->ops->fw_loader->validate(sdev); 8570cd5254SLiam Girdwood if (ret < 0) { 8670cd5254SLiam Girdwood dev_err(sdev->dev, "error: invalid FW header\n"); 8770cd5254SLiam Girdwood goto error; 8870cd5254SLiam Girdwood } 8970cd5254SLiam Girdwood 9070cd5254SLiam Girdwood /* prepare the DSP for FW loading */ 9170cd5254SLiam Girdwood ret = snd_sof_dsp_reset(sdev); 9270cd5254SLiam Girdwood if (ret < 0) { 9370cd5254SLiam Girdwood dev_err(sdev->dev, "error: failed to reset DSP\n"); 9470cd5254SLiam Girdwood goto error; 9570cd5254SLiam Girdwood } 9670cd5254SLiam Girdwood 9770cd5254SLiam Girdwood /* parse and load firmware modules to DSP */ 98143cdcf1SPeter Ujfalusi if (sdev->ipc->ops->fw_loader->load_fw_to_dsp) { 99143cdcf1SPeter Ujfalusi ret = sdev->ipc->ops->fw_loader->load_fw_to_dsp(sdev); 10070cd5254SLiam Girdwood if (ret < 0) { 101143cdcf1SPeter Ujfalusi dev_err(sdev->dev, "Firmware loading failed\n"); 10270cd5254SLiam Girdwood goto error; 10370cd5254SLiam Girdwood } 104143cdcf1SPeter Ujfalusi } 10570cd5254SLiam Girdwood 10670cd5254SLiam Girdwood return 0; 10770cd5254SLiam Girdwood 10870cd5254SLiam Girdwood error: 109*4f373ccfSPeter Ujfalusi release_firmware(sdev->basefw.fw); 110*4f373ccfSPeter Ujfalusi sdev->basefw.fw = NULL; 11170cd5254SLiam Girdwood plat_data->fw = NULL; 11270cd5254SLiam Girdwood return ret; 11370cd5254SLiam Girdwood 11470cd5254SLiam Girdwood } 11570cd5254SLiam Girdwood EXPORT_SYMBOL(snd_sof_load_firmware_memcpy); 11670cd5254SLiam Girdwood 11770cd5254SLiam Girdwood int snd_sof_run_firmware(struct snd_sof_dev *sdev) 11870cd5254SLiam Girdwood { 11970cd5254SLiam Girdwood int ret; 12070cd5254SLiam Girdwood 12170cd5254SLiam Girdwood init_waitqueue_head(&sdev->boot_wait); 12270cd5254SLiam Girdwood 1239ff90859SPeter Ujfalusi /* (re-)enable dsp dump */ 1249ff90859SPeter Ujfalusi sdev->dbg_dump_printed = false; 1259ff90859SPeter Ujfalusi sdev->ipc_dump_printed = false; 1269ff90859SPeter Ujfalusi 1275c9714f6SRanjani Sridharan /* create read-only fw_version debugfs to store boot version info */ 12870cd5254SLiam Girdwood if (sdev->first_boot) { 12970cd5254SLiam Girdwood ret = snd_sof_debugfs_buf_item(sdev, &sdev->fw_version, 13070cd5254SLiam Girdwood sizeof(sdev->fw_version), 1315c9714f6SRanjani Sridharan "fw_version", 0444); 13270cd5254SLiam Girdwood /* errors are only due to memory allocation, not debugfs */ 13370cd5254SLiam Girdwood if (ret < 0) { 13470cd5254SLiam Girdwood dev_err(sdev->dev, "error: snd_sof_debugfs_buf_item failed\n"); 13570cd5254SLiam Girdwood return ret; 13670cd5254SLiam Girdwood } 13770cd5254SLiam Girdwood } 13870cd5254SLiam Girdwood 13970cd5254SLiam Girdwood /* perform pre fw run operations */ 14070cd5254SLiam Girdwood ret = snd_sof_dsp_pre_fw_run(sdev); 14170cd5254SLiam Girdwood if (ret < 0) { 14270cd5254SLiam Girdwood dev_err(sdev->dev, "error: failed pre fw run op\n"); 14370cd5254SLiam Girdwood return ret; 14470cd5254SLiam Girdwood } 14570cd5254SLiam Girdwood 14670cd5254SLiam Girdwood dev_dbg(sdev->dev, "booting DSP firmware\n"); 14770cd5254SLiam Girdwood 14870cd5254SLiam Girdwood /* boot the firmware on the DSP */ 14970cd5254SLiam Girdwood ret = snd_sof_dsp_run(sdev); 15070cd5254SLiam Girdwood if (ret < 0) { 1512f148430SPeter Ujfalusi snd_sof_dsp_dbg_dump(sdev, "Failed to start DSP", 1522f148430SPeter Ujfalusi SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_PCI); 15370cd5254SLiam Girdwood return ret; 15470cd5254SLiam Girdwood } 15570cd5254SLiam Girdwood 1566ca5cecbSRanjani Sridharan /* 1576ca5cecbSRanjani Sridharan * now wait for the DSP to boot. There are 3 possible outcomes: 1586ca5cecbSRanjani Sridharan * 1. Boot wait times out indicating FW boot failure. 1596ca5cecbSRanjani Sridharan * 2. FW boots successfully and fw_ready op succeeds. 1606ca5cecbSRanjani Sridharan * 3. FW boots but fw_ready op fails. 1616ca5cecbSRanjani Sridharan */ 1626ca5cecbSRanjani Sridharan ret = wait_event_timeout(sdev->boot_wait, 1636ca5cecbSRanjani Sridharan sdev->fw_state > SOF_FW_BOOT_IN_PROGRESS, 16470cd5254SLiam Girdwood msecs_to_jiffies(sdev->boot_timeout)); 16570cd5254SLiam Girdwood if (ret == 0) { 1662f148430SPeter Ujfalusi snd_sof_dsp_dbg_dump(sdev, "Firmware boot failure due to timeout", 1672f148430SPeter Ujfalusi SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX | 16823013335SPeter Ujfalusi SOF_DBG_DUMP_TEXT | SOF_DBG_DUMP_PCI); 16970cd5254SLiam Girdwood return -EIO; 17070cd5254SLiam Girdwood } 17170cd5254SLiam Girdwood 172b2e9eb3aSPeter Ujfalusi if (sdev->fw_state == SOF_FW_BOOT_READY_FAILED) 1736ca5cecbSRanjani Sridharan return -EIO; /* FW boots but fw_ready op failed */ 17470cd5254SLiam Girdwood 1759b9db0d6SRanjani Sridharan dev_dbg(sdev->dev, "firmware boot complete\n"); 1769b9db0d6SRanjani Sridharan sof_set_fw_state(sdev, SOF_FW_BOOT_COMPLETE); 1779b9db0d6SRanjani Sridharan 17870cd5254SLiam Girdwood /* perform post fw run operations */ 17970cd5254SLiam Girdwood ret = snd_sof_dsp_post_fw_run(sdev); 18070cd5254SLiam Girdwood if (ret < 0) { 18170cd5254SLiam Girdwood dev_err(sdev->dev, "error: failed post fw run op\n"); 18270cd5254SLiam Girdwood return ret; 18370cd5254SLiam Girdwood } 18470cd5254SLiam Girdwood 1851dd4b999SPeter Ujfalusi if (sdev->first_boot && sdev->ipc->ops->fw_loader->query_fw_configuration) 1861dd4b999SPeter Ujfalusi return sdev->ipc->ops->fw_loader->query_fw_configuration(sdev); 1871dd4b999SPeter Ujfalusi 18870cd5254SLiam Girdwood return 0; 18970cd5254SLiam Girdwood } 19070cd5254SLiam Girdwood EXPORT_SYMBOL(snd_sof_run_firmware); 19170cd5254SLiam Girdwood 19270cd5254SLiam Girdwood void snd_sof_fw_unload(struct snd_sof_dev *sdev) 19370cd5254SLiam Girdwood { 19470cd5254SLiam Girdwood /* TODO: support module unloading at runtime */ 195*4f373ccfSPeter Ujfalusi release_firmware(sdev->basefw.fw); 196*4f373ccfSPeter Ujfalusi sdev->basefw.fw = NULL; 1978a8e1813SMarc Herbert sdev->pdata->fw = NULL; 19870cd5254SLiam Girdwood } 19970cd5254SLiam Girdwood EXPORT_SYMBOL(snd_sof_fw_unload); 200