2020-05-01 09:58:50 -05:00
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
2019-04-12 11:05:14 -05:00
//
// 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. All rights reserved.
//
// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
//
// Generic firmware loader.
//
# include <linux/firmware.h>
2021-10-08 12:38:36 +03:00
# include "sof-priv.h"
2019-04-12 11:05:14 -05:00
# 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 ;
2020-05-20 19:59:08 +03:00
ssize_t ext_man_size ;
2019-04-12 11:05:14 -05:00
int ret ;
/* Don't request firmware again if firmware is already requested */
2022-10-20 15:12:21 +03:00
if ( sdev - > basefw . fw )
2019-04-12 11:05:14 -05:00
return 0 ;
fw_filename = kasprintf ( GFP_KERNEL , " %s/%s " ,
plat_data - > fw_filename_prefix ,
plat_data - > fw_filename ) ;
if ( ! fw_filename )
return - ENOMEM ;
2022-10-20 15:12:21 +03:00
ret = request_firmware ( & sdev - > basefw . fw , fw_filename , sdev - > dev ) ;
2019-04-12 11:05:14 -05:00
if ( ret < 0 ) {
2021-01-27 14:23:58 +02:00
dev_err ( sdev - > dev ,
2021-09-16 11:53:42 +03:00
" 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 " ) ;
2020-05-20 19:59:08 +03:00
goto err ;
2020-01-07 10:08:40 -06:00
} else {
dev_dbg ( sdev - > dev , " request_firmware %s successful \n " ,
fw_filename ) ;
2019-04-12 11:05:14 -05:00
}
2020-05-20 19:59:08 +03:00
/* check for extended manifest */
2022-04-25 15:11:23 -07:00
ext_man_size = sdev - > ipc - > ops - > fw_loader - > parse_ext_manifest ( sdev ) ;
2020-05-20 19:59:08 +03:00
if ( ext_man_size > 0 ) {
/* when no error occurred, drop extended manifest */
2022-10-20 15:12:21 +03:00
sdev - > basefw . payload_offset = ext_man_size ;
2020-05-20 19:59:08 +03:00
} 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 :
2019-04-12 11:05:14 -05:00
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 */
2022-04-25 15:11:23 -07:00
ret = sdev - > ipc - > ops - > fw_loader - > validate ( sdev ) ;
2019-04-12 11:05:14 -05:00
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 */
2022-04-25 15:11:23 -07:00
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 ;
}
2019-04-12 11:05:14 -05:00
}
return 0 ;
error :
2022-10-20 15:12:21 +03:00
release_firmware ( sdev - > basefw . fw ) ;
sdev - > basefw . fw = NULL ;
2019-04-12 11:05:14 -05:00
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 ) ;
2021-10-06 14:06:30 +03:00
/* (re-)enable dsp dump */
sdev - > dbg_dump_printed = false ;
sdev - > ipc_dump_printed = false ;
2019-06-03 11:18:18 -05:00
/* create read-only fw_version debugfs to store boot version info */
2019-04-12 11:05:14 -05:00
if ( sdev - > first_boot ) {
ret = snd_sof_debugfs_buf_item ( sdev , & sdev - > fw_version ,
sizeof ( sdev - > fw_version ) ,
2019-06-03 11:18:18 -05:00
" fw_version " , 0444 ) ;
2019-04-12 11:05:14 -05:00
/* errors are only due to memory allocation, not debugfs */
if ( ret < 0 ) {
2023-03-07 13:50:18 +02:00
dev_err ( sdev - > dev , " snd_sof_debugfs_buf_item failed \n " ) ;
2019-04-12 11:05:14 -05:00
return ret ;
}
}
/* perform pre fw run operations */
ret = snd_sof_dsp_pre_fw_run ( sdev ) ;
if ( ret < 0 ) {
2023-03-07 13:50:18 +02:00
dev_err ( sdev - > dev , " failed pre fw run op \n " ) ;
2019-04-12 11:05:14 -05:00
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 ) {
2021-12-23 13:36:14 +02:00
snd_sof_dsp_dbg_dump ( sdev , " Failed to start DSP " ,
SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_PCI ) ;
2019-04-12 11:05:14 -05:00
return ret ;
}
2019-12-17 18:26:09 -06:00
/*
* 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 ,
2019-04-12 11:05:14 -05:00
msecs_to_jiffies ( sdev - > boot_timeout ) ) ;
if ( ret = = 0 ) {
2021-12-23 13:36:14 +02:00
snd_sof_dsp_dbg_dump ( sdev , " Firmware boot failure due to timeout " ,
SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX |
2021-10-06 14:06:36 +03:00
SOF_DBG_DUMP_TEXT | SOF_DBG_DUMP_PCI ) ;
2019-04-12 11:05:14 -05:00
return - EIO ;
}
2021-12-23 13:36:16 +02:00
if ( sdev - > fw_state = = SOF_FW_BOOT_READY_FAILED )
2019-12-17 18:26:09 -06:00
return - EIO ; /* FW boots but fw_ready op failed */
2019-04-12 11:05:14 -05:00
2022-10-20 15:12:20 +03:00
dev_dbg ( sdev - > dev , " firmware boot complete \n " ) ;
sof_set_fw_state ( sdev , SOF_FW_BOOT_COMPLETE ) ;
2019-04-12 11:05:14 -05:00
/* 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 ;
}
2022-10-20 15:12:37 +03:00
if ( sdev - > ipc - > ops - > post_fw_boot )
return sdev - > ipc - > ops - > post_fw_boot ( sdev ) ;
2022-04-25 15:11:29 -07:00
2019-04-12 11:05:14 -05:00
return 0 ;
}
EXPORT_SYMBOL ( snd_sof_run_firmware ) ;
void snd_sof_fw_unload ( struct snd_sof_dev * sdev )
{
/* TODO: support module unloading at runtime */
2022-10-20 15:12:21 +03:00
release_firmware ( sdev - > basefw . fw ) ;
sdev - > basefw . fw = NULL ;
2019-04-12 11:05:14 -05:00
}
EXPORT_SYMBOL ( snd_sof_fw_unload ) ;