2020-05-01 17:58:50 +03:00
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
2019-04-12 19:05:06 +03: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>
//
# include <linux/firmware.h>
# include <linux/module.h>
# include <sound/soc.h>
# include <sound/sof.h>
# include "sof-priv.h"
# include "ops.h"
2019-09-27 23:05:28 +03:00
/* see SOF_DBG_ flags */
2021-12-23 14:36:12 +03:00
static int sof_core_debug = IS_ENABLED ( CONFIG_SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE ) ;
2019-09-27 23:05:28 +03:00
module_param_named ( sof_debug , sof_core_debug , int , 0444 ) ;
MODULE_PARM_DESC ( sof_debug , " SOF core debug options (0x0 all off) " ) ;
2019-04-12 19:05:06 +03:00
/* SOF defaults if not provided by the platform in ms */
2019-07-22 17:13:46 +03:00
# define TIMEOUT_DEFAULT_IPC_MS 500
# define TIMEOUT_DEFAULT_BOOT_MS 2000
2019-04-12 19:05:06 +03:00
2021-12-23 14:36:11 +03:00
/**
* sof_debug_check_flag - check if a given flag ( s ) is set in sof_core_debug
* @ mask : Flag or combination of flags to check
*
* Returns true if all bits set in mask is also set in sof_core_debug , otherwise
* false
*/
bool sof_debug_check_flag ( int mask )
{
if ( ( sof_core_debug & mask ) = = mask )
return true ;
return false ;
}
EXPORT_SYMBOL ( sof_debug_check_flag ) ;
2019-04-12 19:05:06 +03:00
/*
* FW Panic / fault handling .
*/
struct sof_panic_msg {
u32 id ;
const char * msg ;
} ;
/* standard FW panic types */
static const struct sof_panic_msg panic_msg [ ] = {
{ SOF_IPC_PANIC_MEM , " out of memory " } ,
{ SOF_IPC_PANIC_WORK , " work subsystem init failed " } ,
{ SOF_IPC_PANIC_IPC , " IPC subsystem init failed " } ,
{ SOF_IPC_PANIC_ARCH , " arch init failed " } ,
{ SOF_IPC_PANIC_PLATFORM , " platform init failed " } ,
{ SOF_IPC_PANIC_TASK , " scheduler init failed " } ,
{ SOF_IPC_PANIC_EXCEPTION , " runtime exception " } ,
{ SOF_IPC_PANIC_DEADLOCK , " deadlock " } ,
{ SOF_IPC_PANIC_STACK , " stack overflow " } ,
{ SOF_IPC_PANIC_IDLE , " can't enter idle " } ,
{ SOF_IPC_PANIC_WFI , " invalid wait state " } ,
{ SOF_IPC_PANIC_ASSERT , " assertion failed " } ,
} ;
2021-12-23 14:36:25 +03:00
/**
* sof_print_oops_and_stack - Handle the printing of DSP oops and stack trace
* @ sdev : Pointer to the device ' s sdev
* @ level : prink log level to use for the printing
* @ panic_code : the panic code
* @ tracep_code : tracepoint code
* @ oops : Pointer to DSP specific oops data
* @ panic_info : Pointer to the received panic information message
* @ stack : Pointer to the call stack data
* @ stack_words : Number of words in the stack data
*
2019-04-12 19:05:06 +03:00
* helper to be called from . dbg_dump callbacks . No error code is
* provided , it ' s left as an exercise for the caller of . dbg_dump
* ( typically IPC or loader )
*/
2021-12-23 14:36:25 +03:00
void sof_print_oops_and_stack ( struct snd_sof_dev * sdev , const char * level ,
u32 panic_code , u32 tracep_code , void * oops ,
struct sof_ipc_panic_info * panic_info ,
void * stack , size_t stack_words )
2019-04-12 19:05:06 +03:00
{
u32 code ;
int i ;
/* is firmware dead ? */
if ( ( panic_code & SOF_IPC_PANIC_MAGIC_MASK ) ! = SOF_IPC_PANIC_MAGIC ) {
2021-12-23 14:36:25 +03:00
dev_printk ( level , sdev - > dev , " unexpected fault %#010x trace %#010x \n " ,
panic_code , tracep_code ) ;
2019-04-12 19:05:06 +03:00
return ; /* no fault ? */
}
code = panic_code & ( SOF_IPC_PANIC_MAGIC_MASK | SOF_IPC_PANIC_CODE_MASK ) ;
for ( i = 0 ; i < ARRAY_SIZE ( panic_msg ) ; i + + ) {
if ( panic_msg [ i ] . id = = code ) {
2021-12-23 14:36:25 +03:00
dev_printk ( level , sdev - > dev , " reason: %s (%#x) \n " ,
panic_msg [ i ] . msg , code & SOF_IPC_PANIC_CODE_MASK ) ;
dev_printk ( level , sdev - > dev , " trace point: %#010x \n " , tracep_code ) ;
2019-04-12 19:05:06 +03:00
goto out ;
}
}
/* unknown error */
2021-12-23 14:36:25 +03:00
dev_printk ( level , sdev - > dev , " unknown panic code: %#x \n " ,
code & SOF_IPC_PANIC_CODE_MASK ) ;
dev_printk ( level , sdev - > dev , " trace point: %#010x \n " , tracep_code ) ;
2019-04-12 19:05:06 +03:00
out :
2021-12-23 14:36:25 +03:00
dev_printk ( level , sdev - > dev , " panic at %s:%d \n " , panic_info - > filename ,
panic_info - > linenum ) ;
sof_oops ( sdev , level , oops ) ;
sof_stack ( sdev , level , oops , stack , stack_words ) ;
2019-04-12 19:05:06 +03:00
}
2021-12-23 14:36:25 +03:00
EXPORT_SYMBOL ( sof_print_oops_and_stack ) ;
2019-04-12 19:05:06 +03:00
2022-02-10 18:05:21 +03:00
/* Helper to manage DSP state */
void sof_set_fw_state ( struct snd_sof_dev * sdev , enum sof_fw_state new_state )
{
if ( sdev - > fw_state = = new_state )
return ;
dev_dbg ( sdev - > dev , " fw_state change: %d -> %d \n " , sdev - > fw_state , new_state ) ;
sdev - > fw_state = new_state ;
switch ( new_state ) {
case SOF_FW_BOOT_NOT_STARTED :
case SOF_FW_BOOT_COMPLETE :
case SOF_FW_CRASHED :
sof_client_fw_state_dispatcher ( sdev ) ;
fallthrough ;
default :
break ;
}
}
EXPORT_SYMBOL ( sof_set_fw_state ) ;
2019-12-18 03:26:09 +03:00
/*
* FW Boot State Transition Diagram
*
2021-12-23 14:36:22 +03:00
* + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
* | |
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
* | | | | |
* | BOOT_FAILED | < - - - - - - - | READY_FAILED | |
* | | < - - + | | - - - - - - - - - - - - - - - - - - |
* - - - - - - - - - - - - - - - - - - | - - - - - - - - - - - - - - - - - - | | |
* ^ | ^ | CRASHED | - - - + |
* | | | | | | |
* ( FW Boot Timeout ) | ( FW_READY FAIL ) - - - - - - - - - - - - - - - - - - | |
* | | | ^ | |
* | | | | ( DSP Panic ) | |
* - - - - - - - - - - - - - - - - - - | | - - - - - - - - - - - - - - - - - - | |
* | | | | | | | |
* | IN_PROGRESS | - - - - - - - - - - - - - - - + - - - - - - - - - - - - - > | COMPLETE | | |
* | | ( FW Boot OK ) ( FW_READY OK ) | | | |
* - - - - - - - - - - - - - - - - - - | - - - - - - - - - - - - - - - - - - | |
* ^ | | | |
* | | | | |
* ( FW Loading OK ) | ( System Suspend / Runtime Suspend )
* | | | | |
* | ( FW Loading Fail ) | | |
* - - - - - - - - - - - - - - - - - - | - - - - - - - - - - - - - - - - - - | | |
* | | | | | < - - - - - + | |
* | PREPARE | - - - + | NOT_STARTED | < - - - - - - - - - - - - - - - - - - - - - + |
* | | | | < - - - - - - - - - - - - - - - - - - - - - - - - - - +
2019-12-18 03:26:09 +03:00
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* | ^ | ^
* | | | |
* | + - - - - - - - - - - - - - - - - - - - - - - - + |
* | ( DSP Probe OK ) |
* | |
* | |
* + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
* ( System Suspend / Runtime Suspend )
*/
2019-04-12 19:05:06 +03:00
static int sof_probe_continue ( struct snd_sof_dev * sdev )
{
struct snd_sof_pdata * plat_data = sdev - > pdata ;
int ret ;
/* probe the DSP hardware */
ret = snd_sof_probe ( sdev ) ;
if ( ret < 0 ) {
dev_err ( sdev - > dev , " error: failed to probe DSP %d \n " , ret ) ;
2022-06-09 06:26:37 +03:00
goto probe_err ;
2019-04-12 19:05:06 +03:00
}
2021-10-06 14:06:40 +03:00
sof_set_fw_state ( sdev , SOF_FW_BOOT_PREPARE ) ;
2019-12-18 03:26:09 +03:00
2019-04-12 19:05:06 +03:00
/* check machine info */
ret = sof_machine_check ( sdev ) ;
if ( ret < 0 ) {
dev_err ( sdev - > dev , " error: failed to get machine info %d \n " ,
ret ) ;
2021-03-15 19:39:30 +03:00
goto dsp_err ;
2019-04-12 19:05:06 +03:00
}
/* set up platform component driver */
snd_sof_new_platform_drv ( sdev ) ;
/* register any debug/trace capabilities */
ret = snd_sof_dbg_init ( sdev ) ;
if ( ret < 0 ) {
/*
* debugfs issues are suppressed in snd_sof_dbg_init ( ) since
* we cannot rely on debugfs
* here we trap errors due to memory allocation only .
*/
dev_err ( sdev - > dev , " error: failed to init DSP trace/debug %d \n " ,
ret ) ;
goto dbg_err ;
}
/* init the IPC */
sdev - > ipc = snd_sof_ipc_init ( sdev ) ;
if ( ! sdev - > ipc ) {
2020-05-09 12:33:37 +03:00
ret = - ENOMEM ;
2019-04-12 19:05:06 +03:00
dev_err ( sdev - > dev , " error: failed to init DSP IPC %d \n " , ret ) ;
goto ipc_err ;
}
/* load the firmware */
ret = snd_sof_load_firmware ( sdev ) ;
if ( ret < 0 ) {
dev_err ( sdev - > dev , " error: failed to load DSP firmware %d \n " ,
ret ) ;
2021-12-23 14:36:20 +03:00
sof_set_fw_state ( sdev , SOF_FW_BOOT_FAILED ) ;
2019-04-12 19:05:06 +03:00
goto fw_load_err ;
}
2021-10-06 14:06:40 +03:00
sof_set_fw_state ( sdev , SOF_FW_BOOT_IN_PROGRESS ) ;
2019-12-18 03:26:09 +03:00
/*
* Boot the firmware . The FW boot status will be modified
* in snd_sof_run_firmware ( ) depending on the outcome .
*/
2019-04-12 19:05:06 +03:00
ret = snd_sof_run_firmware ( sdev ) ;
if ( ret < 0 ) {
dev_err ( sdev - > dev , " error: failed to boot DSP firmware %d \n " ,
ret ) ;
2021-12-23 14:36:20 +03:00
sof_set_fw_state ( sdev , SOF_FW_BOOT_FAILED ) ;
2019-04-12 19:05:06 +03:00
goto fw_run_err ;
}
2021-12-23 14:36:12 +03:00
if ( sof_debug_check_flag ( SOF_DBG_ENABLE_TRACE ) ) {
2022-05-16 13:47:05 +03:00
sdev - > fw_trace_is_supported = true ;
2019-09-27 23:05:28 +03:00
2022-05-16 13:47:07 +03:00
/* init firmware tracing */
ret = sof_fw_trace_init ( sdev ) ;
2019-09-27 23:05:28 +03:00
if ( ret < 0 ) {
/* non fatal */
2022-05-16 13:47:07 +03:00
dev_warn ( sdev - > dev , " failed to initialize firmware tracing %d \n " ,
2019-09-27 23:05:28 +03:00
ret ) ;
}
} else {
dev_dbg ( sdev - > dev , " SOF firmware trace disabled \n " ) ;
2019-04-12 19:05:06 +03:00
}
/* hereafter all FW boot flows are for PM reasons */
sdev - > first_boot = false ;
/* now register audio DSP platform driver and dai */
ret = devm_snd_soc_register_component ( sdev - > dev , & sdev - > plat_drv ,
sof_ops ( sdev ) - > drv ,
sof_ops ( sdev ) - > num_drv ) ;
if ( ret < 0 ) {
dev_err ( sdev - > dev ,
" error: failed to register DSP DAI driver %d \n " , ret ) ;
2020-01-25 00:36:20 +03:00
goto fw_trace_err ;
2019-04-12 19:05:06 +03:00
}
2019-12-05 00:15:53 +03:00
ret = snd_sof_machine_register ( sdev , plat_data ) ;
2021-04-10 01:09:58 +03:00
if ( ret < 0 ) {
dev_err ( sdev - > dev ,
" error: failed to register machine driver %d \n " , ret ) ;
2020-01-25 00:36:20 +03:00
goto fw_trace_err ;
2021-04-10 01:09:58 +03:00
}
2019-04-12 19:05:06 +03:00
2022-02-10 18:05:21 +03:00
ret = sof_register_clients ( sdev ) ;
if ( ret < 0 ) {
dev_err ( sdev - > dev , " failed to register clients %d \n " , ret ) ;
goto sof_machine_err ;
}
2019-12-05 00:15:49 +03:00
/*
* Some platforms in SOF , ex : BYT , may not have their platform PM
* callbacks set . Increment the usage count so as to
* prevent the device from entering runtime suspend .
*/
if ( ! sof_ops ( sdev ) - > runtime_suspend | | ! sof_ops ( sdev ) - > runtime_resume )
pm_runtime_get_noresume ( sdev - > dev ) ;
2019-04-12 19:05:06 +03:00
if ( plat_data - > sof_probe_complete )
plat_data - > sof_probe_complete ( sdev - > dev ) ;
2021-02-10 13:52:37 +03:00
sdev - > probe_completed = true ;
2019-04-12 19:05:06 +03:00
return 0 ;
2022-02-10 18:05:21 +03:00
sof_machine_err :
snd_sof_machine_unregister ( sdev , plat_data ) ;
2020-01-25 00:36:20 +03:00
fw_trace_err :
2022-05-16 13:47:07 +03:00
sof_fw_trace_free ( sdev ) ;
2019-04-12 19:05:06 +03:00
fw_run_err :
snd_sof_fw_unload ( sdev ) ;
fw_load_err :
snd_sof_ipc_free ( sdev ) ;
ipc_err :
dbg_err :
2021-03-15 19:39:30 +03:00
snd_sof_free_debug ( sdev ) ;
dsp_err :
2019-04-12 19:05:06 +03:00
snd_sof_remove ( sdev ) ;
2022-06-09 06:26:37 +03:00
probe_err :
sof_ops_free ( sdev ) ;
2019-05-24 22:09:19 +03:00
2020-01-25 00:36:21 +03:00
/* all resources freed, update state to match */
2021-10-06 14:06:40 +03:00
sof_set_fw_state ( sdev , SOF_FW_BOOT_NOT_STARTED ) ;
2020-01-25 00:36:21 +03:00
sdev - > first_boot = true ;
2019-04-12 19:05:06 +03:00
return ret ;
}
static void sof_probe_work ( struct work_struct * work )
{
struct snd_sof_dev * sdev =
container_of ( work , struct snd_sof_dev , probe_work ) ;
int ret ;
ret = sof_probe_continue ( sdev ) ;
if ( ret < 0 ) {
/* errors cannot be propagated, log */
dev_err ( sdev - > dev , " error: %s failed err: %d \n " , __func__ , ret ) ;
}
}
int snd_sof_device_probe ( struct device * dev , struct snd_sof_pdata * plat_data )
{
struct snd_sof_dev * sdev ;
2022-04-14 21:48:11 +03:00
int ret ;
2019-04-12 19:05:06 +03:00
sdev = devm_kzalloc ( dev , sizeof ( * sdev ) , GFP_KERNEL ) ;
if ( ! sdev )
return - ENOMEM ;
/* initialize sof device */
sdev - > dev = dev ;
2020-01-30 01:07:22 +03:00
/* initialize default DSP power state */
sdev - > dsp_power_state . state = SOF_DSP_PM_D0 ;
2019-10-26 01:40:58 +03:00
2019-04-12 19:05:06 +03:00
sdev - > pdata = plat_data ;
sdev - > first_boot = true ;
dev_set_drvdata ( dev , sdev ) ;
2022-04-21 19:33:56 +03:00
/* check IPC support */
if ( ! ( BIT ( plat_data - > ipc_type ) & plat_data - > desc - > ipc_supported_mask ) ) {
dev_err ( dev , " ipc_type %d is not supported on this platform, mask is %#x \n " ,
plat_data - > ipc_type , plat_data - > desc - > ipc_supported_mask ) ;
return - EINVAL ;
}
2022-04-14 21:48:10 +03:00
/* init ops, if necessary */
2022-04-14 21:48:11 +03:00
ret = sof_ops_init ( sdev ) ;
if ( ret < 0 )
return ret ;
2022-04-14 21:48:10 +03:00
2019-04-12 19:05:06 +03:00
/* check all mandatory ops */
if ( ! sof_ops ( sdev ) | | ! sof_ops ( sdev ) - > probe | | ! sof_ops ( sdev ) - > run | |
! sof_ops ( sdev ) - > block_read | | ! sof_ops ( sdev ) - > block_write | |
! sof_ops ( sdev ) - > send_msg | | ! sof_ops ( sdev ) - > load_firmware | |
2022-04-21 11:07:31 +03:00
! sof_ops ( sdev ) - > ipc_msg_data ) {
2022-06-09 06:26:37 +03:00
sof_ops_free ( sdev ) ;
2021-04-10 01:09:58 +03:00
dev_err ( dev , " error: missing mandatory ops \n " ) ;
2019-04-12 19:05:06 +03:00
return - EINVAL ;
2021-04-10 01:09:58 +03:00
}
2019-04-12 19:05:06 +03:00
INIT_LIST_HEAD ( & sdev - > pcm_list ) ;
INIT_LIST_HEAD ( & sdev - > kcontrol_list ) ;
INIT_LIST_HEAD ( & sdev - > widget_list ) ;
INIT_LIST_HEAD ( & sdev - > dai_list ) ;
2022-03-14 23:05:02 +03:00
INIT_LIST_HEAD ( & sdev - > dai_link_list ) ;
2019-04-12 19:05:06 +03:00
INIT_LIST_HEAD ( & sdev - > route_list ) ;
2022-02-10 18:05:21 +03:00
INIT_LIST_HEAD ( & sdev - > ipc_client_list ) ;
INIT_LIST_HEAD ( & sdev - > ipc_rx_handler_list ) ;
INIT_LIST_HEAD ( & sdev - > fw_state_handler_list ) ;
2019-04-12 19:05:06 +03:00
spin_lock_init ( & sdev - > ipc_lock ) ;
spin_lock_init ( & sdev - > hw_lock ) ;
2021-01-05 18:56:40 +03:00
mutex_init ( & sdev - > power_state_access ) ;
2022-02-10 18:05:21 +03:00
mutex_init ( & sdev - > ipc_client_mutex ) ;
mutex_init ( & sdev - > client_event_handler_mutex ) ;
2019-04-12 19:05:06 +03:00
/* set default timeouts if none provided */
if ( plat_data - > desc - > ipc_timeout = = 0 )
sdev - > ipc_timeout = TIMEOUT_DEFAULT_IPC_MS ;
else
sdev - > ipc_timeout = plat_data - > desc - > ipc_timeout ;
if ( plat_data - > desc - > boot_timeout = = 0 )
sdev - > boot_timeout = TIMEOUT_DEFAULT_BOOT_MS ;
else
sdev - > boot_timeout = plat_data - > desc - > boot_timeout ;
2022-02-10 18:05:21 +03:00
sof_set_fw_state ( sdev , SOF_FW_BOOT_NOT_STARTED ) ;
2019-04-12 19:05:06 +03:00
if ( IS_ENABLED ( CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE ) ) {
2021-09-16 15:49:02 +03:00
INIT_WORK ( & sdev - > probe_work , sof_probe_work ) ;
2019-04-12 19:05:06 +03:00
schedule_work ( & sdev - > probe_work ) ;
return 0 ;
}
return sof_probe_continue ( sdev ) ;
}
EXPORT_SYMBOL ( snd_sof_device_probe ) ;
2021-02-10 13:52:37 +03:00
bool snd_sof_device_probe_completed ( struct device * dev )
{
struct snd_sof_dev * sdev = dev_get_drvdata ( dev ) ;
return sdev - > probe_completed ;
}
EXPORT_SYMBOL ( snd_sof_device_probe_completed ) ;
2019-04-12 19:05:06 +03:00
int snd_sof_device_remove ( struct device * dev )
{
struct snd_sof_dev * sdev = dev_get_drvdata ( dev ) ;
struct snd_sof_pdata * pdata = sdev - > pdata ;
2020-05-15 16:59:53 +03:00
int ret ;
2019-04-12 19:05:06 +03:00
if ( IS_ENABLED ( CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE ) )
cancel_work_sync ( & sdev - > probe_work ) ;
2022-02-10 18:05:21 +03:00
/*
* Unregister any registered client device first before IPC and debugfs
* to allow client drivers to be removed cleanly
*/
sof_unregister_clients ( sdev ) ;
2021-11-02 12:47:56 +03:00
/*
* Unregister machine driver . This will unbind the snd_card which
* will remove the component driver and unload the topology
* before freeing the snd_card .
*/
snd_sof_machine_unregister ( sdev , pdata ) ;
2020-01-25 00:36:21 +03:00
if ( sdev - > fw_state > SOF_FW_BOOT_NOT_STARTED ) {
2022-05-16 13:47:07 +03:00
sof_fw_trace_free ( sdev ) ;
2020-07-07 23:40:27 +03:00
ret = snd_sof_dsp_power_down_notify ( sdev ) ;
if ( ret < 0 )
dev_warn ( dev , " error: %d failed to prepare DSP for device removal " ,
ret ) ;
2020-01-25 00:36:21 +03:00
snd_sof_ipc_free ( sdev ) ;
snd_sof_free_debug ( sdev ) ;
snd_sof_remove ( sdev ) ;
2022-03-07 21:10:55 +03:00
}
2019-05-24 22:09:17 +03:00
2022-06-09 06:26:37 +03:00
sof_ops_free ( sdev ) ;
2019-04-12 19:05:06 +03:00
/* release firmware */
2021-09-16 11:50:08 +03:00
snd_sof_fw_unload ( sdev ) ;
2019-04-12 19:05:06 +03:00
return 0 ;
}
EXPORT_SYMBOL ( snd_sof_device_remove ) ;
2021-01-13 18:26:15 +03:00
int snd_sof_device_shutdown ( struct device * dev )
{
struct snd_sof_dev * sdev = dev_get_drvdata ( dev ) ;
2022-02-16 16:32:41 +03:00
struct snd_sof_pdata * pdata = sdev - > pdata ;
2021-01-13 18:26:15 +03:00
2021-03-22 19:37:22 +03:00
if ( IS_ENABLED ( CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE ) )
cancel_work_sync ( & sdev - > probe_work ) ;
2022-02-16 16:32:41 +03:00
/*
* make sure clients and machine driver ( s ) are unregistered to force
* all userspace devices to be closed prior to the DSP shutdown sequence
*/
sof_unregister_clients ( sdev ) ;
snd_sof_machine_unregister ( sdev , pdata ) ;
2021-03-22 19:37:22 +03:00
if ( sdev - > fw_state = = SOF_FW_BOOT_COMPLETE )
return snd_sof_shutdown ( sdev ) ;
return 0 ;
2021-01-13 18:26:15 +03:00
}
EXPORT_SYMBOL ( snd_sof_device_shutdown ) ;
2019-04-12 19:05:06 +03:00
MODULE_AUTHOR ( " Liam Girdwood " ) ;
MODULE_DESCRIPTION ( " Sound Open Firmware (SOF) Core " ) ;
MODULE_LICENSE ( " Dual BSD/GPL " ) ;
MODULE_ALIAS ( " platform:sof-audio " ) ;
2022-02-10 18:05:23 +03:00
MODULE_IMPORT_NS ( SND_SOC_SOF_CLIENT ) ;