18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license. When using or 48c2ecf20Sopenharmony_ci// redistributing this file, you may do so under either license. 58c2ecf20Sopenharmony_ci// 68c2ecf20Sopenharmony_ci// Copyright(c) 2018 Intel Corporation. All rights reserved. 78c2ecf20Sopenharmony_ci// 88c2ecf20Sopenharmony_ci// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com> 98c2ecf20Sopenharmony_ci// 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/firmware.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <sound/soc.h> 148c2ecf20Sopenharmony_ci#include <sound/sof.h> 158c2ecf20Sopenharmony_ci#include "sof-priv.h" 168c2ecf20Sopenharmony_ci#include "ops.h" 178c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) 188c2ecf20Sopenharmony_ci#include "probe.h" 198c2ecf20Sopenharmony_ci#endif 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* see SOF_DBG_ flags */ 228c2ecf20Sopenharmony_ciint sof_core_debug; 238c2ecf20Sopenharmony_cimodule_param_named(sof_debug, sof_core_debug, int, 0444); 248c2ecf20Sopenharmony_ciMODULE_PARM_DESC(sof_debug, "SOF core debug options (0x0 all off)"); 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* SOF defaults if not provided by the platform in ms */ 278c2ecf20Sopenharmony_ci#define TIMEOUT_DEFAULT_IPC_MS 500 288c2ecf20Sopenharmony_ci#define TIMEOUT_DEFAULT_BOOT_MS 2000 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* 318c2ecf20Sopenharmony_ci * FW Panic/fault handling. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistruct sof_panic_msg { 358c2ecf20Sopenharmony_ci u32 id; 368c2ecf20Sopenharmony_ci const char *msg; 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* standard FW panic types */ 408c2ecf20Sopenharmony_cistatic const struct sof_panic_msg panic_msg[] = { 418c2ecf20Sopenharmony_ci {SOF_IPC_PANIC_MEM, "out of memory"}, 428c2ecf20Sopenharmony_ci {SOF_IPC_PANIC_WORK, "work subsystem init failed"}, 438c2ecf20Sopenharmony_ci {SOF_IPC_PANIC_IPC, "IPC subsystem init failed"}, 448c2ecf20Sopenharmony_ci {SOF_IPC_PANIC_ARCH, "arch init failed"}, 458c2ecf20Sopenharmony_ci {SOF_IPC_PANIC_PLATFORM, "platform init failed"}, 468c2ecf20Sopenharmony_ci {SOF_IPC_PANIC_TASK, "scheduler init failed"}, 478c2ecf20Sopenharmony_ci {SOF_IPC_PANIC_EXCEPTION, "runtime exception"}, 488c2ecf20Sopenharmony_ci {SOF_IPC_PANIC_DEADLOCK, "deadlock"}, 498c2ecf20Sopenharmony_ci {SOF_IPC_PANIC_STACK, "stack overflow"}, 508c2ecf20Sopenharmony_ci {SOF_IPC_PANIC_IDLE, "can't enter idle"}, 518c2ecf20Sopenharmony_ci {SOF_IPC_PANIC_WFI, "invalid wait state"}, 528c2ecf20Sopenharmony_ci {SOF_IPC_PANIC_ASSERT, "assertion failed"}, 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* 568c2ecf20Sopenharmony_ci * helper to be called from .dbg_dump callbacks. No error code is 578c2ecf20Sopenharmony_ci * provided, it's left as an exercise for the caller of .dbg_dump 588c2ecf20Sopenharmony_ci * (typically IPC or loader) 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_civoid snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, 618c2ecf20Sopenharmony_ci u32 tracep_code, void *oops, 628c2ecf20Sopenharmony_ci struct sof_ipc_panic_info *panic_info, 638c2ecf20Sopenharmony_ci void *stack, size_t stack_words) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci u32 code; 668c2ecf20Sopenharmony_ci int i; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci /* is firmware dead ? */ 698c2ecf20Sopenharmony_ci if ((panic_code & SOF_IPC_PANIC_MAGIC_MASK) != SOF_IPC_PANIC_MAGIC) { 708c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: unexpected fault 0x%8.8x trace 0x%8.8x\n", 718c2ecf20Sopenharmony_ci panic_code, tracep_code); 728c2ecf20Sopenharmony_ci return; /* no fault ? */ 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci code = panic_code & (SOF_IPC_PANIC_MAGIC_MASK | SOF_IPC_PANIC_CODE_MASK); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(panic_msg); i++) { 788c2ecf20Sopenharmony_ci if (panic_msg[i].id == code) { 798c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: %s\n", panic_msg[i].msg); 808c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: trace point %8.8x\n", 818c2ecf20Sopenharmony_ci tracep_code); 828c2ecf20Sopenharmony_ci goto out; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* unknown error */ 878c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: unknown reason %8.8x\n", panic_code); 888c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: trace point %8.8x\n", tracep_code); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ciout: 918c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: panic at %s:%d\n", 928c2ecf20Sopenharmony_ci panic_info->filename, panic_info->linenum); 938c2ecf20Sopenharmony_ci sof_oops(sdev, oops); 948c2ecf20Sopenharmony_ci sof_stack(sdev, oops, stack, stack_words); 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_sof_get_status); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/* 998c2ecf20Sopenharmony_ci * FW Boot State Transition Diagram 1008c2ecf20Sopenharmony_ci * 1018c2ecf20Sopenharmony_ci * +-----------------------------------------------------------------------+ 1028c2ecf20Sopenharmony_ci * | | 1038c2ecf20Sopenharmony_ci * ------------------ ------------------ | 1048c2ecf20Sopenharmony_ci * | | | | | 1058c2ecf20Sopenharmony_ci * | BOOT_FAILED | | READY_FAILED |-------------------------+ | 1068c2ecf20Sopenharmony_ci * | | | | | | 1078c2ecf20Sopenharmony_ci * ------------------ ------------------ | | 1088c2ecf20Sopenharmony_ci * ^ ^ | | 1098c2ecf20Sopenharmony_ci * | | | | 1108c2ecf20Sopenharmony_ci * (FW Boot Timeout) (FW_READY FAIL) | | 1118c2ecf20Sopenharmony_ci * | | | | 1128c2ecf20Sopenharmony_ci * | | | | 1138c2ecf20Sopenharmony_ci * ------------------ | ------------------ | | 1148c2ecf20Sopenharmony_ci * | | | | | | | 1158c2ecf20Sopenharmony_ci * | IN_PROGRESS |---------------+------------->| COMPLETE | | | 1168c2ecf20Sopenharmony_ci * | | (FW Boot OK) (FW_READY OK) | | | | 1178c2ecf20Sopenharmony_ci * ------------------ ------------------ | | 1188c2ecf20Sopenharmony_ci * ^ | | | 1198c2ecf20Sopenharmony_ci * | | | | 1208c2ecf20Sopenharmony_ci * (FW Loading OK) (System Suspend/Runtime Suspend) 1218c2ecf20Sopenharmony_ci * | | | | 1228c2ecf20Sopenharmony_ci * | | | | 1238c2ecf20Sopenharmony_ci * ------------------ ------------------ | | | 1248c2ecf20Sopenharmony_ci * | | | |<-----+ | | 1258c2ecf20Sopenharmony_ci * | PREPARE | | NOT_STARTED |<---------------------+ | 1268c2ecf20Sopenharmony_ci * | | | |<---------------------------+ 1278c2ecf20Sopenharmony_ci * ------------------ ------------------ 1288c2ecf20Sopenharmony_ci * | ^ | ^ 1298c2ecf20Sopenharmony_ci * | | | | 1308c2ecf20Sopenharmony_ci * | +-----------------------+ | 1318c2ecf20Sopenharmony_ci * | (DSP Probe OK) | 1328c2ecf20Sopenharmony_ci * | | 1338c2ecf20Sopenharmony_ci * | | 1348c2ecf20Sopenharmony_ci * +------------------------------------+ 1358c2ecf20Sopenharmony_ci * (System Suspend/Runtime Suspend) 1368c2ecf20Sopenharmony_ci */ 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic int sof_probe_continue(struct snd_sof_dev *sdev) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci struct snd_sof_pdata *plat_data = sdev->pdata; 1418c2ecf20Sopenharmony_ci int ret; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* probe the DSP hardware */ 1448c2ecf20Sopenharmony_ci ret = snd_sof_probe(sdev); 1458c2ecf20Sopenharmony_ci if (ret < 0) { 1468c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: failed to probe DSP %d\n", ret); 1478c2ecf20Sopenharmony_ci return ret; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci sdev->fw_state = SOF_FW_BOOT_PREPARE; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* check machine info */ 1538c2ecf20Sopenharmony_ci ret = sof_machine_check(sdev); 1548c2ecf20Sopenharmony_ci if (ret < 0) { 1558c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: failed to get machine info %d\n", 1568c2ecf20Sopenharmony_ci ret); 1578c2ecf20Sopenharmony_ci goto dbg_err; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* set up platform component driver */ 1618c2ecf20Sopenharmony_ci snd_sof_new_platform_drv(sdev); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* register any debug/trace capabilities */ 1648c2ecf20Sopenharmony_ci ret = snd_sof_dbg_init(sdev); 1658c2ecf20Sopenharmony_ci if (ret < 0) { 1668c2ecf20Sopenharmony_ci /* 1678c2ecf20Sopenharmony_ci * debugfs issues are suppressed in snd_sof_dbg_init() since 1688c2ecf20Sopenharmony_ci * we cannot rely on debugfs 1698c2ecf20Sopenharmony_ci * here we trap errors due to memory allocation only. 1708c2ecf20Sopenharmony_ci */ 1718c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: failed to init DSP trace/debug %d\n", 1728c2ecf20Sopenharmony_ci ret); 1738c2ecf20Sopenharmony_ci goto dbg_err; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* init the IPC */ 1778c2ecf20Sopenharmony_ci sdev->ipc = snd_sof_ipc_init(sdev); 1788c2ecf20Sopenharmony_ci if (!sdev->ipc) { 1798c2ecf20Sopenharmony_ci ret = -ENOMEM; 1808c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: failed to init DSP IPC %d\n", ret); 1818c2ecf20Sopenharmony_ci goto ipc_err; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* load the firmware */ 1858c2ecf20Sopenharmony_ci ret = snd_sof_load_firmware(sdev); 1868c2ecf20Sopenharmony_ci if (ret < 0) { 1878c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: failed to load DSP firmware %d\n", 1888c2ecf20Sopenharmony_ci ret); 1898c2ecf20Sopenharmony_ci goto fw_load_err; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci sdev->fw_state = SOF_FW_BOOT_IN_PROGRESS; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* 1958c2ecf20Sopenharmony_ci * Boot the firmware. The FW boot status will be modified 1968c2ecf20Sopenharmony_ci * in snd_sof_run_firmware() depending on the outcome. 1978c2ecf20Sopenharmony_ci */ 1988c2ecf20Sopenharmony_ci ret = snd_sof_run_firmware(sdev); 1998c2ecf20Sopenharmony_ci if (ret < 0) { 2008c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: failed to boot DSP firmware %d\n", 2018c2ecf20Sopenharmony_ci ret); 2028c2ecf20Sopenharmony_ci goto fw_run_err; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE) || 2068c2ecf20Sopenharmony_ci (sof_core_debug & SOF_DBG_ENABLE_TRACE)) { 2078c2ecf20Sopenharmony_ci sdev->dtrace_is_supported = true; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* init DMA trace */ 2108c2ecf20Sopenharmony_ci ret = snd_sof_init_trace(sdev); 2118c2ecf20Sopenharmony_ci if (ret < 0) { 2128c2ecf20Sopenharmony_ci /* non fatal */ 2138c2ecf20Sopenharmony_ci dev_warn(sdev->dev, 2148c2ecf20Sopenharmony_ci "warning: failed to initialize trace %d\n", 2158c2ecf20Sopenharmony_ci ret); 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci } else { 2188c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, "SOF firmware trace disabled\n"); 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci /* hereafter all FW boot flows are for PM reasons */ 2228c2ecf20Sopenharmony_ci sdev->first_boot = false; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci /* now register audio DSP platform driver and dai */ 2258c2ecf20Sopenharmony_ci ret = devm_snd_soc_register_component(sdev->dev, &sdev->plat_drv, 2268c2ecf20Sopenharmony_ci sof_ops(sdev)->drv, 2278c2ecf20Sopenharmony_ci sof_ops(sdev)->num_drv); 2288c2ecf20Sopenharmony_ci if (ret < 0) { 2298c2ecf20Sopenharmony_ci dev_err(sdev->dev, 2308c2ecf20Sopenharmony_ci "error: failed to register DSP DAI driver %d\n", ret); 2318c2ecf20Sopenharmony_ci goto fw_trace_err; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci ret = snd_sof_machine_register(sdev, plat_data); 2358c2ecf20Sopenharmony_ci if (ret < 0) 2368c2ecf20Sopenharmony_ci goto fw_trace_err; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci /* 2398c2ecf20Sopenharmony_ci * Some platforms in SOF, ex: BYT, may not have their platform PM 2408c2ecf20Sopenharmony_ci * callbacks set. Increment the usage count so as to 2418c2ecf20Sopenharmony_ci * prevent the device from entering runtime suspend. 2428c2ecf20Sopenharmony_ci */ 2438c2ecf20Sopenharmony_ci if (!sof_ops(sdev)->runtime_suspend || !sof_ops(sdev)->runtime_resume) 2448c2ecf20Sopenharmony_ci pm_runtime_get_noresume(sdev->dev); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (plat_data->sof_probe_complete) 2478c2ecf20Sopenharmony_ci plat_data->sof_probe_complete(sdev->dev); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci return 0; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cifw_trace_err: 2528c2ecf20Sopenharmony_ci snd_sof_free_trace(sdev); 2538c2ecf20Sopenharmony_cifw_run_err: 2548c2ecf20Sopenharmony_ci snd_sof_fw_unload(sdev); 2558c2ecf20Sopenharmony_cifw_load_err: 2568c2ecf20Sopenharmony_ci snd_sof_ipc_free(sdev); 2578c2ecf20Sopenharmony_ciipc_err: 2588c2ecf20Sopenharmony_ci snd_sof_free_debug(sdev); 2598c2ecf20Sopenharmony_cidbg_err: 2608c2ecf20Sopenharmony_ci snd_sof_remove(sdev); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* all resources freed, update state to match */ 2638c2ecf20Sopenharmony_ci sdev->fw_state = SOF_FW_BOOT_NOT_STARTED; 2648c2ecf20Sopenharmony_ci sdev->first_boot = true; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci return ret; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic void sof_probe_work(struct work_struct *work) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct snd_sof_dev *sdev = 2728c2ecf20Sopenharmony_ci container_of(work, struct snd_sof_dev, probe_work); 2738c2ecf20Sopenharmony_ci int ret; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci ret = sof_probe_continue(sdev); 2768c2ecf20Sopenharmony_ci if (ret < 0) { 2778c2ecf20Sopenharmony_ci /* errors cannot be propagated, log */ 2788c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: %s failed err: %d\n", __func__, ret); 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ciint snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci struct snd_sof_dev *sdev; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci sdev = devm_kzalloc(dev, sizeof(*sdev), GFP_KERNEL); 2878c2ecf20Sopenharmony_ci if (!sdev) 2888c2ecf20Sopenharmony_ci return -ENOMEM; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci /* initialize sof device */ 2918c2ecf20Sopenharmony_ci sdev->dev = dev; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci /* initialize default DSP power state */ 2948c2ecf20Sopenharmony_ci sdev->dsp_power_state.state = SOF_DSP_PM_D0; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci sdev->pdata = plat_data; 2978c2ecf20Sopenharmony_ci sdev->first_boot = true; 2988c2ecf20Sopenharmony_ci sdev->fw_state = SOF_FW_BOOT_NOT_STARTED; 2998c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) 3008c2ecf20Sopenharmony_ci sdev->extractor_stream_tag = SOF_PROBE_INVALID_NODE_ID; 3018c2ecf20Sopenharmony_ci#endif 3028c2ecf20Sopenharmony_ci dev_set_drvdata(dev, sdev); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci /* check all mandatory ops */ 3058c2ecf20Sopenharmony_ci if (!sof_ops(sdev) || !sof_ops(sdev)->probe || !sof_ops(sdev)->run || 3068c2ecf20Sopenharmony_ci !sof_ops(sdev)->block_read || !sof_ops(sdev)->block_write || 3078c2ecf20Sopenharmony_ci !sof_ops(sdev)->send_msg || !sof_ops(sdev)->load_firmware || 3088c2ecf20Sopenharmony_ci !sof_ops(sdev)->ipc_msg_data || !sof_ops(sdev)->ipc_pcm_params || 3098c2ecf20Sopenharmony_ci !sof_ops(sdev)->fw_ready) 3108c2ecf20Sopenharmony_ci return -EINVAL; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sdev->pcm_list); 3138c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sdev->kcontrol_list); 3148c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sdev->widget_list); 3158c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sdev->dai_list); 3168c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sdev->route_list); 3178c2ecf20Sopenharmony_ci spin_lock_init(&sdev->ipc_lock); 3188c2ecf20Sopenharmony_ci spin_lock_init(&sdev->hw_lock); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) 3218c2ecf20Sopenharmony_ci INIT_WORK(&sdev->probe_work, sof_probe_work); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci /* set default timeouts if none provided */ 3248c2ecf20Sopenharmony_ci if (plat_data->desc->ipc_timeout == 0) 3258c2ecf20Sopenharmony_ci sdev->ipc_timeout = TIMEOUT_DEFAULT_IPC_MS; 3268c2ecf20Sopenharmony_ci else 3278c2ecf20Sopenharmony_ci sdev->ipc_timeout = plat_data->desc->ipc_timeout; 3288c2ecf20Sopenharmony_ci if (plat_data->desc->boot_timeout == 0) 3298c2ecf20Sopenharmony_ci sdev->boot_timeout = TIMEOUT_DEFAULT_BOOT_MS; 3308c2ecf20Sopenharmony_ci else 3318c2ecf20Sopenharmony_ci sdev->boot_timeout = plat_data->desc->boot_timeout; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) { 3348c2ecf20Sopenharmony_ci schedule_work(&sdev->probe_work); 3358c2ecf20Sopenharmony_ci return 0; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci return sof_probe_continue(sdev); 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_sof_device_probe); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ciint snd_sof_device_remove(struct device *dev) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci struct snd_sof_dev *sdev = dev_get_drvdata(dev); 3458c2ecf20Sopenharmony_ci struct snd_sof_pdata *pdata = sdev->pdata; 3468c2ecf20Sopenharmony_ci int ret; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) 3498c2ecf20Sopenharmony_ci cancel_work_sync(&sdev->probe_work); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) { 3528c2ecf20Sopenharmony_ci ret = snd_sof_dsp_power_down_notify(sdev); 3538c2ecf20Sopenharmony_ci if (ret < 0) 3548c2ecf20Sopenharmony_ci dev_warn(dev, "error: %d failed to prepare DSP for device removal", 3558c2ecf20Sopenharmony_ci ret); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci snd_sof_ipc_free(sdev); 3588c2ecf20Sopenharmony_ci snd_sof_free_debug(sdev); 3598c2ecf20Sopenharmony_ci snd_sof_free_trace(sdev); 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci /* 3638c2ecf20Sopenharmony_ci * Unregister machine driver. This will unbind the snd_card which 3648c2ecf20Sopenharmony_ci * will remove the component driver and unload the topology 3658c2ecf20Sopenharmony_ci * before freeing the snd_card. 3668c2ecf20Sopenharmony_ci */ 3678c2ecf20Sopenharmony_ci snd_sof_machine_unregister(sdev, pdata); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci /* 3708c2ecf20Sopenharmony_ci * Unregistering the machine driver results in unloading the topology. 3718c2ecf20Sopenharmony_ci * Some widgets, ex: scheduler, attempt to power down the core they are 3728c2ecf20Sopenharmony_ci * scheduled on, when they are unloaded. Therefore, the DSP must be 3738c2ecf20Sopenharmony_ci * removed only after the topology has been unloaded. 3748c2ecf20Sopenharmony_ci */ 3758c2ecf20Sopenharmony_ci if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) 3768c2ecf20Sopenharmony_ci snd_sof_remove(sdev); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci /* release firmware */ 3798c2ecf20Sopenharmony_ci snd_sof_fw_unload(sdev); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci return 0; 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_sof_device_remove); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ciMODULE_AUTHOR("Liam Girdwood"); 3868c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Sound Open Firmware (SOF) Core"); 3878c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 3888c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:sof-audio"); 389