]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - sound/soc/sof/loader.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
[mirror_ubuntu-jammy-kernel.git] / sound / soc / sof / loader.c
CommitLineData
70cd5254
LG
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// Generic firmware loader.
11//
12
13#include <linux/firmware.h>
14#include <sound/sof.h>
15#include "ops.h"
16
17static int get_ext_windows(struct snd_sof_dev *sdev,
18 struct sof_ipc_ext_data_hdr *ext_hdr)
19{
20 struct sof_ipc_window *w =
21 container_of(ext_hdr, struct sof_ipc_window, ext_hdr);
70cd5254
LG
22
23 if (w->num_windows == 0 || w->num_windows > SOF_IPC_MAX_ELEMS)
24 return -EINVAL;
25
70cd5254 26 /* keep a local copy of the data */
b11c5b5e
GS
27 sdev->info_window = kmemdup(w, struct_size(w, window, w->num_windows),
28 GFP_KERNEL);
70cd5254
LG
29 if (!sdev->info_window)
30 return -ENOMEM;
31
32 return 0;
33}
34
35/* parse the extended FW boot data structures from FW boot message */
36int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset)
37{
38 struct sof_ipc_ext_data_hdr *ext_hdr;
39 void *ext_data;
40 int ret = 0;
41
42 ext_data = kzalloc(PAGE_SIZE, GFP_KERNEL);
43 if (!ext_data)
44 return -ENOMEM;
45
46 /* get first header */
47 snd_sof_dsp_block_read(sdev, bar, offset, ext_data,
48 sizeof(*ext_hdr));
49 ext_hdr = ext_data;
50
51 while (ext_hdr->hdr.cmd == SOF_IPC_FW_READY) {
52 /* read in ext structure */
6bb03c21 53 snd_sof_dsp_block_read(sdev, bar, offset + sizeof(*ext_hdr),
70cd5254
LG
54 (void *)((u8 *)ext_data + sizeof(*ext_hdr)),
55 ext_hdr->hdr.size - sizeof(*ext_hdr));
56
57 dev_dbg(sdev->dev, "found ext header type %d size 0x%x\n",
58 ext_hdr->type, ext_hdr->hdr.size);
59
60 /* process structure data */
61 switch (ext_hdr->type) {
62 case SOF_IPC_EXT_DMA_BUFFER:
6bb03c21 63 ret = 0;
70cd5254
LG
64 break;
65 case SOF_IPC_EXT_WINDOW:
66 ret = get_ext_windows(sdev, ext_hdr);
67 break;
68 default:
8edc9566
KT
69 dev_warn(sdev->dev, "warning: unknown ext header type %d size 0x%x\n",
70 ext_hdr->type, ext_hdr->hdr.size);
6bb03c21 71 ret = 0;
70cd5254
LG
72 break;
73 }
74
75 if (ret < 0) {
76 dev_err(sdev->dev, "error: failed to parse ext data type %d\n",
77 ext_hdr->type);
78 break;
79 }
80
81 /* move to next header */
82 offset += ext_hdr->hdr.size;
83 snd_sof_dsp_block_read(sdev, bar, offset, ext_data,
84 sizeof(*ext_hdr));
85 ext_hdr = ext_data;
86 }
87
88 kfree(ext_data);
89 return ret;
90}
91EXPORT_SYMBOL(snd_sof_fw_parse_ext_data);
92
83ee7ab1
DB
93/*
94 * IPC Firmware ready.
95 */
96static void sof_get_windows(struct snd_sof_dev *sdev)
97{
98 struct sof_ipc_window_elem *elem;
99 u32 outbox_offset = 0;
100 u32 stream_offset = 0;
101 u32 inbox_offset = 0;
102 u32 outbox_size = 0;
103 u32 stream_size = 0;
104 u32 inbox_size = 0;
105 int window_offset;
106 int bar;
107 int i;
108
109 if (!sdev->info_window) {
110 dev_err(sdev->dev, "error: have no window info\n");
111 return;
112 }
113
114 bar = snd_sof_dsp_get_bar_index(sdev, SOF_FW_BLK_TYPE_SRAM);
115 if (bar < 0) {
116 dev_err(sdev->dev, "error: have no bar mapping\n");
117 return;
118 }
119
120 for (i = 0; i < sdev->info_window->num_windows; i++) {
121 elem = &sdev->info_window->window[i];
122
123 window_offset = snd_sof_dsp_get_window_offset(sdev, elem->id);
124 if (window_offset < 0) {
125 dev_warn(sdev->dev, "warn: no offset for window %d\n",
126 elem->id);
127 continue;
128 }
129
130 switch (elem->type) {
131 case SOF_IPC_REGION_UPBOX:
132 inbox_offset = window_offset + elem->offset;
133 inbox_size = elem->size;
134 snd_sof_debugfs_io_item(sdev,
135 sdev->bar[bar] +
136 inbox_offset,
137 elem->size, "inbox",
138 SOF_DEBUGFS_ACCESS_D0_ONLY);
139 break;
140 case SOF_IPC_REGION_DOWNBOX:
141 outbox_offset = window_offset + elem->offset;
142 outbox_size = elem->size;
143 snd_sof_debugfs_io_item(sdev,
144 sdev->bar[bar] +
145 outbox_offset,
146 elem->size, "outbox",
147 SOF_DEBUGFS_ACCESS_D0_ONLY);
148 break;
149 case SOF_IPC_REGION_TRACE:
150 snd_sof_debugfs_io_item(sdev,
151 sdev->bar[bar] +
152 window_offset +
153 elem->offset,
154 elem->size, "etrace",
155 SOF_DEBUGFS_ACCESS_D0_ONLY);
156 break;
157 case SOF_IPC_REGION_DEBUG:
158 snd_sof_debugfs_io_item(sdev,
159 sdev->bar[bar] +
160 window_offset +
161 elem->offset,
162 elem->size, "debug",
163 SOF_DEBUGFS_ACCESS_D0_ONLY);
164 break;
165 case SOF_IPC_REGION_STREAM:
166 stream_offset = window_offset + elem->offset;
167 stream_size = elem->size;
168 snd_sof_debugfs_io_item(sdev,
169 sdev->bar[bar] +
170 stream_offset,
171 elem->size, "stream",
172 SOF_DEBUGFS_ACCESS_D0_ONLY);
173 break;
174 case SOF_IPC_REGION_REGS:
175 snd_sof_debugfs_io_item(sdev,
176 sdev->bar[bar] +
177 window_offset +
178 elem->offset,
179 elem->size, "regs",
180 SOF_DEBUGFS_ACCESS_D0_ONLY);
181 break;
182 case SOF_IPC_REGION_EXCEPTION:
183 sdev->dsp_oops_offset = window_offset + elem->offset;
184 snd_sof_debugfs_io_item(sdev,
185 sdev->bar[bar] +
186 window_offset +
187 elem->offset,
188 elem->size, "exception",
189 SOF_DEBUGFS_ACCESS_D0_ONLY);
190 break;
191 default:
192 dev_err(sdev->dev, "error: get illegal window info\n");
193 return;
194 }
195 }
196
197 if (outbox_size == 0 || inbox_size == 0) {
198 dev_err(sdev->dev, "error: get illegal mailbox window\n");
199 return;
200 }
201
202 snd_sof_dsp_mailbox_init(sdev, inbox_offset, inbox_size,
203 outbox_offset, outbox_size);
204 sdev->stream_box.offset = stream_offset;
205 sdev->stream_box.size = stream_size;
206
207 dev_dbg(sdev->dev, " mailbox upstream 0x%x - size 0x%x\n",
208 inbox_offset, inbox_size);
209 dev_dbg(sdev->dev, " mailbox downstream 0x%x - size 0x%x\n",
210 outbox_offset, outbox_size);
211 dev_dbg(sdev->dev, " stream region 0x%x - size 0x%x\n",
212 stream_offset, stream_size);
213}
214
215/* check for ABI compatibility and create memory windows on first boot */
216int sof_fw_ready(struct snd_sof_dev *sdev, u32 msg_id)
217{
218 struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready;
219 int offset;
220 int bar;
221 int ret;
222
223 /* mailbox must be on 4k boundary */
224 offset = snd_sof_dsp_get_mailbox_offset(sdev);
225 if (offset < 0) {
226 dev_err(sdev->dev, "error: have no mailbox offset\n");
227 return offset;
228 }
229
230 bar = snd_sof_dsp_get_bar_index(sdev, SOF_FW_BLK_TYPE_SRAM);
231 if (bar < 0) {
232 dev_err(sdev->dev, "error: have no bar mapping\n");
233 return -EINVAL;
234 }
235
236 dev_dbg(sdev->dev, "ipc: DSP is ready 0x%8.8x offset 0x%x\n",
237 msg_id, offset);
238
239 /* no need to re-check version/ABI for subsequent boots */
240 if (!sdev->first_boot)
241 return 0;
242
243 /* copy data from the DSP FW ready offset */
244 sof_block_read(sdev, bar, offset, fw_ready, sizeof(*fw_ready));
245
246 /* make sure ABI version is compatible */
247 ret = snd_sof_ipc_valid(sdev);
248 if (ret < 0)
249 return ret;
250
251 /* now check for extended data */
252 snd_sof_fw_parse_ext_data(sdev, bar, offset +
253 sizeof(struct sof_ipc_fw_ready));
254
255 sof_get_windows(sdev);
256
257 return 0;
258}
259EXPORT_SYMBOL(sof_fw_ready);
260
70cd5254
LG
261/* generic module parser for mmaped DSPs */
262int snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev,
263 struct snd_sof_mod_hdr *module)
264{
265 struct snd_sof_blk_hdr *block;
7198879e 266 int count, bar;
70cd5254
LG
267 u32 offset;
268 size_t remaining;
269
270 dev_dbg(sdev->dev, "new module size 0x%x blocks 0x%x type 0x%x\n",
271 module->size, module->num_blocks, module->type);
272
273 block = (struct snd_sof_blk_hdr *)((u8 *)module + sizeof(*module));
274
275 /* module->size doesn't include header size */
276 remaining = module->size;
277 for (count = 0; count < module->num_blocks; count++) {
278 /* check for wrap */
279 if (remaining < sizeof(*block)) {
280 dev_err(sdev->dev, "error: not enough data remaining\n");
281 return -EINVAL;
282 }
283
284 /* minus header size of block */
285 remaining -= sizeof(*block);
286
287 if (block->size == 0) {
288 dev_warn(sdev->dev,
289 "warning: block %d size zero\n", count);
290 dev_warn(sdev->dev, " type 0x%x offset 0x%x\n",
291 block->type, block->offset);
292 continue;
293 }
294
295 switch (block->type) {
296 case SOF_FW_BLK_TYPE_RSRVD0:
441c58cf 297 case SOF_FW_BLK_TYPE_ROM...SOF_FW_BLK_TYPE_RSRVD14:
70cd5254
LG
298 continue; /* not handled atm */
299 case SOF_FW_BLK_TYPE_IRAM:
300 case SOF_FW_BLK_TYPE_DRAM:
441c58cf 301 case SOF_FW_BLK_TYPE_SRAM:
70cd5254 302 offset = block->offset;
7198879e
DB
303 bar = snd_sof_dsp_get_bar_index(sdev, block->type);
304 if (bar < 0) {
305 dev_err(sdev->dev,
306 "error: no BAR mapping for block type 0x%x\n",
307 block->type);
308 return bar;
309 }
70cd5254
LG
310 break;
311 default:
312 dev_err(sdev->dev, "error: bad type 0x%x for block 0x%x\n",
313 block->type, count);
314 return -EINVAL;
315 }
316
317 dev_dbg(sdev->dev,
318 "block %d type 0x%x size 0x%x ==> offset 0x%x\n",
319 count, block->type, block->size, offset);
320
321 /* checking block->size to avoid unaligned access */
322 if (block->size % sizeof(u32)) {
323 dev_err(sdev->dev, "error: invalid block size 0x%x\n",
324 block->size);
325 return -EINVAL;
326 }
7198879e 327 snd_sof_dsp_block_write(sdev, bar, offset,
70cd5254
LG
328 block + 1, block->size);
329
330 if (remaining < block->size) {
331 dev_err(sdev->dev, "error: not enough data remaining\n");
332 return -EINVAL;
333 }
334
335 /* minus body size of block */
336 remaining -= block->size;
337 /* next block */
338 block = (struct snd_sof_blk_hdr *)((u8 *)block + sizeof(*block)
339 + block->size);
340 }
341
342 return 0;
343}
344EXPORT_SYMBOL(snd_sof_parse_module_memcpy);
345
346static int check_header(struct snd_sof_dev *sdev, const struct firmware *fw)
347{
348 struct snd_sof_fw_header *header;
349
350 /* Read the header information from the data pointer */
351 header = (struct snd_sof_fw_header *)fw->data;
352
353 /* verify FW sig */
354 if (strncmp(header->sig, SND_SOF_FW_SIG, SND_SOF_FW_SIG_SIZE) != 0) {
355 dev_err(sdev->dev, "error: invalid firmware signature\n");
356 return -EINVAL;
357 }
358
359 /* check size is valid */
360 if (fw->size != header->file_size + sizeof(*header)) {
361 dev_err(sdev->dev, "error: invalid filesize mismatch got 0x%zx expected 0x%zx\n",
362 fw->size, header->file_size + sizeof(*header));
363 return -EINVAL;
364 }
365
366 dev_dbg(sdev->dev, "header size=0x%x modules=0x%x abi=0x%x size=%zu\n",
367 header->file_size, header->num_modules,
368 header->abi, sizeof(*header));
369
370 return 0;
371}
372
373static int load_modules(struct snd_sof_dev *sdev, const struct firmware *fw)
374{
375 struct snd_sof_fw_header *header;
376 struct snd_sof_mod_hdr *module;
377 int (*load_module)(struct snd_sof_dev *sof_dev,
378 struct snd_sof_mod_hdr *hdr);
379 int ret, count;
380 size_t remaining;
381
382 header = (struct snd_sof_fw_header *)fw->data;
383 load_module = sof_ops(sdev)->load_module;
384 if (!load_module)
385 return -EINVAL;
386
387 /* parse each module */
388 module = (struct snd_sof_mod_hdr *)((u8 *)(fw->data) + sizeof(*header));
389 remaining = fw->size - sizeof(*header);
390 /* check for wrap */
391 if (remaining > fw->size) {
392 dev_err(sdev->dev, "error: fw size smaller than header size\n");
393 return -EINVAL;
394 }
395
396 for (count = 0; count < header->num_modules; count++) {
397 /* check for wrap */
398 if (remaining < sizeof(*module)) {
399 dev_err(sdev->dev, "error: not enough data remaining\n");
400 return -EINVAL;
401 }
402
403 /* minus header size of module */
404 remaining -= sizeof(*module);
405
406 /* module */
407 ret = load_module(sdev, module);
408 if (ret < 0) {
409 dev_err(sdev->dev, "error: invalid module %d\n", count);
410 return ret;
411 }
412
413 if (remaining < module->size) {
414 dev_err(sdev->dev, "error: not enough data remaining\n");
415 return -EINVAL;
416 }
417
418 /* minus body size of module */
419 remaining -= module->size;
420 module = (struct snd_sof_mod_hdr *)((u8 *)module
421 + sizeof(*module) + module->size);
422 }
423
424 return 0;
425}
426
427int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev)
428{
429 struct snd_sof_pdata *plat_data = sdev->pdata;
430 const char *fw_filename;
431 int ret;
432
433 /* set code loading condition to true */
434 sdev->code_loading = 1;
435
436 /* Don't request firmware again if firmware is already requested */
437 if (plat_data->fw)
438 return 0;
439
440 fw_filename = kasprintf(GFP_KERNEL, "%s/%s",
441 plat_data->fw_filename_prefix,
442 plat_data->fw_filename);
443 if (!fw_filename)
444 return -ENOMEM;
445
446 ret = request_firmware(&plat_data->fw, fw_filename, sdev->dev);
447
448 if (ret < 0) {
449 dev_err(sdev->dev, "error: request firmware %s failed err: %d\n",
450 fw_filename, ret);
451 }
452
453 kfree(fw_filename);
454
455 return ret;
456}
457EXPORT_SYMBOL(snd_sof_load_firmware_raw);
458
459int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev)
460{
461 struct snd_sof_pdata *plat_data = sdev->pdata;
462 int ret;
463
464 ret = snd_sof_load_firmware_raw(sdev);
465 if (ret < 0)
466 return ret;
467
468 /* make sure the FW header and file is valid */
469 ret = check_header(sdev, plat_data->fw);
470 if (ret < 0) {
471 dev_err(sdev->dev, "error: invalid FW header\n");
472 goto error;
473 }
474
475 /* prepare the DSP for FW loading */
476 ret = snd_sof_dsp_reset(sdev);
477 if (ret < 0) {
478 dev_err(sdev->dev, "error: failed to reset DSP\n");
479 goto error;
480 }
481
482 /* parse and load firmware modules to DSP */
483 ret = load_modules(sdev, plat_data->fw);
484 if (ret < 0) {
485 dev_err(sdev->dev, "error: invalid FW modules\n");
486 goto error;
487 }
488
489 return 0;
490
491error:
492 release_firmware(plat_data->fw);
493 plat_data->fw = NULL;
494 return ret;
495
496}
497EXPORT_SYMBOL(snd_sof_load_firmware_memcpy);
498
499int snd_sof_load_firmware(struct snd_sof_dev *sdev)
500{
501 dev_dbg(sdev->dev, "loading firmware\n");
502
503 if (sof_ops(sdev)->load_firmware)
504 return sof_ops(sdev)->load_firmware(sdev);
505 return 0;
506}
507EXPORT_SYMBOL(snd_sof_load_firmware);
508
509int snd_sof_run_firmware(struct snd_sof_dev *sdev)
510{
511 int ret;
512 int init_core_mask;
513
514 init_waitqueue_head(&sdev->boot_wait);
515 sdev->boot_complete = false;
516
5c9714f6 517 /* create read-only fw_version debugfs to store boot version info */
70cd5254
LG
518 if (sdev->first_boot) {
519 ret = snd_sof_debugfs_buf_item(sdev, &sdev->fw_version,
520 sizeof(sdev->fw_version),
5c9714f6 521 "fw_version", 0444);
70cd5254
LG
522 /* errors are only due to memory allocation, not debugfs */
523 if (ret < 0) {
524 dev_err(sdev->dev, "error: snd_sof_debugfs_buf_item failed\n");
525 return ret;
526 }
527 }
528
529 /* perform pre fw run operations */
530 ret = snd_sof_dsp_pre_fw_run(sdev);
531 if (ret < 0) {
532 dev_err(sdev->dev, "error: failed pre fw run op\n");
533 return ret;
534 }
535
536 dev_dbg(sdev->dev, "booting DSP firmware\n");
537
538 /* boot the firmware on the DSP */
539 ret = snd_sof_dsp_run(sdev);
540 if (ret < 0) {
541 dev_err(sdev->dev, "error: failed to reset DSP\n");
542 return ret;
543 }
544
545 init_core_mask = ret;
546
547 /* now wait for the DSP to boot */
548 ret = wait_event_timeout(sdev->boot_wait, sdev->boot_complete,
549 msecs_to_jiffies(sdev->boot_timeout));
550 if (ret == 0) {
551 dev_err(sdev->dev, "error: firmware boot failure\n");
552 snd_sof_dsp_dbg_dump(sdev, SOF_DBG_REGS | SOF_DBG_MBOX |
553 SOF_DBG_TEXT | SOF_DBG_PCI);
79861488
PLB
554 /* after this point FW_READY msg should be ignored */
555 sdev->boot_complete = true;
70cd5254
LG
556 return -EIO;
557 }
558
559 dev_info(sdev->dev, "firmware boot complete\n");
560
561 /* perform post fw run operations */
562 ret = snd_sof_dsp_post_fw_run(sdev);
563 if (ret < 0) {
564 dev_err(sdev->dev, "error: failed post fw run op\n");
565 return ret;
566 }
567
568 /* fw boot is complete. Update the active cores mask */
569 sdev->enabled_cores_mask = init_core_mask;
570
571 return 0;
572}
573EXPORT_SYMBOL(snd_sof_run_firmware);
574
575void snd_sof_fw_unload(struct snd_sof_dev *sdev)
576{
577 /* TODO: support module unloading at runtime */
578}
579EXPORT_SYMBOL(snd_sof_fw_unload);