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 dbg_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 goto fw_trace_err;
237
238
239
240
241
242
243 if (!sof_ops(sdev)->runtime_suspend || !sof_ops(sdev)->runtime_resume)
244 pm_runtime_get_noresume(sdev->dev);
245
246 if (plat_data->sof_probe_complete)
247 plat_data->sof_probe_complete(sdev->dev);
248
249 return 0;
250
251fw_trace_err:
252 snd_sof_free_trace(sdev);
253fw_run_err:
254 snd_sof_fw_unload(sdev);
255fw_load_err:
256 snd_sof_ipc_free(sdev);
257ipc_err:
258 snd_sof_free_debug(sdev);
259dbg_err:
260 snd_sof_remove(sdev);
261
262
263 sdev->fw_state = SOF_FW_BOOT_NOT_STARTED;
264 sdev->first_boot = true;
265
266 return ret;
267}
268
269static void sof_probe_work(struct work_struct *work)
270{
271 struct snd_sof_dev *sdev =
272 container_of(work, struct snd_sof_dev, probe_work);
273 int ret;
274
275 ret = sof_probe_continue(sdev);
276 if (ret < 0) {
277
278 dev_err(sdev->dev, "error: %s failed err: %d\n", __func__, ret);
279 }
280}
281
282int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
283{
284 struct snd_sof_dev *sdev;
285
286 sdev = devm_kzalloc(dev, sizeof(*sdev), GFP_KERNEL);
287 if (!sdev)
288 return -ENOMEM;
289
290
291 sdev->dev = dev;
292
293
294 sdev->dsp_power_state.state = SOF_DSP_PM_D0;
295
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;
301#endif
302 dev_set_drvdata(dev, sdev);
303
304
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)
310 return -EINVAL;
311
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);
319
320 if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE))
321 INIT_WORK(&sdev->probe_work, sof_probe_work);
322
323
324 if (plat_data->desc->ipc_timeout == 0)
325 sdev->ipc_timeout = TIMEOUT_DEFAULT_IPC_MS;
326 else
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;
330 else
331 sdev->boot_timeout = plat_data->desc->boot_timeout;
332
333 if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) {
334 schedule_work(&sdev->probe_work);
335 return 0;
336 }
337
338 return sof_probe_continue(sdev);
339}
340EXPORT_SYMBOL(snd_sof_device_probe);
341
342int snd_sof_device_remove(struct device *dev)
343{
344 struct snd_sof_dev *sdev = dev_get_drvdata(dev);
345 struct snd_sof_pdata *pdata = sdev->pdata;
346 int ret;
347
348 if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE))
349 cancel_work_sync(&sdev->probe_work);
350
351 if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) {
352 ret = snd_sof_dsp_power_down_notify(sdev);
353 if (ret < 0)
354 dev_warn(dev, "error: %d failed to prepare DSP for device removal",
355 ret);
356
357 snd_sof_fw_unload(sdev);
358 snd_sof_ipc_free(sdev);
359 snd_sof_free_debug(sdev);
360 snd_sof_free_trace(sdev);
361 }
362
363
364
365
366
367
368 snd_sof_machine_unregister(sdev, pdata);
369
370
371
372
373
374
375
376 if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED)
377 snd_sof_remove(sdev);
378
379
380 release_firmware(pdata->fw);
381 pdata->fw = NULL;
382
383 return 0;
384}
385EXPORT_SYMBOL(snd_sof_device_remove);
386
387MODULE_AUTHOR("Liam Girdwood");
388MODULE_DESCRIPTION("Sound Open Firmware (SOF) Core");
389MODULE_LICENSE("Dual BSD/GPL");
390MODULE_ALIAS("platform:sof-audio");
391