ASoC: SOF: Intel: add namespace for HDA_COMMON
[platform/kernel/linux-rpi.git] / sound / soc / sof / core.c
1 // SPDX-License-Identifier: (GPL-2.0 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 "ops.h"
17
18 /* see SOF_DBG_ flags */
19 int sof_core_debug;
20 module_param_named(sof_debug, sof_core_debug, int, 0444);
21 MODULE_PARM_DESC(sof_debug, "SOF core debug options (0x0 all off)");
22
23 /* SOF defaults if not provided by the platform in ms */
24 #define TIMEOUT_DEFAULT_IPC_MS  500
25 #define TIMEOUT_DEFAULT_BOOT_MS 2000
26
27 /*
28  * FW Panic/fault handling.
29  */
30
31 struct sof_panic_msg {
32         u32 id;
33         const char *msg;
34 };
35
36 /* standard FW panic types */
37 static const struct sof_panic_msg panic_msg[] = {
38         {SOF_IPC_PANIC_MEM, "out of memory"},
39         {SOF_IPC_PANIC_WORK, "work subsystem init failed"},
40         {SOF_IPC_PANIC_IPC, "IPC subsystem init failed"},
41         {SOF_IPC_PANIC_ARCH, "arch init failed"},
42         {SOF_IPC_PANIC_PLATFORM, "platform init failed"},
43         {SOF_IPC_PANIC_TASK, "scheduler init failed"},
44         {SOF_IPC_PANIC_EXCEPTION, "runtime exception"},
45         {SOF_IPC_PANIC_DEADLOCK, "deadlock"},
46         {SOF_IPC_PANIC_STACK, "stack overflow"},
47         {SOF_IPC_PANIC_IDLE, "can't enter idle"},
48         {SOF_IPC_PANIC_WFI, "invalid wait state"},
49         {SOF_IPC_PANIC_ASSERT, "assertion failed"},
50 };
51
52 /*
53  * helper to be called from .dbg_dump callbacks. No error code is
54  * provided, it's left as an exercise for the caller of .dbg_dump
55  * (typically IPC or loader)
56  */
57 void snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code,
58                         u32 tracep_code, void *oops,
59                         struct sof_ipc_panic_info *panic_info,
60                         void *stack, size_t stack_words)
61 {
62         u32 code;
63         int i;
64
65         /* is firmware dead ? */
66         if ((panic_code & SOF_IPC_PANIC_MAGIC_MASK) != SOF_IPC_PANIC_MAGIC) {
67                 dev_err(sdev->dev, "error: unexpected fault 0x%8.8x trace 0x%8.8x\n",
68                         panic_code, tracep_code);
69                 return; /* no fault ? */
70         }
71
72         code = panic_code & (SOF_IPC_PANIC_MAGIC_MASK | SOF_IPC_PANIC_CODE_MASK);
73
74         for (i = 0; i < ARRAY_SIZE(panic_msg); i++) {
75                 if (panic_msg[i].id == code) {
76                         dev_err(sdev->dev, "error: %s\n", panic_msg[i].msg);
77                         dev_err(sdev->dev, "error: trace point %8.8x\n",
78                                 tracep_code);
79                         goto out;
80                 }
81         }
82
83         /* unknown error */
84         dev_err(sdev->dev, "error: unknown reason %8.8x\n", panic_code);
85         dev_err(sdev->dev, "error: trace point %8.8x\n", tracep_code);
86
87 out:
88         dev_err(sdev->dev, "error: panic at %s:%d\n",
89                 panic_info->filename, panic_info->linenum);
90         sof_oops(sdev, oops);
91         sof_stack(sdev, oops, stack, stack_words);
92 }
93 EXPORT_SYMBOL(snd_sof_get_status);
94
95 static int sof_probe_continue(struct snd_sof_dev *sdev)
96 {
97         struct snd_sof_pdata *plat_data = sdev->pdata;
98         int ret;
99
100         /* probe the DSP hardware */
101         ret = snd_sof_probe(sdev);
102         if (ret < 0) {
103                 dev_err(sdev->dev, "error: failed to probe DSP %d\n", ret);
104                 return ret;
105         }
106
107         /* check machine info */
108         ret = sof_machine_check(sdev);
109         if (ret < 0) {
110                 dev_err(sdev->dev, "error: failed to get machine info %d\n",
111                         ret);
112                 goto dbg_err;
113         }
114
115         /* set up platform component driver */
116         snd_sof_new_platform_drv(sdev);
117
118         /* register any debug/trace capabilities */
119         ret = snd_sof_dbg_init(sdev);
120         if (ret < 0) {
121                 /*
122                  * debugfs issues are suppressed in snd_sof_dbg_init() since
123                  * we cannot rely on debugfs
124                  * here we trap errors due to memory allocation only.
125                  */
126                 dev_err(sdev->dev, "error: failed to init DSP trace/debug %d\n",
127                         ret);
128                 goto dbg_err;
129         }
130
131         /* init the IPC */
132         sdev->ipc = snd_sof_ipc_init(sdev);
133         if (!sdev->ipc) {
134                 dev_err(sdev->dev, "error: failed to init DSP IPC %d\n", ret);
135                 goto ipc_err;
136         }
137
138         /* load the firmware */
139         ret = snd_sof_load_firmware(sdev);
140         if (ret < 0) {
141                 dev_err(sdev->dev, "error: failed to load DSP firmware %d\n",
142                         ret);
143                 goto fw_load_err;
144         }
145
146         /* boot the firmware */
147         ret = snd_sof_run_firmware(sdev);
148         if (ret < 0) {
149                 dev_err(sdev->dev, "error: failed to boot DSP firmware %d\n",
150                         ret);
151                 goto fw_run_err;
152         }
153
154         if (IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE) ||
155             (sof_core_debug & SOF_DBG_ENABLE_TRACE)) {
156                 sdev->dtrace_is_supported = true;
157
158                 /* init DMA trace */
159                 ret = snd_sof_init_trace(sdev);
160                 if (ret < 0) {
161                         /* non fatal */
162                         dev_warn(sdev->dev,
163                                  "warning: failed to initialize trace %d\n",
164                                  ret);
165                 }
166         } else {
167                 dev_dbg(sdev->dev, "SOF firmware trace disabled\n");
168         }
169
170         /* hereafter all FW boot flows are for PM reasons */
171         sdev->first_boot = false;
172
173         /* now register audio DSP platform driver and dai */
174         ret = devm_snd_soc_register_component(sdev->dev, &sdev->plat_drv,
175                                               sof_ops(sdev)->drv,
176                                               sof_ops(sdev)->num_drv);
177         if (ret < 0) {
178                 dev_err(sdev->dev,
179                         "error: failed to register DSP DAI driver %d\n", ret);
180                 goto fw_run_err;
181         }
182
183         ret = snd_sof_machine_register(sdev, plat_data);
184         if (ret < 0)
185                 goto fw_run_err;
186
187         /*
188          * Some platforms in SOF, ex: BYT, may not have their platform PM
189          * callbacks set. Increment the usage count so as to
190          * prevent the device from entering runtime suspend.
191          */
192         if (!sof_ops(sdev)->runtime_suspend || !sof_ops(sdev)->runtime_resume)
193                 pm_runtime_get_noresume(sdev->dev);
194
195         if (plat_data->sof_probe_complete)
196                 plat_data->sof_probe_complete(sdev->dev);
197
198         return 0;
199
200 #if !IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)
201 fw_run_err:
202         snd_sof_fw_unload(sdev);
203 fw_load_err:
204         snd_sof_ipc_free(sdev);
205 ipc_err:
206         snd_sof_free_debug(sdev);
207 dbg_err:
208         snd_sof_remove(sdev);
209 #else
210
211         /*
212          * when the probe_continue is handled in a work queue, the
213          * probe does not fail so we don't release resources here.
214          * They will be released with an explicit call to
215          * snd_sof_device_remove() when the PCI/ACPI device is removed
216          */
217
218 fw_run_err:
219 fw_load_err:
220 ipc_err:
221 dbg_err:
222
223 #endif
224
225         return ret;
226 }
227
228 static void sof_probe_work(struct work_struct *work)
229 {
230         struct snd_sof_dev *sdev =
231                 container_of(work, struct snd_sof_dev, probe_work);
232         int ret;
233
234         ret = sof_probe_continue(sdev);
235         if (ret < 0) {
236                 /* errors cannot be propagated, log */
237                 dev_err(sdev->dev, "error: %s failed err: %d\n", __func__, ret);
238         }
239 }
240
241 int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
242 {
243         struct snd_sof_dev *sdev;
244
245         sdev = devm_kzalloc(dev, sizeof(*sdev), GFP_KERNEL);
246         if (!sdev)
247                 return -ENOMEM;
248
249         /* initialize sof device */
250         sdev->dev = dev;
251
252         /* initialize default D0 sub-state */
253         sdev->d0_substate = SOF_DSP_D0I0;
254
255         sdev->pdata = plat_data;
256         sdev->first_boot = true;
257         dev_set_drvdata(dev, sdev);
258
259         /* check all mandatory ops */
260         if (!sof_ops(sdev) || !sof_ops(sdev)->probe || !sof_ops(sdev)->run ||
261             !sof_ops(sdev)->block_read || !sof_ops(sdev)->block_write ||
262             !sof_ops(sdev)->send_msg || !sof_ops(sdev)->load_firmware ||
263             !sof_ops(sdev)->ipc_msg_data || !sof_ops(sdev)->ipc_pcm_params ||
264             !sof_ops(sdev)->fw_ready)
265                 return -EINVAL;
266
267         INIT_LIST_HEAD(&sdev->pcm_list);
268         INIT_LIST_HEAD(&sdev->kcontrol_list);
269         INIT_LIST_HEAD(&sdev->widget_list);
270         INIT_LIST_HEAD(&sdev->dai_list);
271         INIT_LIST_HEAD(&sdev->route_list);
272         spin_lock_init(&sdev->ipc_lock);
273         spin_lock_init(&sdev->hw_lock);
274
275         if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE))
276                 INIT_WORK(&sdev->probe_work, sof_probe_work);
277
278         /* set default timeouts if none provided */
279         if (plat_data->desc->ipc_timeout == 0)
280                 sdev->ipc_timeout = TIMEOUT_DEFAULT_IPC_MS;
281         else
282                 sdev->ipc_timeout = plat_data->desc->ipc_timeout;
283         if (plat_data->desc->boot_timeout == 0)
284                 sdev->boot_timeout = TIMEOUT_DEFAULT_BOOT_MS;
285         else
286                 sdev->boot_timeout = plat_data->desc->boot_timeout;
287
288         if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) {
289                 schedule_work(&sdev->probe_work);
290                 return 0;
291         }
292
293         return sof_probe_continue(sdev);
294 }
295 EXPORT_SYMBOL(snd_sof_device_probe);
296
297 int snd_sof_device_remove(struct device *dev)
298 {
299         struct snd_sof_dev *sdev = dev_get_drvdata(dev);
300         struct snd_sof_pdata *pdata = sdev->pdata;
301
302         if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE))
303                 cancel_work_sync(&sdev->probe_work);
304
305         snd_sof_fw_unload(sdev);
306         snd_sof_ipc_free(sdev);
307         snd_sof_free_debug(sdev);
308         snd_sof_free_trace(sdev);
309
310         /*
311          * Unregister machine driver. This will unbind the snd_card which
312          * will remove the component driver and unload the topology
313          * before freeing the snd_card.
314          */
315         snd_sof_machine_unregister(sdev, pdata);
316         /*
317          * Unregistering the machine driver results in unloading the topology.
318          * Some widgets, ex: scheduler, attempt to power down the core they are
319          * scheduled on, when they are unloaded. Therefore, the DSP must be
320          * removed only after the topology has been unloaded.
321          */
322         snd_sof_remove(sdev);
323
324         /* release firmware */
325         release_firmware(pdata->fw);
326         pdata->fw = NULL;
327
328         return 0;
329 }
330 EXPORT_SYMBOL(snd_sof_device_remove);
331
332 MODULE_AUTHOR("Liam Girdwood");
333 MODULE_DESCRIPTION("Sound Open Firmware (SOF) Core");
334 MODULE_LICENSE("Dual BSD/GPL");
335 MODULE_ALIAS("platform:sof-audio");