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