1
2
3
4
5
6
7
8
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 "ops.h"
17#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
18#include "probe.h"
19#endif
20
21
22int sof_core_debug;
23module_param_named(sof_debug, sof_core_debug, int, 0444);
24MODULE_PARM_DESC(sof_debug, "SOF core debug options (0x0 all off)");
25
26
27#define TIMEOUT_DEFAULT_IPC_MS 500
28#define TIMEOUT_DEFAULT_BOOT_MS 2000
29
30
31
32
33
34struct sof_panic_msg {
35 u32 id;
36 const char *msg;
37};
38
39
40static 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"},
53};
54
55
56
57
58
59
60void 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)
64{
65 u32 code;
66 int i;
67
68
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;
73 }
74
75 code = panic_code & (SOF_IPC_PANIC_MAGIC_MASK | SOF_IPC_PANIC_CODE_MASK);
76
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",
81 tracep_code);
82 goto out;
83 }
84 }
85
86
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);
89
90out:
91 dev_err(sdev->dev, "error: panic at %s:%d\n",
92 panic_info->filename, panic_info->linenum);
93 sof_oops(sdev, oops);
94 sof_stack(sdev, oops, stack, stack_words);
95}
96EXPORT_SYMBOL(snd_sof_get_status);
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138static int sof_probe_continue(struct snd_sof_dev *sdev)
139{
140 struct snd_sof_pdata *plat_data = sdev->pdata;
141 int ret;
142
143
144 ret = snd_sof_probe(sdev);
145 if (ret < 0) {
146 dev_err(sdev->dev, "error: failed to probe DSP %d\n", ret);
147 return ret;
148 }
149
150 sdev->fw_state = SOF_FW_BOOT_PREPARE;
151
152
153 ret = sof_machine_check(sdev);
154 if (ret < 0) {
155 dev_err(sdev->dev, "error: failed to get machine info %d\n",
156 ret);
157 goto dsp_err;
158 }
159
160
161 snd_sof_new_platform_drv(sdev);
162
163
164 ret = snd_sof_dbg_init(sdev);
165 if (ret < 0) {
166
167
168
169
170
171 dev_err(sdev->dev, "error: failed to init DSP trace/debug %d\n",
172 ret);
173 goto dbg_err;
174 }
175
176
177 sdev->ipc = snd_sof_ipc_init(sdev);
178 if (!sdev->ipc) {
179 ret = -ENOMEM;
180 dev_err(sdev->dev, "error: failed to init DSP IPC %d\n", ret);
181 goto ipc_err;
182 }
183
184
185 ret = snd_sof_load_firmware(sdev);
186 if (ret < 0) {
187 dev_err(sdev->dev, "error: failed to load DSP firmware %d\n",
188 ret);
189 goto fw_load_err;
190 }
191
192 sdev->fw_state = SOF_FW_BOOT_IN_PROGRESS;
193
194
195
196
197
198 ret = snd_sof_run_firmware(sdev);
199 if (ret < 0) {
200 dev_err(sdev->dev, "error: failed to boot DSP firmware %d\n",
201 ret);
202 goto fw_run_err;
203 }
204
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;
208
209
210 ret = snd_sof_init_trace(sdev);
211 if (ret < 0) {
212
213 dev_warn(sdev->dev,
214 "warning: failed to initialize trace %d\n",
215 ret);
216 }
217 } else {
218 dev_dbg(sdev->dev, "SOF firmware trace disabled\n");
219 }
220
221
222 sdev->first_boot = false;
223
224
225 ret = devm_snd_soc_register_component(sdev->dev, &sdev->plat_drv,
226 sof_ops(sdev)->drv,
227 sof_ops(sdev)->num_drv);
228 if (ret < 0) {
229 dev_err(sdev->dev,
230 "error: failed to register DSP DAI driver %d\n", ret);
231 goto fw_trace_err;
232 }
233
234 ret = snd_sof_machine_register(sdev, plat_data);
235 if (ret < 0) {
236 dev_err(sdev->dev,
237 "error: failed to register machine driver %d\n", ret);
238 goto fw_trace_err;
239 }
240
241
242
243
244
245
246 if (!sof_ops(sdev)->runtime_suspend || !sof_ops(sdev)->runtime_resume)
247 pm_runtime_get_noresume(sdev->dev);
248
249 if (plat_data->sof_probe_complete)
250 plat_data->sof_probe_complete(sdev->dev);
251
252 sdev->probe_completed = true;
253
254 return 0;
255
256fw_trace_err:
257 snd_sof_free_trace(sdev);
258fw_run_err:
259 snd_sof_fw_unload(sdev);
260fw_load_err:
261 snd_sof_ipc_free(sdev);
262ipc_err:
263dbg_err:
264 snd_sof_free_debug(sdev);
265dsp_err:
266 snd_sof_remove(sdev);
267
268
269 sdev->fw_state = SOF_FW_BOOT_NOT_STARTED;
270 sdev->first_boot = true;
271
272 return ret;
273}
274
275static void sof_probe_work(struct work_struct *work)
276{
277 struct snd_sof_dev *sdev =
278 container_of(work, struct snd_sof_dev, probe_work);
279 int ret;
280
281 ret = sof_probe_continue(sdev);
282 if (ret < 0) {
283
284 dev_err(sdev->dev, "error: %s failed err: %d\n", __func__, ret);
285 }
286}
287
288int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
289{
290 struct snd_sof_dev *sdev;
291
292 sdev = devm_kzalloc(dev, sizeof(*sdev), GFP_KERNEL);
293 if (!sdev)
294 return -ENOMEM;
295
296
297 sdev->dev = dev;
298
299
300 sdev->dsp_power_state.state = SOF_DSP_PM_D0;
301
302 sdev->pdata = plat_data;
303 sdev->first_boot = true;
304 sdev->fw_state = SOF_FW_BOOT_NOT_STARTED;
305#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
306 sdev->extractor_stream_tag = SOF_PROBE_INVALID_NODE_ID;
307#endif
308 dev_set_drvdata(dev, sdev);
309
310
311 if (!sof_ops(sdev) || !sof_ops(sdev)->probe || !sof_ops(sdev)->run ||
312 !sof_ops(sdev)->block_read || !sof_ops(sdev)->block_write ||
313 !sof_ops(sdev)->send_msg || !sof_ops(sdev)->load_firmware ||
314 !sof_ops(sdev)->ipc_msg_data || !sof_ops(sdev)->ipc_pcm_params ||
315 !sof_ops(sdev)->fw_ready) {
316 dev_err(dev, "error: missing mandatory ops\n");
317 return -EINVAL;
318 }
319
320 INIT_LIST_HEAD(&sdev->pcm_list);
321 INIT_LIST_HEAD(&sdev->kcontrol_list);
322 INIT_LIST_HEAD(&sdev->widget_list);
323 INIT_LIST_HEAD(&sdev->dai_list);
324 INIT_LIST_HEAD(&sdev->route_list);
325 spin_lock_init(&sdev->ipc_lock);
326 spin_lock_init(&sdev->hw_lock);
327 mutex_init(&sdev->power_state_access);
328
329 if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE))
330 INIT_WORK(&sdev->probe_work, sof_probe_work);
331
332
333 if (plat_data->desc->ipc_timeout == 0)
334 sdev->ipc_timeout = TIMEOUT_DEFAULT_IPC_MS;
335 else
336 sdev->ipc_timeout = plat_data->desc->ipc_timeout;
337 if (plat_data->desc->boot_timeout == 0)
338 sdev->boot_timeout = TIMEOUT_DEFAULT_BOOT_MS;
339 else
340 sdev->boot_timeout = plat_data->desc->boot_timeout;
341
342 if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) {
343 schedule_work(&sdev->probe_work);
344 return 0;
345 }
346
347 return sof_probe_continue(sdev);
348}
349EXPORT_SYMBOL(snd_sof_device_probe);
350
351bool snd_sof_device_probe_completed(struct device *dev)
352{
353 struct snd_sof_dev *sdev = dev_get_drvdata(dev);
354
355 return sdev->probe_completed;
356}
357EXPORT_SYMBOL(snd_sof_device_probe_completed);
358
359int snd_sof_device_remove(struct device *dev)
360{
361 struct snd_sof_dev *sdev = dev_get_drvdata(dev);
362 struct snd_sof_pdata *pdata = sdev->pdata;
363 int ret;
364
365 if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE))
366 cancel_work_sync(&sdev->probe_work);
367
368 if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) {
369 ret = snd_sof_dsp_power_down_notify(sdev);
370 if (ret < 0)
371 dev_warn(dev, "error: %d failed to prepare DSP for device removal",
372 ret);
373
374 snd_sof_fw_unload(sdev);
375 snd_sof_ipc_free(sdev);
376 snd_sof_free_debug(sdev);
377 snd_sof_free_trace(sdev);
378 }
379
380
381
382
383
384
385 snd_sof_machine_unregister(sdev, pdata);
386
387
388
389
390
391
392
393 if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED)
394 snd_sof_remove(sdev);
395
396
397 release_firmware(pdata->fw);
398 pdata->fw = NULL;
399
400 return 0;
401}
402EXPORT_SYMBOL(snd_sof_device_remove);
403
404int snd_sof_device_shutdown(struct device *dev)
405{
406 struct snd_sof_dev *sdev = dev_get_drvdata(dev);
407
408 if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE))
409 cancel_work_sync(&sdev->probe_work);
410
411 if (sdev->fw_state == SOF_FW_BOOT_COMPLETE)
412 return snd_sof_shutdown(sdev);
413
414 return 0;
415}
416EXPORT_SYMBOL(snd_sof_device_shutdown);
417
418MODULE_AUTHOR("Liam Girdwood");
419MODULE_DESCRIPTION("Sound Open Firmware (SOF) Core");
420MODULE_LICENSE("Dual BSD/GPL");
421MODULE_ALIAS("platform:sof-audio");
422