162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license. When using or 462306a36Sopenharmony_ci// redistributing this file, you may do so under either license. 562306a36Sopenharmony_ci// 662306a36Sopenharmony_ci// Copyright(c) 2018 Intel Corporation. All rights reserved. 762306a36Sopenharmony_ci// 862306a36Sopenharmony_ci// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com> 962306a36Sopenharmony_ci// 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/firmware.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <sound/soc.h> 1462306a36Sopenharmony_ci#include <sound/sof.h> 1562306a36Sopenharmony_ci#include "sof-priv.h" 1662306a36Sopenharmony_ci#include "ops.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define CREATE_TRACE_POINTS 1962306a36Sopenharmony_ci#include <trace/events/sof.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* see SOF_DBG_ flags */ 2262306a36Sopenharmony_cistatic int sof_core_debug = IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE); 2362306a36Sopenharmony_cimodule_param_named(sof_debug, sof_core_debug, int, 0444); 2462306a36Sopenharmony_ciMODULE_PARM_DESC(sof_debug, "SOF core debug options (0x0 all off)"); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* SOF defaults if not provided by the platform in ms */ 2762306a36Sopenharmony_ci#define TIMEOUT_DEFAULT_IPC_MS 500 2862306a36Sopenharmony_ci#define TIMEOUT_DEFAULT_BOOT_MS 2000 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/** 3162306a36Sopenharmony_ci * sof_debug_check_flag - check if a given flag(s) is set in sof_core_debug 3262306a36Sopenharmony_ci * @mask: Flag or combination of flags to check 3362306a36Sopenharmony_ci * 3462306a36Sopenharmony_ci * Returns true if all bits set in mask is also set in sof_core_debug, otherwise 3562306a36Sopenharmony_ci * false 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_cibool sof_debug_check_flag(int mask) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci if ((sof_core_debug & mask) == mask) 4062306a36Sopenharmony_ci return true; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci return false; 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ciEXPORT_SYMBOL(sof_debug_check_flag); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* 4762306a36Sopenharmony_ci * FW Panic/fault handling. 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistruct sof_panic_msg { 5162306a36Sopenharmony_ci u32 id; 5262306a36Sopenharmony_ci const char *msg; 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* standard FW panic types */ 5662306a36Sopenharmony_cistatic const struct sof_panic_msg panic_msg[] = { 5762306a36Sopenharmony_ci {SOF_IPC_PANIC_MEM, "out of memory"}, 5862306a36Sopenharmony_ci {SOF_IPC_PANIC_WORK, "work subsystem init failed"}, 5962306a36Sopenharmony_ci {SOF_IPC_PANIC_IPC, "IPC subsystem init failed"}, 6062306a36Sopenharmony_ci {SOF_IPC_PANIC_ARCH, "arch init failed"}, 6162306a36Sopenharmony_ci {SOF_IPC_PANIC_PLATFORM, "platform init failed"}, 6262306a36Sopenharmony_ci {SOF_IPC_PANIC_TASK, "scheduler init failed"}, 6362306a36Sopenharmony_ci {SOF_IPC_PANIC_EXCEPTION, "runtime exception"}, 6462306a36Sopenharmony_ci {SOF_IPC_PANIC_DEADLOCK, "deadlock"}, 6562306a36Sopenharmony_ci {SOF_IPC_PANIC_STACK, "stack overflow"}, 6662306a36Sopenharmony_ci {SOF_IPC_PANIC_IDLE, "can't enter idle"}, 6762306a36Sopenharmony_ci {SOF_IPC_PANIC_WFI, "invalid wait state"}, 6862306a36Sopenharmony_ci {SOF_IPC_PANIC_ASSERT, "assertion failed"}, 6962306a36Sopenharmony_ci}; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/** 7262306a36Sopenharmony_ci * sof_print_oops_and_stack - Handle the printing of DSP oops and stack trace 7362306a36Sopenharmony_ci * @sdev: Pointer to the device's sdev 7462306a36Sopenharmony_ci * @level: prink log level to use for the printing 7562306a36Sopenharmony_ci * @panic_code: the panic code 7662306a36Sopenharmony_ci * @tracep_code: tracepoint code 7762306a36Sopenharmony_ci * @oops: Pointer to DSP specific oops data 7862306a36Sopenharmony_ci * @panic_info: Pointer to the received panic information message 7962306a36Sopenharmony_ci * @stack: Pointer to the call stack data 8062306a36Sopenharmony_ci * @stack_words: Number of words in the stack data 8162306a36Sopenharmony_ci * 8262306a36Sopenharmony_ci * helper to be called from .dbg_dump callbacks. No error code is 8362306a36Sopenharmony_ci * provided, it's left as an exercise for the caller of .dbg_dump 8462306a36Sopenharmony_ci * (typically IPC or loader) 8562306a36Sopenharmony_ci */ 8662306a36Sopenharmony_civoid sof_print_oops_and_stack(struct snd_sof_dev *sdev, const char *level, 8762306a36Sopenharmony_ci u32 panic_code, u32 tracep_code, void *oops, 8862306a36Sopenharmony_ci struct sof_ipc_panic_info *panic_info, 8962306a36Sopenharmony_ci void *stack, size_t stack_words) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci u32 code; 9262306a36Sopenharmony_ci int i; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci /* is firmware dead ? */ 9562306a36Sopenharmony_ci if ((panic_code & SOF_IPC_PANIC_MAGIC_MASK) != SOF_IPC_PANIC_MAGIC) { 9662306a36Sopenharmony_ci dev_printk(level, sdev->dev, "unexpected fault %#010x trace %#010x\n", 9762306a36Sopenharmony_ci panic_code, tracep_code); 9862306a36Sopenharmony_ci return; /* no fault ? */ 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci code = panic_code & (SOF_IPC_PANIC_MAGIC_MASK | SOF_IPC_PANIC_CODE_MASK); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(panic_msg); i++) { 10462306a36Sopenharmony_ci if (panic_msg[i].id == code) { 10562306a36Sopenharmony_ci dev_printk(level, sdev->dev, "reason: %s (%#x)\n", 10662306a36Sopenharmony_ci panic_msg[i].msg, code & SOF_IPC_PANIC_CODE_MASK); 10762306a36Sopenharmony_ci dev_printk(level, sdev->dev, "trace point: %#010x\n", tracep_code); 10862306a36Sopenharmony_ci goto out; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci /* unknown error */ 11362306a36Sopenharmony_ci dev_printk(level, sdev->dev, "unknown panic code: %#x\n", 11462306a36Sopenharmony_ci code & SOF_IPC_PANIC_CODE_MASK); 11562306a36Sopenharmony_ci dev_printk(level, sdev->dev, "trace point: %#010x\n", tracep_code); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ciout: 11862306a36Sopenharmony_ci dev_printk(level, sdev->dev, "panic at %s:%d\n", panic_info->filename, 11962306a36Sopenharmony_ci panic_info->linenum); 12062306a36Sopenharmony_ci sof_oops(sdev, level, oops); 12162306a36Sopenharmony_ci sof_stack(sdev, level, oops, stack, stack_words); 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ciEXPORT_SYMBOL(sof_print_oops_and_stack); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci/* Helper to manage DSP state */ 12662306a36Sopenharmony_civoid sof_set_fw_state(struct snd_sof_dev *sdev, enum sof_fw_state new_state) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci if (sdev->fw_state == new_state) 12962306a36Sopenharmony_ci return; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci dev_dbg(sdev->dev, "fw_state change: %d -> %d\n", sdev->fw_state, new_state); 13262306a36Sopenharmony_ci sdev->fw_state = new_state; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci switch (new_state) { 13562306a36Sopenharmony_ci case SOF_FW_BOOT_NOT_STARTED: 13662306a36Sopenharmony_ci case SOF_FW_BOOT_COMPLETE: 13762306a36Sopenharmony_ci case SOF_FW_CRASHED: 13862306a36Sopenharmony_ci sof_client_fw_state_dispatcher(sdev); 13962306a36Sopenharmony_ci fallthrough; 14062306a36Sopenharmony_ci default: 14162306a36Sopenharmony_ci break; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ciEXPORT_SYMBOL(sof_set_fw_state); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci/* 14762306a36Sopenharmony_ci * FW Boot State Transition Diagram 14862306a36Sopenharmony_ci * 14962306a36Sopenharmony_ci * +----------------------------------------------------------------------+ 15062306a36Sopenharmony_ci * | | 15162306a36Sopenharmony_ci * ------------------ ------------------ | 15262306a36Sopenharmony_ci * | | | | | 15362306a36Sopenharmony_ci * | BOOT_FAILED |<-------| READY_FAILED | | 15462306a36Sopenharmony_ci * | |<--+ | | ------------------ | 15562306a36Sopenharmony_ci * ------------------ | ------------------ | | | 15662306a36Sopenharmony_ci * ^ | ^ | CRASHED |---+ | 15762306a36Sopenharmony_ci * | | | | | | | 15862306a36Sopenharmony_ci * (FW Boot Timeout) | (FW_READY FAIL) ------------------ | | 15962306a36Sopenharmony_ci * | | | ^ | | 16062306a36Sopenharmony_ci * | | | |(DSP Panic) | | 16162306a36Sopenharmony_ci * ------------------ | | ------------------ | | 16262306a36Sopenharmony_ci * | | | | | | | | 16362306a36Sopenharmony_ci * | IN_PROGRESS |---------------+------------->| COMPLETE | | | 16462306a36Sopenharmony_ci * | | (FW Boot OK) (FW_READY OK) | | | | 16562306a36Sopenharmony_ci * ------------------ | ------------------ | | 16662306a36Sopenharmony_ci * ^ | | | | 16762306a36Sopenharmony_ci * | | | | | 16862306a36Sopenharmony_ci * (FW Loading OK) | (System Suspend/Runtime Suspend) 16962306a36Sopenharmony_ci * | | | | | 17062306a36Sopenharmony_ci * | (FW Loading Fail) | | | 17162306a36Sopenharmony_ci * ------------------ | ------------------ | | | 17262306a36Sopenharmony_ci * | | | | |<-----+ | | 17362306a36Sopenharmony_ci * | PREPARE |---+ | NOT_STARTED |<---------------------+ | 17462306a36Sopenharmony_ci * | | | |<--------------------------+ 17562306a36Sopenharmony_ci * ------------------ ------------------ 17662306a36Sopenharmony_ci * | ^ | ^ 17762306a36Sopenharmony_ci * | | | | 17862306a36Sopenharmony_ci * | +-----------------------+ | 17962306a36Sopenharmony_ci * | (DSP Probe OK) | 18062306a36Sopenharmony_ci * | | 18162306a36Sopenharmony_ci * | | 18262306a36Sopenharmony_ci * +------------------------------------+ 18362306a36Sopenharmony_ci * (System Suspend/Runtime Suspend) 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic int sof_probe_continue(struct snd_sof_dev *sdev) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct snd_sof_pdata *plat_data = sdev->pdata; 18962306a36Sopenharmony_ci int ret; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* probe the DSP hardware */ 19262306a36Sopenharmony_ci ret = snd_sof_probe(sdev); 19362306a36Sopenharmony_ci if (ret < 0) { 19462306a36Sopenharmony_ci dev_err(sdev->dev, "error: failed to probe DSP %d\n", ret); 19562306a36Sopenharmony_ci goto probe_err; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* check machine info */ 20162306a36Sopenharmony_ci ret = sof_machine_check(sdev); 20262306a36Sopenharmony_ci if (ret < 0) { 20362306a36Sopenharmony_ci dev_err(sdev->dev, "error: failed to get machine info %d\n", 20462306a36Sopenharmony_ci ret); 20562306a36Sopenharmony_ci goto dsp_err; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci /* set up platform component driver */ 20962306a36Sopenharmony_ci snd_sof_new_platform_drv(sdev); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (sdev->dspless_mode_selected) { 21262306a36Sopenharmony_ci sof_set_fw_state(sdev, SOF_DSPLESS_MODE); 21362306a36Sopenharmony_ci goto skip_dsp_init; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci /* register any debug/trace capabilities */ 21762306a36Sopenharmony_ci ret = snd_sof_dbg_init(sdev); 21862306a36Sopenharmony_ci if (ret < 0) { 21962306a36Sopenharmony_ci /* 22062306a36Sopenharmony_ci * debugfs issues are suppressed in snd_sof_dbg_init() since 22162306a36Sopenharmony_ci * we cannot rely on debugfs 22262306a36Sopenharmony_ci * here we trap errors due to memory allocation only. 22362306a36Sopenharmony_ci */ 22462306a36Sopenharmony_ci dev_err(sdev->dev, "error: failed to init DSP trace/debug %d\n", 22562306a36Sopenharmony_ci ret); 22662306a36Sopenharmony_ci goto dbg_err; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* init the IPC */ 23062306a36Sopenharmony_ci sdev->ipc = snd_sof_ipc_init(sdev); 23162306a36Sopenharmony_ci if (!sdev->ipc) { 23262306a36Sopenharmony_ci ret = -ENOMEM; 23362306a36Sopenharmony_ci dev_err(sdev->dev, "error: failed to init DSP IPC %d\n", ret); 23462306a36Sopenharmony_ci goto ipc_err; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* load the firmware */ 23862306a36Sopenharmony_ci ret = snd_sof_load_firmware(sdev); 23962306a36Sopenharmony_ci if (ret < 0) { 24062306a36Sopenharmony_ci dev_err(sdev->dev, "error: failed to load DSP firmware %d\n", 24162306a36Sopenharmony_ci ret); 24262306a36Sopenharmony_ci sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED); 24362306a36Sopenharmony_ci goto fw_load_err; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci sof_set_fw_state(sdev, SOF_FW_BOOT_IN_PROGRESS); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* 24962306a36Sopenharmony_ci * Boot the firmware. The FW boot status will be modified 25062306a36Sopenharmony_ci * in snd_sof_run_firmware() depending on the outcome. 25162306a36Sopenharmony_ci */ 25262306a36Sopenharmony_ci ret = snd_sof_run_firmware(sdev); 25362306a36Sopenharmony_ci if (ret < 0) { 25462306a36Sopenharmony_ci dev_err(sdev->dev, "error: failed to boot DSP firmware %d\n", 25562306a36Sopenharmony_ci ret); 25662306a36Sopenharmony_ci sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED); 25762306a36Sopenharmony_ci goto fw_run_err; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (sof_debug_check_flag(SOF_DBG_ENABLE_TRACE)) { 26162306a36Sopenharmony_ci sdev->fw_trace_is_supported = true; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* init firmware tracing */ 26462306a36Sopenharmony_ci ret = sof_fw_trace_init(sdev); 26562306a36Sopenharmony_ci if (ret < 0) { 26662306a36Sopenharmony_ci /* non fatal */ 26762306a36Sopenharmony_ci dev_warn(sdev->dev, "failed to initialize firmware tracing %d\n", 26862306a36Sopenharmony_ci ret); 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci } else { 27162306a36Sopenharmony_ci dev_dbg(sdev->dev, "SOF firmware trace disabled\n"); 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ciskip_dsp_init: 27562306a36Sopenharmony_ci /* hereafter all FW boot flows are for PM reasons */ 27662306a36Sopenharmony_ci sdev->first_boot = false; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* now register audio DSP platform driver and dai */ 27962306a36Sopenharmony_ci ret = devm_snd_soc_register_component(sdev->dev, &sdev->plat_drv, 28062306a36Sopenharmony_ci sof_ops(sdev)->drv, 28162306a36Sopenharmony_ci sof_ops(sdev)->num_drv); 28262306a36Sopenharmony_ci if (ret < 0) { 28362306a36Sopenharmony_ci dev_err(sdev->dev, 28462306a36Sopenharmony_ci "error: failed to register DSP DAI driver %d\n", ret); 28562306a36Sopenharmony_ci goto fw_trace_err; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci ret = snd_sof_machine_register(sdev, plat_data); 28962306a36Sopenharmony_ci if (ret < 0) { 29062306a36Sopenharmony_ci dev_err(sdev->dev, 29162306a36Sopenharmony_ci "error: failed to register machine driver %d\n", ret); 29262306a36Sopenharmony_ci goto fw_trace_err; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci ret = sof_register_clients(sdev); 29662306a36Sopenharmony_ci if (ret < 0) { 29762306a36Sopenharmony_ci dev_err(sdev->dev, "failed to register clients %d\n", ret); 29862306a36Sopenharmony_ci goto sof_machine_err; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* 30262306a36Sopenharmony_ci * Some platforms in SOF, ex: BYT, may not have their platform PM 30362306a36Sopenharmony_ci * callbacks set. Increment the usage count so as to 30462306a36Sopenharmony_ci * prevent the device from entering runtime suspend. 30562306a36Sopenharmony_ci */ 30662306a36Sopenharmony_ci if (!sof_ops(sdev)->runtime_suspend || !sof_ops(sdev)->runtime_resume) 30762306a36Sopenharmony_ci pm_runtime_get_noresume(sdev->dev); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (plat_data->sof_probe_complete) 31062306a36Sopenharmony_ci plat_data->sof_probe_complete(sdev->dev); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci sdev->probe_completed = true; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci return 0; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cisof_machine_err: 31762306a36Sopenharmony_ci snd_sof_machine_unregister(sdev, plat_data); 31862306a36Sopenharmony_cifw_trace_err: 31962306a36Sopenharmony_ci sof_fw_trace_free(sdev); 32062306a36Sopenharmony_cifw_run_err: 32162306a36Sopenharmony_ci snd_sof_fw_unload(sdev); 32262306a36Sopenharmony_cifw_load_err: 32362306a36Sopenharmony_ci snd_sof_ipc_free(sdev); 32462306a36Sopenharmony_ciipc_err: 32562306a36Sopenharmony_cidbg_err: 32662306a36Sopenharmony_ci snd_sof_free_debug(sdev); 32762306a36Sopenharmony_cidsp_err: 32862306a36Sopenharmony_ci snd_sof_remove(sdev); 32962306a36Sopenharmony_ciprobe_err: 33062306a36Sopenharmony_ci sof_ops_free(sdev); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci /* all resources freed, update state to match */ 33362306a36Sopenharmony_ci sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED); 33462306a36Sopenharmony_ci sdev->first_boot = true; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci return ret; 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic void sof_probe_work(struct work_struct *work) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci struct snd_sof_dev *sdev = 34262306a36Sopenharmony_ci container_of(work, struct snd_sof_dev, probe_work); 34362306a36Sopenharmony_ci int ret; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci ret = sof_probe_continue(sdev); 34662306a36Sopenharmony_ci if (ret < 0) { 34762306a36Sopenharmony_ci /* errors cannot be propagated, log */ 34862306a36Sopenharmony_ci dev_err(sdev->dev, "error: %s failed err: %d\n", __func__, ret); 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ciint snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci struct snd_sof_dev *sdev; 35562306a36Sopenharmony_ci int ret; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci sdev = devm_kzalloc(dev, sizeof(*sdev), GFP_KERNEL); 35862306a36Sopenharmony_ci if (!sdev) 35962306a36Sopenharmony_ci return -ENOMEM; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* initialize sof device */ 36262306a36Sopenharmony_ci sdev->dev = dev; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* initialize default DSP power state */ 36562306a36Sopenharmony_ci sdev->dsp_power_state.state = SOF_DSP_PM_D0; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci sdev->pdata = plat_data; 36862306a36Sopenharmony_ci sdev->first_boot = true; 36962306a36Sopenharmony_ci dev_set_drvdata(dev, sdev); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (sof_core_debug) 37262306a36Sopenharmony_ci dev_info(dev, "sof_debug value: %#x\n", sof_core_debug); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (sof_debug_check_flag(SOF_DBG_DSPLESS_MODE)) { 37562306a36Sopenharmony_ci if (plat_data->desc->dspless_mode_supported) { 37662306a36Sopenharmony_ci dev_info(dev, "Switching to DSPless mode\n"); 37762306a36Sopenharmony_ci sdev->dspless_mode_selected = true; 37862306a36Sopenharmony_ci } else { 37962306a36Sopenharmony_ci dev_info(dev, "DSPless mode is not supported by the platform\n"); 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci /* check IPC support */ 38462306a36Sopenharmony_ci if (!(BIT(plat_data->ipc_type) & plat_data->desc->ipc_supported_mask)) { 38562306a36Sopenharmony_ci dev_err(dev, "ipc_type %d is not supported on this platform, mask is %#x\n", 38662306a36Sopenharmony_ci plat_data->ipc_type, plat_data->desc->ipc_supported_mask); 38762306a36Sopenharmony_ci return -EINVAL; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* init ops, if necessary */ 39162306a36Sopenharmony_ci ret = sof_ops_init(sdev); 39262306a36Sopenharmony_ci if (ret < 0) 39362306a36Sopenharmony_ci return ret; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci /* check all mandatory ops */ 39662306a36Sopenharmony_ci if (!sof_ops(sdev) || !sof_ops(sdev)->probe) { 39762306a36Sopenharmony_ci sof_ops_free(sdev); 39862306a36Sopenharmony_ci dev_err(dev, "missing mandatory ops\n"); 39962306a36Sopenharmony_ci return -EINVAL; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (!sdev->dspless_mode_selected && 40362306a36Sopenharmony_ci (!sof_ops(sdev)->run || !sof_ops(sdev)->block_read || 40462306a36Sopenharmony_ci !sof_ops(sdev)->block_write || !sof_ops(sdev)->send_msg || 40562306a36Sopenharmony_ci !sof_ops(sdev)->load_firmware || !sof_ops(sdev)->ipc_msg_data)) { 40662306a36Sopenharmony_ci sof_ops_free(sdev); 40762306a36Sopenharmony_ci dev_err(dev, "missing mandatory DSP ops\n"); 40862306a36Sopenharmony_ci return -EINVAL; 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci INIT_LIST_HEAD(&sdev->pcm_list); 41262306a36Sopenharmony_ci INIT_LIST_HEAD(&sdev->kcontrol_list); 41362306a36Sopenharmony_ci INIT_LIST_HEAD(&sdev->widget_list); 41462306a36Sopenharmony_ci INIT_LIST_HEAD(&sdev->pipeline_list); 41562306a36Sopenharmony_ci INIT_LIST_HEAD(&sdev->dai_list); 41662306a36Sopenharmony_ci INIT_LIST_HEAD(&sdev->dai_link_list); 41762306a36Sopenharmony_ci INIT_LIST_HEAD(&sdev->route_list); 41862306a36Sopenharmony_ci INIT_LIST_HEAD(&sdev->ipc_client_list); 41962306a36Sopenharmony_ci INIT_LIST_HEAD(&sdev->ipc_rx_handler_list); 42062306a36Sopenharmony_ci INIT_LIST_HEAD(&sdev->fw_state_handler_list); 42162306a36Sopenharmony_ci spin_lock_init(&sdev->ipc_lock); 42262306a36Sopenharmony_ci spin_lock_init(&sdev->hw_lock); 42362306a36Sopenharmony_ci mutex_init(&sdev->power_state_access); 42462306a36Sopenharmony_ci mutex_init(&sdev->ipc_client_mutex); 42562306a36Sopenharmony_ci mutex_init(&sdev->client_event_handler_mutex); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* set default timeouts if none provided */ 42862306a36Sopenharmony_ci if (plat_data->desc->ipc_timeout == 0) 42962306a36Sopenharmony_ci sdev->ipc_timeout = TIMEOUT_DEFAULT_IPC_MS; 43062306a36Sopenharmony_ci else 43162306a36Sopenharmony_ci sdev->ipc_timeout = plat_data->desc->ipc_timeout; 43262306a36Sopenharmony_ci if (plat_data->desc->boot_timeout == 0) 43362306a36Sopenharmony_ci sdev->boot_timeout = TIMEOUT_DEFAULT_BOOT_MS; 43462306a36Sopenharmony_ci else 43562306a36Sopenharmony_ci sdev->boot_timeout = plat_data->desc->boot_timeout; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) { 44062306a36Sopenharmony_ci INIT_WORK(&sdev->probe_work, sof_probe_work); 44162306a36Sopenharmony_ci schedule_work(&sdev->probe_work); 44262306a36Sopenharmony_ci return 0; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci return sof_probe_continue(sdev); 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ciEXPORT_SYMBOL(snd_sof_device_probe); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cibool snd_sof_device_probe_completed(struct device *dev) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci struct snd_sof_dev *sdev = dev_get_drvdata(dev); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci return sdev->probe_completed; 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ciEXPORT_SYMBOL(snd_sof_device_probe_completed); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ciint snd_sof_device_remove(struct device *dev) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci struct snd_sof_dev *sdev = dev_get_drvdata(dev); 46062306a36Sopenharmony_ci struct snd_sof_pdata *pdata = sdev->pdata; 46162306a36Sopenharmony_ci int ret; 46262306a36Sopenharmony_ci bool aborted = false; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) 46562306a36Sopenharmony_ci aborted = cancel_work_sync(&sdev->probe_work); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* 46862306a36Sopenharmony_ci * Unregister any registered client device first before IPC and debugfs 46962306a36Sopenharmony_ci * to allow client drivers to be removed cleanly 47062306a36Sopenharmony_ci */ 47162306a36Sopenharmony_ci sof_unregister_clients(sdev); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci /* 47462306a36Sopenharmony_ci * Unregister machine driver. This will unbind the snd_card which 47562306a36Sopenharmony_ci * will remove the component driver and unload the topology 47662306a36Sopenharmony_ci * before freeing the snd_card. 47762306a36Sopenharmony_ci */ 47862306a36Sopenharmony_ci snd_sof_machine_unregister(sdev, pdata); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) { 48162306a36Sopenharmony_ci sof_fw_trace_free(sdev); 48262306a36Sopenharmony_ci ret = snd_sof_dsp_power_down_notify(sdev); 48362306a36Sopenharmony_ci if (ret < 0) 48462306a36Sopenharmony_ci dev_warn(dev, "error: %d failed to prepare DSP for device removal", 48562306a36Sopenharmony_ci ret); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci snd_sof_ipc_free(sdev); 48862306a36Sopenharmony_ci snd_sof_free_debug(sdev); 48962306a36Sopenharmony_ci snd_sof_remove(sdev); 49062306a36Sopenharmony_ci sof_ops_free(sdev); 49162306a36Sopenharmony_ci } else if (aborted) { 49262306a36Sopenharmony_ci /* probe_work never ran */ 49362306a36Sopenharmony_ci sof_ops_free(sdev); 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci /* release firmware */ 49762306a36Sopenharmony_ci snd_sof_fw_unload(sdev); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci return 0; 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ciEXPORT_SYMBOL(snd_sof_device_remove); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ciint snd_sof_device_shutdown(struct device *dev) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci struct snd_sof_dev *sdev = dev_get_drvdata(dev); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) 50862306a36Sopenharmony_ci cancel_work_sync(&sdev->probe_work); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) { 51162306a36Sopenharmony_ci sof_fw_trace_free(sdev); 51262306a36Sopenharmony_ci return snd_sof_shutdown(sdev); 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci return 0; 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ciEXPORT_SYMBOL(snd_sof_device_shutdown); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ciMODULE_AUTHOR("Liam Girdwood"); 52062306a36Sopenharmony_ciMODULE_DESCRIPTION("Sound Open Firmware (SOF) Core"); 52162306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 52262306a36Sopenharmony_ciMODULE_ALIAS("platform:sof-audio"); 52362306a36Sopenharmony_ciMODULE_IMPORT_NS(SND_SOC_SOF_CLIENT); 524