1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
2//
3// This file is provided under a dual BSD/GPLv2 license.  When using or
4// redistributing this file, you may do so under either license.
5//
6// Copyright(c) 2018 Intel Corporation. All rights reserved.
7//
8// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
9//
10
11#include <linux/firmware.h>
12#include <linux/module.h>
13#include <sound/soc.h>
14#include <sound/sof.h>
15#include "sof-priv.h"
16#include "sof-of-dev.h"
17#include "ops.h"
18
19#define CREATE_TRACE_POINTS
20#include <trace/events/sof.h>
21
22/* see SOF_DBG_ flags */
23static int sof_core_debug =  IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE);
24module_param_named(sof_debug, sof_core_debug, int, 0444);
25MODULE_PARM_DESC(sof_debug, "SOF core debug options (0x0 all off)");
26
27/* SOF defaults if not provided by the platform in ms */
28#define TIMEOUT_DEFAULT_IPC_MS  500
29#define TIMEOUT_DEFAULT_BOOT_MS 2000
30
31/**
32 * sof_debug_check_flag - check if a given flag(s) is set in sof_core_debug
33 * @mask: Flag or combination of flags to check
34 *
35 * Returns true if all bits set in mask is also set in sof_core_debug, otherwise
36 * false
37 */
38bool sof_debug_check_flag(int mask)
39{
40	if ((sof_core_debug & mask) == mask)
41		return true;
42
43	return false;
44}
45EXPORT_SYMBOL(sof_debug_check_flag);
46
47/*
48 * FW Panic/fault handling.
49 */
50
51struct sof_panic_msg {
52	u32 id;
53	const char *msg;
54};
55
56/* standard FW panic types */
57static const struct sof_panic_msg panic_msg[] = {
58	{SOF_IPC_PANIC_MEM, "out of memory"},
59	{SOF_IPC_PANIC_WORK, "work subsystem init failed"},
60	{SOF_IPC_PANIC_IPC, "IPC subsystem init failed"},
61	{SOF_IPC_PANIC_ARCH, "arch init failed"},
62	{SOF_IPC_PANIC_PLATFORM, "platform init failed"},
63	{SOF_IPC_PANIC_TASK, "scheduler init failed"},
64	{SOF_IPC_PANIC_EXCEPTION, "runtime exception"},
65	{SOF_IPC_PANIC_DEADLOCK, "deadlock"},
66	{SOF_IPC_PANIC_STACK, "stack overflow"},
67	{SOF_IPC_PANIC_IDLE, "can't enter idle"},
68	{SOF_IPC_PANIC_WFI, "invalid wait state"},
69	{SOF_IPC_PANIC_ASSERT, "assertion failed"},
70};
71
72/**
73 * sof_print_oops_and_stack - Handle the printing of DSP oops and stack trace
74 * @sdev: Pointer to the device's sdev
75 * @level: prink log level to use for the printing
76 * @panic_code: the panic code
77 * @tracep_code: tracepoint code
78 * @oops: Pointer to DSP specific oops data
79 * @panic_info: Pointer to the received panic information message
80 * @stack: Pointer to the call stack data
81 * @stack_words: Number of words in the stack data
82 *
83 * helper to be called from .dbg_dump callbacks. No error code is
84 * provided, it's left as an exercise for the caller of .dbg_dump
85 * (typically IPC or loader)
86 */
87void sof_print_oops_and_stack(struct snd_sof_dev *sdev, const char *level,
88			      u32 panic_code, u32 tracep_code, void *oops,
89			      struct sof_ipc_panic_info *panic_info,
90			      void *stack, size_t stack_words)
91{
92	u32 code;
93	int i;
94
95	/* is firmware dead ? */
96	if ((panic_code & SOF_IPC_PANIC_MAGIC_MASK) != SOF_IPC_PANIC_MAGIC) {
97		dev_printk(level, sdev->dev, "unexpected fault %#010x trace %#010x\n",
98			   panic_code, tracep_code);
99		return; /* no fault ? */
100	}
101
102	code = panic_code & (SOF_IPC_PANIC_MAGIC_MASK | SOF_IPC_PANIC_CODE_MASK);
103
104	for (i = 0; i < ARRAY_SIZE(panic_msg); i++) {
105		if (panic_msg[i].id == code) {
106			dev_printk(level, sdev->dev, "reason: %s (%#x)\n",
107				   panic_msg[i].msg, code & SOF_IPC_PANIC_CODE_MASK);
108			dev_printk(level, sdev->dev, "trace point: %#010x\n", tracep_code);
109			goto out;
110		}
111	}
112
113	/* unknown error */
114	dev_printk(level, sdev->dev, "unknown panic code: %#x\n",
115		   code & SOF_IPC_PANIC_CODE_MASK);
116	dev_printk(level, sdev->dev, "trace point: %#010x\n", tracep_code);
117
118out:
119	dev_printk(level, sdev->dev, "panic at %s:%d\n", panic_info->filename,
120		   panic_info->linenum);
121	sof_oops(sdev, level, oops);
122	sof_stack(sdev, level, oops, stack, stack_words);
123}
124EXPORT_SYMBOL(sof_print_oops_and_stack);
125
126/* Helper to manage DSP state */
127void sof_set_fw_state(struct snd_sof_dev *sdev, enum sof_fw_state new_state)
128{
129	if (sdev->fw_state == new_state)
130		return;
131
132	dev_dbg(sdev->dev, "fw_state change: %d -> %d\n", sdev->fw_state, new_state);
133	sdev->fw_state = new_state;
134
135	switch (new_state) {
136	case SOF_FW_BOOT_NOT_STARTED:
137	case SOF_FW_BOOT_COMPLETE:
138	case SOF_FW_CRASHED:
139		sof_client_fw_state_dispatcher(sdev);
140		fallthrough;
141	default:
142		break;
143	}
144}
145EXPORT_SYMBOL(sof_set_fw_state);
146
147static struct snd_sof_of_mach *sof_of_machine_select(struct snd_sof_dev *sdev)
148{
149	struct snd_sof_pdata *sof_pdata = sdev->pdata;
150	const struct sof_dev_desc *desc = sof_pdata->desc;
151	struct snd_sof_of_mach *mach = desc->of_machines;
152
153	if (!mach)
154		return NULL;
155
156	for (; mach->compatible; mach++) {
157		if (of_machine_is_compatible(mach->compatible)) {
158			sof_pdata->tplg_filename = mach->sof_tplg_filename;
159			if (mach->fw_filename)
160				sof_pdata->fw_filename = mach->fw_filename;
161
162			return mach;
163		}
164	}
165
166	return NULL;
167}
168
169/* SOF Driver enumeration */
170static int sof_machine_check(struct snd_sof_dev *sdev)
171{
172	struct snd_sof_pdata *sof_pdata = sdev->pdata;
173	const struct sof_dev_desc *desc = sof_pdata->desc;
174	struct snd_soc_acpi_mach *mach;
175
176	if (!IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE)) {
177		const struct snd_sof_of_mach *of_mach;
178
179		if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) &&
180		    sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC))
181			goto nocodec;
182
183		/* find machine */
184		mach = snd_sof_machine_select(sdev);
185		if (mach) {
186			sof_pdata->machine = mach;
187
188			if (sof_pdata->subsystem_id_set) {
189				mach->mach_params.subsystem_vendor = sof_pdata->subsystem_vendor;
190				mach->mach_params.subsystem_device = sof_pdata->subsystem_device;
191				mach->mach_params.subsystem_id_set = true;
192			}
193
194			snd_sof_set_mach_params(mach, sdev);
195			return 0;
196		}
197
198		of_mach = sof_of_machine_select(sdev);
199		if (of_mach) {
200			sof_pdata->of_machine = of_mach;
201			return 0;
202		}
203
204		if (!IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC)) {
205			dev_err(sdev->dev, "error: no matching ASoC machine driver found - aborting probe\n");
206			return -ENODEV;
207		}
208	} else {
209		dev_warn(sdev->dev, "Force to use nocodec mode\n");
210	}
211
212nocodec:
213	/* select nocodec mode */
214	dev_warn(sdev->dev, "Using nocodec machine driver\n");
215	mach = devm_kzalloc(sdev->dev, sizeof(*mach), GFP_KERNEL);
216	if (!mach)
217		return -ENOMEM;
218
219	mach->drv_name = "sof-nocodec";
220	if (!sof_pdata->tplg_filename)
221		sof_pdata->tplg_filename = desc->nocodec_tplg_filename;
222
223	sof_pdata->machine = mach;
224	snd_sof_set_mach_params(mach, sdev);
225
226	return 0;
227}
228
229static int sof_select_ipc_and_paths(struct snd_sof_dev *sdev)
230{
231	struct snd_sof_pdata *plat_data = sdev->pdata;
232	struct sof_loadable_file_profile *base_profile = &plat_data->ipc_file_profile_base;
233	struct sof_loadable_file_profile out_profile;
234	struct device *dev = sdev->dev;
235	int ret;
236
237	if (base_profile->ipc_type != plat_data->desc->ipc_default)
238		dev_info(dev,
239			 "Module parameter used, overriding default IPC %d to %d\n",
240			 plat_data->desc->ipc_default, base_profile->ipc_type);
241
242	if (base_profile->fw_path)
243		dev_dbg(dev, "Module parameter used, changed fw path to %s\n",
244			base_profile->fw_path);
245	else if (base_profile->fw_path_postfix)
246		dev_dbg(dev, "Path postfix appended to default fw path: %s\n",
247			base_profile->fw_path_postfix);
248
249	if (base_profile->fw_lib_path)
250		dev_dbg(dev, "Module parameter used, changed fw_lib path to %s\n",
251			base_profile->fw_lib_path);
252	else if (base_profile->fw_lib_path_postfix)
253		dev_dbg(dev, "Path postfix appended to default fw_lib path: %s\n",
254			base_profile->fw_lib_path_postfix);
255
256	if (base_profile->fw_name)
257		dev_dbg(dev, "Module parameter used, changed fw filename to %s\n",
258			base_profile->fw_name);
259
260	if (base_profile->tplg_path)
261		dev_dbg(dev, "Module parameter used, changed tplg path to %s\n",
262			base_profile->tplg_path);
263
264	if (base_profile->tplg_name)
265		dev_dbg(dev, "Module parameter used, changed tplg name to %s\n",
266			base_profile->tplg_name);
267
268	ret = sof_create_ipc_file_profile(sdev, base_profile, &out_profile);
269	if (ret)
270		return ret;
271
272	plat_data->ipc_type = out_profile.ipc_type;
273	plat_data->fw_filename = out_profile.fw_name;
274	plat_data->fw_filename_prefix = out_profile.fw_path;
275	plat_data->fw_lib_prefix = out_profile.fw_lib_path;
276	plat_data->tplg_filename_prefix = out_profile.tplg_path;
277
278	return 0;
279}
280
281static int validate_sof_ops(struct snd_sof_dev *sdev)
282{
283	int ret;
284
285	/* init ops, if necessary */
286	ret = sof_ops_init(sdev);
287	if (ret < 0)
288		return ret;
289
290	/* check all mandatory ops */
291	if (!sof_ops(sdev) || !sof_ops(sdev)->probe) {
292		dev_err(sdev->dev, "missing mandatory ops\n");
293		sof_ops_free(sdev);
294		return -EINVAL;
295	}
296
297	if (!sdev->dspless_mode_selected &&
298	    (!sof_ops(sdev)->run || !sof_ops(sdev)->block_read ||
299	     !sof_ops(sdev)->block_write || !sof_ops(sdev)->send_msg ||
300	     !sof_ops(sdev)->load_firmware || !sof_ops(sdev)->ipc_msg_data)) {
301		dev_err(sdev->dev, "missing mandatory DSP ops\n");
302		sof_ops_free(sdev);
303		return -EINVAL;
304	}
305
306	return 0;
307}
308
309static int sof_init_sof_ops(struct snd_sof_dev *sdev)
310{
311	struct snd_sof_pdata *plat_data = sdev->pdata;
312	struct sof_loadable_file_profile *base_profile = &plat_data->ipc_file_profile_base;
313
314	/* check IPC support */
315	if (!(BIT(base_profile->ipc_type) & plat_data->desc->ipc_supported_mask)) {
316		dev_err(sdev->dev,
317			"ipc_type %d is not supported on this platform, mask is %#x\n",
318			base_profile->ipc_type, plat_data->desc->ipc_supported_mask);
319		return -EINVAL;
320	}
321
322	/*
323	 * Save the selected IPC type and a topology name override before
324	 * selecting ops since platform code might need this information
325	 */
326	plat_data->ipc_type = base_profile->ipc_type;
327	plat_data->tplg_filename = base_profile->tplg_name;
328
329	return validate_sof_ops(sdev);
330}
331
332static int sof_init_environment(struct snd_sof_dev *sdev)
333{
334	struct snd_sof_pdata *plat_data = sdev->pdata;
335	struct sof_loadable_file_profile *base_profile = &plat_data->ipc_file_profile_base;
336	int ret;
337
338	/* probe the DSP hardware */
339	ret = snd_sof_probe(sdev);
340	if (ret < 0) {
341		dev_err(sdev->dev, "failed to probe DSP %d\n", ret);
342		goto err_sof_probe;
343	}
344
345	/* check machine info */
346	ret = sof_machine_check(sdev);
347	if (ret < 0) {
348		dev_err(sdev->dev, "failed to get machine info %d\n", ret);
349		goto err_machine_check;
350	}
351
352	ret = sof_select_ipc_and_paths(sdev);
353	if (ret) {
354		goto err_machine_check;
355	} else if (plat_data->ipc_type != base_profile->ipc_type) {
356		/* IPC type changed, re-initialize the ops */
357		sof_ops_free(sdev);
358
359		ret = validate_sof_ops(sdev);
360		if (ret < 0) {
361			snd_sof_remove(sdev);
362			snd_sof_remove_late(sdev);
363			return ret;
364		}
365	}
366
367	return 0;
368
369err_machine_check:
370	snd_sof_remove(sdev);
371err_sof_probe:
372	snd_sof_remove_late(sdev);
373	sof_ops_free(sdev);
374
375	return ret;
376}
377
378/*
379 *			FW Boot State Transition Diagram
380 *
381 *    +----------------------------------------------------------------------+
382 *    |									     |
383 * ------------------	     ------------------				     |
384 * |		    |	     |		      |				     |
385 * |   BOOT_FAILED  |<-------|  READY_FAILED  |				     |
386 * |		    |<--+    |	              |	   ------------------	     |
387 * ------------------	|    ------------------	   |		    |	     |
388 *	^		|	    ^		   |	CRASHED	    |---+    |
389 *	|		|	    |		   |		    |	|    |
390 * (FW Boot Timeout)	|	(FW_READY FAIL)	   ------------------	|    |
391 *	|		|	    |		     ^			|    |
392 *	|		|	    |		     |(DSP Panic)	|    |
393 * ------------------	|	    |		   ------------------	|    |
394 * |		    |	|	    |		   |		    |	|    |
395 * |   IN_PROGRESS  |---------------+------------->|    COMPLETE    |	|    |
396 * |		    | (FW Boot OK)   (FW_READY OK) |		    |	|    |
397 * ------------------	|			   ------------------	|    |
398 *	^		|				|		|    |
399 *	|		|				|		|    |
400 * (FW Loading OK)	|			(System Suspend/Runtime Suspend)
401 *	|		|				|		|    |
402 *	|	(FW Loading Fail)			|		|    |
403 * ------------------	|	------------------	|		|    |
404 * |		    |	|	|		 |<-----+		|    |
405 * |   PREPARE	    |---+	|   NOT_STARTED  |<---------------------+    |
406 * |		    |		|		 |<--------------------------+
407 * ------------------		------------------
408 *    |	    ^			    |	   ^
409 *    |	    |			    |	   |
410 *    |	    +-----------------------+	   |
411 *    |		(DSP Probe OK)		   |
412 *    |					   |
413 *    |					   |
414 *    +------------------------------------+
415 *	(System Suspend/Runtime Suspend)
416 */
417
418static int sof_probe_continue(struct snd_sof_dev *sdev)
419{
420	struct snd_sof_pdata *plat_data = sdev->pdata;
421	int ret;
422
423	/* Initialize loadable file paths and check the environment validity */
424	ret = sof_init_environment(sdev);
425	if (ret)
426		return ret;
427
428	sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE);
429
430	/* set up platform component driver */
431	snd_sof_new_platform_drv(sdev);
432
433	if (sdev->dspless_mode_selected) {
434		sof_set_fw_state(sdev, SOF_DSPLESS_MODE);
435		goto skip_dsp_init;
436	}
437
438	/* register any debug/trace capabilities */
439	ret = snd_sof_dbg_init(sdev);
440	if (ret < 0) {
441		/*
442		 * debugfs issues are suppressed in snd_sof_dbg_init() since
443		 * we cannot rely on debugfs
444		 * here we trap errors due to memory allocation only.
445		 */
446		dev_err(sdev->dev, "error: failed to init DSP trace/debug %d\n",
447			ret);
448		goto dbg_err;
449	}
450
451	/* init the IPC */
452	sdev->ipc = snd_sof_ipc_init(sdev);
453	if (!sdev->ipc) {
454		ret = -ENOMEM;
455		dev_err(sdev->dev, "error: failed to init DSP IPC %d\n", ret);
456		goto ipc_err;
457	}
458
459	/* load the firmware */
460	ret = snd_sof_load_firmware(sdev);
461	if (ret < 0) {
462		dev_err(sdev->dev, "error: failed to load DSP firmware %d\n",
463			ret);
464		sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED);
465		goto fw_load_err;
466	}
467
468	sof_set_fw_state(sdev, SOF_FW_BOOT_IN_PROGRESS);
469
470	/*
471	 * Boot the firmware. The FW boot status will be modified
472	 * in snd_sof_run_firmware() depending on the outcome.
473	 */
474	ret = snd_sof_run_firmware(sdev);
475	if (ret < 0) {
476		dev_err(sdev->dev, "error: failed to boot DSP firmware %d\n",
477			ret);
478		sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED);
479		goto fw_run_err;
480	}
481
482	if (sof_debug_check_flag(SOF_DBG_ENABLE_TRACE)) {
483		sdev->fw_trace_is_supported = true;
484
485		/* init firmware tracing */
486		ret = sof_fw_trace_init(sdev);
487		if (ret < 0) {
488			/* non fatal */
489			dev_warn(sdev->dev, "failed to initialize firmware tracing %d\n",
490				 ret);
491		}
492	} else {
493		dev_dbg(sdev->dev, "SOF firmware trace disabled\n");
494	}
495
496skip_dsp_init:
497	/* hereafter all FW boot flows are for PM reasons */
498	sdev->first_boot = false;
499
500	/* now register audio DSP platform driver and dai */
501	ret = devm_snd_soc_register_component(sdev->dev, &sdev->plat_drv,
502					      sof_ops(sdev)->drv,
503					      sof_ops(sdev)->num_drv);
504	if (ret < 0) {
505		dev_err(sdev->dev,
506			"error: failed to register DSP DAI driver %d\n", ret);
507		goto fw_trace_err;
508	}
509
510	ret = snd_sof_machine_register(sdev, plat_data);
511	if (ret < 0) {
512		dev_err(sdev->dev,
513			"error: failed to register machine driver %d\n", ret);
514		goto fw_trace_err;
515	}
516
517	ret = sof_register_clients(sdev);
518	if (ret < 0) {
519		dev_err(sdev->dev, "failed to register clients %d\n", ret);
520		goto sof_machine_err;
521	}
522
523	/*
524	 * Some platforms in SOF, ex: BYT, may not have their platform PM
525	 * callbacks set. Increment the usage count so as to
526	 * prevent the device from entering runtime suspend.
527	 */
528	if (!sof_ops(sdev)->runtime_suspend || !sof_ops(sdev)->runtime_resume)
529		pm_runtime_get_noresume(sdev->dev);
530
531	if (plat_data->sof_probe_complete)
532		plat_data->sof_probe_complete(sdev->dev);
533
534	sdev->probe_completed = true;
535
536	return 0;
537
538sof_machine_err:
539	snd_sof_machine_unregister(sdev, plat_data);
540fw_trace_err:
541	sof_fw_trace_free(sdev);
542fw_run_err:
543	snd_sof_fw_unload(sdev);
544fw_load_err:
545	snd_sof_ipc_free(sdev);
546ipc_err:
547dbg_err:
548	snd_sof_free_debug(sdev);
549	snd_sof_remove(sdev);
550	snd_sof_remove_late(sdev);
551	sof_ops_free(sdev);
552
553	/* all resources freed, update state to match */
554	sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED);
555	sdev->first_boot = true;
556
557	return ret;
558}
559
560static void sof_probe_work(struct work_struct *work)
561{
562	struct snd_sof_dev *sdev =
563		container_of(work, struct snd_sof_dev, probe_work);
564	int ret;
565
566	ret = sof_probe_continue(sdev);
567	if (ret < 0) {
568		/* errors cannot be propagated, log */
569		dev_err(sdev->dev, "error: %s failed err: %d\n", __func__, ret);
570	}
571}
572
573int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
574{
575	struct snd_sof_dev *sdev;
576	int ret;
577
578	sdev = devm_kzalloc(dev, sizeof(*sdev), GFP_KERNEL);
579	if (!sdev)
580		return -ENOMEM;
581
582	/* initialize sof device */
583	sdev->dev = dev;
584
585	/* initialize default DSP power state */
586	sdev->dsp_power_state.state = SOF_DSP_PM_D0;
587
588	sdev->pdata = plat_data;
589	sdev->first_boot = true;
590	dev_set_drvdata(dev, sdev);
591
592	if (sof_core_debug)
593		dev_info(dev, "sof_debug value: %#x\n", sof_core_debug);
594
595	if (sof_debug_check_flag(SOF_DBG_DSPLESS_MODE)) {
596		if (plat_data->desc->dspless_mode_supported) {
597			dev_info(dev, "Switching to DSPless mode\n");
598			sdev->dspless_mode_selected = true;
599		} else {
600			dev_info(dev, "DSPless mode is not supported by the platform\n");
601		}
602	}
603
604	/* Initialize sof_ops based on the initial selected IPC version */
605	ret = sof_init_sof_ops(sdev);
606	if (ret)
607		return ret;
608
609	INIT_LIST_HEAD(&sdev->pcm_list);
610	INIT_LIST_HEAD(&sdev->kcontrol_list);
611	INIT_LIST_HEAD(&sdev->widget_list);
612	INIT_LIST_HEAD(&sdev->pipeline_list);
613	INIT_LIST_HEAD(&sdev->dai_list);
614	INIT_LIST_HEAD(&sdev->dai_link_list);
615	INIT_LIST_HEAD(&sdev->route_list);
616	INIT_LIST_HEAD(&sdev->ipc_client_list);
617	INIT_LIST_HEAD(&sdev->ipc_rx_handler_list);
618	INIT_LIST_HEAD(&sdev->fw_state_handler_list);
619	spin_lock_init(&sdev->ipc_lock);
620	spin_lock_init(&sdev->hw_lock);
621	mutex_init(&sdev->power_state_access);
622	mutex_init(&sdev->ipc_client_mutex);
623	mutex_init(&sdev->client_event_handler_mutex);
624
625	/* set default timeouts if none provided */
626	if (plat_data->desc->ipc_timeout == 0)
627		sdev->ipc_timeout = TIMEOUT_DEFAULT_IPC_MS;
628	else
629		sdev->ipc_timeout = plat_data->desc->ipc_timeout;
630	if (plat_data->desc->boot_timeout == 0)
631		sdev->boot_timeout = TIMEOUT_DEFAULT_BOOT_MS;
632	else
633		sdev->boot_timeout = plat_data->desc->boot_timeout;
634
635	sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED);
636
637	/*
638	 * first pass of probe which isn't allowed to run in a work-queue,
639	 * typically to rely on -EPROBE_DEFER dependencies
640	 */
641	ret = snd_sof_probe_early(sdev);
642	if (ret < 0)
643		return ret;
644
645	if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) {
646		INIT_WORK(&sdev->probe_work, sof_probe_work);
647		schedule_work(&sdev->probe_work);
648		return 0;
649	}
650
651	return sof_probe_continue(sdev);
652}
653EXPORT_SYMBOL(snd_sof_device_probe);
654
655bool snd_sof_device_probe_completed(struct device *dev)
656{
657	struct snd_sof_dev *sdev = dev_get_drvdata(dev);
658
659	return sdev->probe_completed;
660}
661EXPORT_SYMBOL(snd_sof_device_probe_completed);
662
663int snd_sof_device_remove(struct device *dev)
664{
665	struct snd_sof_dev *sdev = dev_get_drvdata(dev);
666	struct snd_sof_pdata *pdata = sdev->pdata;
667	int ret;
668	bool aborted = false;
669
670	if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE))
671		aborted = cancel_work_sync(&sdev->probe_work);
672
673	/*
674	 * Unregister any registered client device first before IPC and debugfs
675	 * to allow client drivers to be removed cleanly
676	 */
677	sof_unregister_clients(sdev);
678
679	/*
680	 * Unregister machine driver. This will unbind the snd_card which
681	 * will remove the component driver and unload the topology
682	 * before freeing the snd_card.
683	 */
684	snd_sof_machine_unregister(sdev, pdata);
685
686	/*
687	 * Balance the runtime pm usage count in case we are faced with an
688	 * exception and we forcably prevented D3 power state to preserve
689	 * context
690	 */
691	if (sdev->d3_prevented) {
692		sdev->d3_prevented = false;
693		pm_runtime_put_noidle(sdev->dev);
694	}
695
696	if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) {
697		sof_fw_trace_free(sdev);
698		ret = snd_sof_dsp_power_down_notify(sdev);
699		if (ret < 0)
700			dev_warn(dev, "error: %d failed to prepare DSP for device removal",
701				 ret);
702
703		snd_sof_ipc_free(sdev);
704		snd_sof_free_debug(sdev);
705		snd_sof_remove(sdev);
706		snd_sof_remove_late(sdev);
707		sof_ops_free(sdev);
708	} else if (aborted) {
709		/* probe_work never ran */
710		snd_sof_remove_late(sdev);
711		sof_ops_free(sdev);
712	}
713
714	/* release firmware */
715	snd_sof_fw_unload(sdev);
716
717	return 0;
718}
719EXPORT_SYMBOL(snd_sof_device_remove);
720
721int snd_sof_device_shutdown(struct device *dev)
722{
723	struct snd_sof_dev *sdev = dev_get_drvdata(dev);
724
725	if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE))
726		cancel_work_sync(&sdev->probe_work);
727
728	if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) {
729		sof_fw_trace_free(sdev);
730		return snd_sof_shutdown(sdev);
731	}
732
733	return 0;
734}
735EXPORT_SYMBOL(snd_sof_device_shutdown);
736
737/* Machine driver registering and unregistering */
738int sof_machine_register(struct snd_sof_dev *sdev, void *pdata)
739{
740	struct snd_sof_pdata *plat_data = pdata;
741	const char *drv_name;
742	const void *mach;
743	int size;
744
745	drv_name = plat_data->machine->drv_name;
746	mach = plat_data->machine;
747	size = sizeof(*plat_data->machine);
748
749	/* register machine driver, pass machine info as pdata */
750	plat_data->pdev_mach =
751		platform_device_register_data(sdev->dev, drv_name,
752					      PLATFORM_DEVID_NONE, mach, size);
753	if (IS_ERR(plat_data->pdev_mach))
754		return PTR_ERR(plat_data->pdev_mach);
755
756	dev_dbg(sdev->dev, "created machine %s\n",
757		dev_name(&plat_data->pdev_mach->dev));
758
759	return 0;
760}
761EXPORT_SYMBOL(sof_machine_register);
762
763void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata)
764{
765	struct snd_sof_pdata *plat_data = pdata;
766
767	platform_device_unregister(plat_data->pdev_mach);
768}
769EXPORT_SYMBOL(sof_machine_unregister);
770
771MODULE_AUTHOR("Liam Girdwood");
772MODULE_DESCRIPTION("Sound Open Firmware (SOF) Core");
773MODULE_LICENSE("Dual BSD/GPL");
774MODULE_ALIAS("platform:sof-audio");
775MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT);
776