1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
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.
6 // Copyright(c) 2018 Intel Corporation. All rights reserved.
8 // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
11 #include <linux/firmware.h>
12 #include <linux/module.h>
13 #include <sound/soc.h>
14 #include <sound/sof.h>
17 #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
21 /* see SOF_DBG_ flags */
23 module_param_named(sof_debug
, sof_core_debug
, int, 0444);
24 MODULE_PARM_DESC(sof_debug
, "SOF core debug options (0x0 all off)");
26 /* SOF defaults if not provided by the platform in ms */
27 #define TIMEOUT_DEFAULT_IPC_MS 500
28 #define TIMEOUT_DEFAULT_BOOT_MS 2000
31 * FW Panic/fault handling.
34 struct sof_panic_msg
{
39 /* standard FW panic types */
40 static const struct sof_panic_msg panic_msg
[] = {
41 {SOF_IPC_PANIC_MEM
, "out of memory"},
42 {SOF_IPC_PANIC_WORK
, "work subsystem init failed"},
43 {SOF_IPC_PANIC_IPC
, "IPC subsystem init failed"},
44 {SOF_IPC_PANIC_ARCH
, "arch init failed"},
45 {SOF_IPC_PANIC_PLATFORM
, "platform init failed"},
46 {SOF_IPC_PANIC_TASK
, "scheduler init failed"},
47 {SOF_IPC_PANIC_EXCEPTION
, "runtime exception"},
48 {SOF_IPC_PANIC_DEADLOCK
, "deadlock"},
49 {SOF_IPC_PANIC_STACK
, "stack overflow"},
50 {SOF_IPC_PANIC_IDLE
, "can't enter idle"},
51 {SOF_IPC_PANIC_WFI
, "invalid wait state"},
52 {SOF_IPC_PANIC_ASSERT
, "assertion failed"},
56 * helper to be called from .dbg_dump callbacks. No error code is
57 * provided, it's left as an exercise for the caller of .dbg_dump
58 * (typically IPC or loader)
60 void snd_sof_get_status(struct snd_sof_dev
*sdev
, u32 panic_code
,
61 u32 tracep_code
, void *oops
,
62 struct sof_ipc_panic_info
*panic_info
,
63 void *stack
, size_t stack_words
)
68 /* is firmware dead ? */
69 if ((panic_code
& SOF_IPC_PANIC_MAGIC_MASK
) != SOF_IPC_PANIC_MAGIC
) {
70 dev_err(sdev
->dev
, "error: unexpected fault 0x%8.8x trace 0x%8.8x\n",
71 panic_code
, tracep_code
);
72 return; /* no fault ? */
75 code
= panic_code
& (SOF_IPC_PANIC_MAGIC_MASK
| SOF_IPC_PANIC_CODE_MASK
);
77 for (i
= 0; i
< ARRAY_SIZE(panic_msg
); i
++) {
78 if (panic_msg
[i
].id
== code
) {
79 dev_err(sdev
->dev
, "error: %s\n", panic_msg
[i
].msg
);
80 dev_err(sdev
->dev
, "error: trace point %8.8x\n",
87 dev_err(sdev
->dev
, "error: unknown reason %8.8x\n", panic_code
);
88 dev_err(sdev
->dev
, "error: trace point %8.8x\n", tracep_code
);
91 dev_err(sdev
->dev
, "error: panic at %s:%d\n",
92 panic_info
->filename
, panic_info
->linenum
);
94 sof_stack(sdev
, oops
, stack
, stack_words
);
96 EXPORT_SYMBOL(snd_sof_get_status
);
99 * FW Boot State Transition Diagram
101 * +-----------------------------------------------------------------------+
103 * ------------------ ------------------ |
105 * | BOOT_FAILED | | READY_FAILED |-------------------------+ |
107 * ------------------ ------------------ | |
110 * (FW Boot Timeout) (FW_READY FAIL) | |
113 * ------------------ | ------------------ | |
115 * | IN_PROGRESS |---------------+------------->| COMPLETE | | |
116 * | | (FW Boot OK) (FW_READY OK) | | | |
117 * ------------------ ------------------ | |
120 * (FW Loading OK) (System Suspend/Runtime Suspend)
123 * ------------------ ------------------ | | |
125 * | PREPARE | | NOT_STARTED |<---------------------+ |
126 * | | | |<---------------------------+
127 * ------------------ ------------------
130 * | +-----------------------+ |
134 * +------------------------------------+
135 * (System Suspend/Runtime Suspend)
138 static int sof_probe_continue(struct snd_sof_dev
*sdev
)
140 struct snd_sof_pdata
*plat_data
= sdev
->pdata
;
143 /* probe the DSP hardware */
144 ret
= snd_sof_probe(sdev
);
146 dev_err(sdev
->dev
, "error: failed to probe DSP %d\n", ret
);
150 sdev
->fw_state
= SOF_FW_BOOT_PREPARE
;
152 /* check machine info */
153 ret
= sof_machine_check(sdev
);
155 dev_err(sdev
->dev
, "error: failed to get machine info %d\n",
160 /* set up platform component driver */
161 snd_sof_new_platform_drv(sdev
);
163 /* register any debug/trace capabilities */
164 ret
= snd_sof_dbg_init(sdev
);
167 * debugfs issues are suppressed in snd_sof_dbg_init() since
168 * we cannot rely on debugfs
169 * here we trap errors due to memory allocation only.
171 dev_err(sdev
->dev
, "error: failed to init DSP trace/debug %d\n",
177 sdev
->ipc
= snd_sof_ipc_init(sdev
);
180 dev_err(sdev
->dev
, "error: failed to init DSP IPC %d\n", ret
);
184 /* load the firmware */
185 ret
= snd_sof_load_firmware(sdev
);
187 dev_err(sdev
->dev
, "error: failed to load DSP firmware %d\n",
192 sdev
->fw_state
= SOF_FW_BOOT_IN_PROGRESS
;
195 * Boot the firmware. The FW boot status will be modified
196 * in snd_sof_run_firmware() depending on the outcome.
198 ret
= snd_sof_run_firmware(sdev
);
200 dev_err(sdev
->dev
, "error: failed to boot DSP firmware %d\n",
205 if (IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE
) ||
206 (sof_core_debug
& SOF_DBG_ENABLE_TRACE
)) {
207 sdev
->dtrace_is_supported
= true;
210 ret
= snd_sof_init_trace(sdev
);
214 "warning: failed to initialize trace %d\n",
218 dev_dbg(sdev
->dev
, "SOF firmware trace disabled\n");
221 /* hereafter all FW boot flows are for PM reasons */
222 sdev
->first_boot
= false;
224 /* now register audio DSP platform driver and dai */
225 ret
= devm_snd_soc_register_component(sdev
->dev
, &sdev
->plat_drv
,
227 sof_ops(sdev
)->num_drv
);
230 "error: failed to register DSP DAI driver %d\n", ret
);
234 ret
= snd_sof_machine_register(sdev
, plat_data
);
239 * Some platforms in SOF, ex: BYT, may not have their platform PM
240 * callbacks set. Increment the usage count so as to
241 * prevent the device from entering runtime suspend.
243 if (!sof_ops(sdev
)->runtime_suspend
|| !sof_ops(sdev
)->runtime_resume
)
244 pm_runtime_get_noresume(sdev
->dev
);
246 if (plat_data
->sof_probe_complete
)
247 plat_data
->sof_probe_complete(sdev
->dev
);
252 snd_sof_free_trace(sdev
);
254 snd_sof_fw_unload(sdev
);
256 snd_sof_ipc_free(sdev
);
258 snd_sof_free_debug(sdev
);
260 snd_sof_remove(sdev
);
262 /* all resources freed, update state to match */
263 sdev
->fw_state
= SOF_FW_BOOT_NOT_STARTED
;
264 sdev
->first_boot
= true;
269 static void sof_probe_work(struct work_struct
*work
)
271 struct snd_sof_dev
*sdev
=
272 container_of(work
, struct snd_sof_dev
, probe_work
);
275 ret
= sof_probe_continue(sdev
);
277 /* errors cannot be propagated, log */
278 dev_err(sdev
->dev
, "error: %s failed err: %d\n", __func__
, ret
);
282 int snd_sof_device_probe(struct device
*dev
, struct snd_sof_pdata
*plat_data
)
284 struct snd_sof_dev
*sdev
;
286 sdev
= devm_kzalloc(dev
, sizeof(*sdev
), GFP_KERNEL
);
290 /* initialize sof device */
293 /* initialize default DSP power state */
294 sdev
->dsp_power_state
.state
= SOF_DSP_PM_D0
;
296 sdev
->pdata
= plat_data
;
297 sdev
->first_boot
= true;
298 sdev
->fw_state
= SOF_FW_BOOT_NOT_STARTED
;
299 #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
300 sdev
->extractor_stream_tag
= SOF_PROBE_INVALID_NODE_ID
;
302 dev_set_drvdata(dev
, sdev
);
304 /* check all mandatory ops */
305 if (!sof_ops(sdev
) || !sof_ops(sdev
)->probe
|| !sof_ops(sdev
)->run
||
306 !sof_ops(sdev
)->block_read
|| !sof_ops(sdev
)->block_write
||
307 !sof_ops(sdev
)->send_msg
|| !sof_ops(sdev
)->load_firmware
||
308 !sof_ops(sdev
)->ipc_msg_data
|| !sof_ops(sdev
)->ipc_pcm_params
||
309 !sof_ops(sdev
)->fw_ready
)
312 INIT_LIST_HEAD(&sdev
->pcm_list
);
313 INIT_LIST_HEAD(&sdev
->kcontrol_list
);
314 INIT_LIST_HEAD(&sdev
->widget_list
);
315 INIT_LIST_HEAD(&sdev
->dai_list
);
316 INIT_LIST_HEAD(&sdev
->route_list
);
317 spin_lock_init(&sdev
->ipc_lock
);
318 spin_lock_init(&sdev
->hw_lock
);
320 if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE
))
321 INIT_WORK(&sdev
->probe_work
, sof_probe_work
);
323 /* set default timeouts if none provided */
324 if (plat_data
->desc
->ipc_timeout
== 0)
325 sdev
->ipc_timeout
= TIMEOUT_DEFAULT_IPC_MS
;
327 sdev
->ipc_timeout
= plat_data
->desc
->ipc_timeout
;
328 if (plat_data
->desc
->boot_timeout
== 0)
329 sdev
->boot_timeout
= TIMEOUT_DEFAULT_BOOT_MS
;
331 sdev
->boot_timeout
= plat_data
->desc
->boot_timeout
;
333 if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE
)) {
334 schedule_work(&sdev
->probe_work
);
338 return sof_probe_continue(sdev
);
340 EXPORT_SYMBOL(snd_sof_device_probe
);
342 int snd_sof_device_remove(struct device
*dev
)
344 struct snd_sof_dev
*sdev
= dev_get_drvdata(dev
);
345 struct snd_sof_pdata
*pdata
= sdev
->pdata
;
348 if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE
))
349 cancel_work_sync(&sdev
->probe_work
);
351 if (sdev
->fw_state
> SOF_FW_BOOT_NOT_STARTED
) {
352 ret
= snd_sof_dsp_power_down_notify(sdev
);
354 dev_warn(dev
, "error: %d failed to prepare DSP for device removal",
357 snd_sof_fw_unload(sdev
);
358 snd_sof_ipc_free(sdev
);
359 snd_sof_free_debug(sdev
);
360 snd_sof_free_trace(sdev
);
364 * Unregister machine driver. This will unbind the snd_card which
365 * will remove the component driver and unload the topology
366 * before freeing the snd_card.
368 snd_sof_machine_unregister(sdev
, pdata
);
371 * Unregistering the machine driver results in unloading the topology.
372 * Some widgets, ex: scheduler, attempt to power down the core they are
373 * scheduled on, when they are unloaded. Therefore, the DSP must be
374 * removed only after the topology has been unloaded.
376 if (sdev
->fw_state
> SOF_FW_BOOT_NOT_STARTED
)
377 snd_sof_remove(sdev
);
379 /* release firmware */
380 release_firmware(pdata
->fw
);
385 EXPORT_SYMBOL(snd_sof_device_remove
);
387 MODULE_AUTHOR("Liam Girdwood");
388 MODULE_DESCRIPTION("Sound Open Firmware (SOF) Core");
389 MODULE_LICENSE("Dual BSD/GPL");
390 MODULE_ALIAS("platform:sof-audio");