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