]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blame - sound/soc/sof/loader.c
Merge tag 'gfs2-for-5.9' of git://git.kernel.org/pub/scm/linux/kernel/git/gfs2/linux...
[mirror_ubuntu-hirsute-kernel.git] / sound / soc / sof / loader.c
CommitLineData
e149ca29 1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
70cd5254
LG
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>
a80cf198 15#include <sound/sof/ext_manifest.h>
70cd5254
LG
16#include "ops.h"
17
18static int get_ext_windows(struct snd_sof_dev *sdev,
0730c092 19 const struct sof_ipc_ext_data_hdr *ext_hdr)
70cd5254 20{
0730c092 21 const struct sof_ipc_window *w =
70cd5254 22 container_of(ext_hdr, struct sof_ipc_window, ext_hdr);
8d809c15 23 size_t w_size = struct_size(w, window, w->num_windows);
70cd5254
LG
24
25 if (w->num_windows == 0 || w->num_windows > SOF_IPC_MAX_ELEMS)
26 return -EINVAL;
27
8d809c15
KT
28 if (sdev->info_window) {
29 if (memcmp(sdev->info_window, w, w_size)) {
30 dev_err(sdev->dev, "error: mismatch between window descriptor from extended manifest and mailbox");
31 return -EINVAL;
32 }
33 return 0;
34 }
35
70cd5254 36 /* keep a local copy of the data */
8d809c15 37 sdev->info_window = kmemdup(w, w_size, GFP_KERNEL);
70cd5254
LG
38 if (!sdev->info_window)
39 return -ENOMEM;
40
41 return 0;
42}
43
59283959 44static int get_cc_info(struct snd_sof_dev *sdev,
0730c092 45 const struct sof_ipc_ext_data_hdr *ext_hdr)
59283959
KT
46{
47 int ret;
48
0730c092 49 const struct sof_ipc_cc_version *cc =
59283959
KT
50 container_of(ext_hdr, struct sof_ipc_cc_version, ext_hdr);
51
4c4a9751
KT
52 if (sdev->cc_version) {
53 if (memcmp(sdev->cc_version, cc, cc->ext_hdr.hdr.size)) {
54 dev_err(sdev->dev, "error: receive diverged cc_version descriptions");
55 return -EINVAL;
56 }
57 return 0;
58 }
59
59283959
KT
60 dev_dbg(sdev->dev, "Firmware info: used compiler %s %d:%d:%d%s used optimization flags %s\n",
61 cc->name, cc->major, cc->minor, cc->micro, cc->desc,
62 cc->optim);
63
64 /* create read-only cc_version debugfs to store compiler version info */
65 /* use local copy of the cc_version to prevent data corruption */
66 if (sdev->first_boot) {
67 sdev->cc_version = devm_kmalloc(sdev->dev, cc->ext_hdr.hdr.size,
68 GFP_KERNEL);
69
70 if (!sdev->cc_version)
71 return -ENOMEM;
72
73 memcpy(sdev->cc_version, cc, cc->ext_hdr.hdr.size);
74 ret = snd_sof_debugfs_buf_item(sdev, sdev->cc_version,
75 cc->ext_hdr.hdr.size,
76 "cc_version", 0444);
77
78 /* errors are only due to memory allocation, not debugfs */
79 if (ret < 0) {
80 dev_err(sdev->dev, "error: snd_sof_debugfs_buf_item failed\n");
81 return ret;
82 }
83 }
84
85 return 0;
86}
87
70cd5254
LG
88/* parse the extended FW boot data structures from FW boot message */
89int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset)
90{
91 struct sof_ipc_ext_data_hdr *ext_hdr;
92 void *ext_data;
93 int ret = 0;
94
95 ext_data = kzalloc(PAGE_SIZE, GFP_KERNEL);
96 if (!ext_data)
97 return -ENOMEM;
98
99 /* get first header */
100 snd_sof_dsp_block_read(sdev, bar, offset, ext_data,
101 sizeof(*ext_hdr));
102 ext_hdr = ext_data;
103
104 while (ext_hdr->hdr.cmd == SOF_IPC_FW_READY) {
105 /* read in ext structure */
6bb03c21 106 snd_sof_dsp_block_read(sdev, bar, offset + sizeof(*ext_hdr),
70cd5254
LG
107 (void *)((u8 *)ext_data + sizeof(*ext_hdr)),
108 ext_hdr->hdr.size - sizeof(*ext_hdr));
109
110 dev_dbg(sdev->dev, "found ext header type %d size 0x%x\n",
111 ext_hdr->type, ext_hdr->hdr.size);
112
113 /* process structure data */
114 switch (ext_hdr->type) {
70cd5254
LG
115 case SOF_IPC_EXT_WINDOW:
116 ret = get_ext_windows(sdev, ext_hdr);
117 break;
59283959
KT
118 case SOF_IPC_EXT_CC_INFO:
119 ret = get_cc_info(sdev, ext_hdr);
120 break;
70cd5254 121 default:
8edc9566
KT
122 dev_warn(sdev->dev, "warning: unknown ext header type %d size 0x%x\n",
123 ext_hdr->type, ext_hdr->hdr.size);
6bb03c21 124 ret = 0;
70cd5254
LG
125 break;
126 }
127
128 if (ret < 0) {
129 dev_err(sdev->dev, "error: failed to parse ext data type %d\n",
130 ext_hdr->type);
131 break;
132 }
133
134 /* move to next header */
135 offset += ext_hdr->hdr.size;
136 snd_sof_dsp_block_read(sdev, bar, offset, ext_data,
137 sizeof(*ext_hdr));
138 ext_hdr = ext_data;
139 }
140
141 kfree(ext_data);
142 return ret;
143}
144EXPORT_SYMBOL(snd_sof_fw_parse_ext_data);
145
3e2a89d3
KT
146static int ext_man_get_fw_version(struct snd_sof_dev *sdev,
147 const struct sof_ext_man_elem_header *hdr)
148{
149 const struct sof_ext_man_fw_version *v =
150 container_of(hdr, struct sof_ext_man_fw_version, hdr);
151
152 memcpy(&sdev->fw_ready.version, &v->version, sizeof(v->version));
153 sdev->fw_ready.flags = v->flags;
154
155 /* log ABI versions and check FW compatibility */
156 return snd_sof_ipc_valid(sdev);
157}
158
8d809c15
KT
159static int ext_man_get_windows(struct snd_sof_dev *sdev,
160 const struct sof_ext_man_elem_header *hdr)
161{
162 const struct sof_ext_man_window *w;
163
164 w = container_of(hdr, struct sof_ext_man_window, hdr);
165
166 return get_ext_windows(sdev, &w->ipc_window.ext_hdr);
167}
168
4c4a9751
KT
169static int ext_man_get_cc_info(struct snd_sof_dev *sdev,
170 const struct sof_ext_man_elem_header *hdr)
171{
172 const struct sof_ext_man_cc_version *cc;
173
174 cc = container_of(hdr, struct sof_ext_man_cc_version, hdr);
175
176 return get_cc_info(sdev, &cc->cc_version.ext_hdr);
177}
178
a80cf198
KT
179static ssize_t snd_sof_ext_man_size(const struct firmware *fw)
180{
181 const struct sof_ext_man_header *head;
182
183 head = (struct sof_ext_man_header *)fw->data;
184
185 /*
186 * assert fw size is big enough to contain extended manifest header,
187 * it prevents from reading unallocated memory from `head` in following
188 * step.
189 */
190 if (fw->size < sizeof(*head))
191 return -EINVAL;
192
193 /*
194 * When fw points to extended manifest,
195 * then first u32 must be equal SOF_EXT_MAN_MAGIC_NUMBER.
196 */
197 if (head->magic == SOF_EXT_MAN_MAGIC_NUMBER)
198 return head->full_size;
199
200 /* otherwise given fw don't have an extended manifest */
201 return 0;
202}
203
204/* parse extended FW manifest data structures */
205static int snd_sof_fw_ext_man_parse(struct snd_sof_dev *sdev,
206 const struct firmware *fw)
207{
208 const struct sof_ext_man_elem_header *elem_hdr;
209 const struct sof_ext_man_header *head;
210 ssize_t ext_man_size;
211 ssize_t remaining;
212 uintptr_t iptr;
213 int ret = 0;
214
215 head = (struct sof_ext_man_header *)fw->data;
216 remaining = head->full_size - head->header_size;
217 ext_man_size = snd_sof_ext_man_size(fw);
218
219 /* Assert firmware starts with extended manifest */
220 if (ext_man_size <= 0)
221 return ext_man_size;
222
223 /* incompatible version */
224 if (SOF_EXT_MAN_VERSION_INCOMPATIBLE(SOF_EXT_MAN_VERSION,
225 head->header_version)) {
226 dev_err(sdev->dev, "error: extended manifest version 0x%X differ from used 0x%X\n",
227 head->header_version, SOF_EXT_MAN_VERSION);
228 return -EINVAL;
229 }
230
231 /* get first extended manifest element header */
232 iptr = (uintptr_t)fw->data + head->header_size;
233
234 while (remaining > sizeof(*elem_hdr)) {
235 elem_hdr = (struct sof_ext_man_elem_header *)iptr;
236
237 dev_dbg(sdev->dev, "found sof_ext_man header type %d size 0x%X\n",
238 elem_hdr->type, elem_hdr->size);
239
240 if (elem_hdr->size < sizeof(*elem_hdr) ||
241 elem_hdr->size > remaining) {
242 dev_err(sdev->dev, "error: invalid sof_ext_man header size, type %d size 0x%X\n",
243 elem_hdr->type, elem_hdr->size);
244 return -EINVAL;
245 }
246
247 /* process structure data */
248 switch (elem_hdr->type) {
3e2a89d3
KT
249 case SOF_EXT_MAN_ELEM_FW_VERSION:
250 ret = ext_man_get_fw_version(sdev, elem_hdr);
251 break;
8d809c15
KT
252 case SOF_EXT_MAN_ELEM_WINDOW:
253 ret = ext_man_get_windows(sdev, elem_hdr);
254 break;
4c4a9751
KT
255 case SOF_EXT_MAN_ELEM_CC_VERSION:
256 ret = ext_man_get_cc_info(sdev, elem_hdr);
257 break;
a80cf198
KT
258 default:
259 dev_warn(sdev->dev, "warning: unknown sof_ext_man header type %d size 0x%X\n",
260 elem_hdr->type, elem_hdr->size);
261 break;
262 }
263
264 if (ret < 0) {
265 dev_err(sdev->dev, "error: failed to parse sof_ext_man header type %d size 0x%X\n",
266 elem_hdr->type, elem_hdr->size);
267 return ret;
268 }
269
270 remaining -= elem_hdr->size;
271 iptr += elem_hdr->size;
272 }
273
274 if (remaining) {
275 dev_err(sdev->dev, "error: sof_ext_man header is inconsistent\n");
276 return -EINVAL;
277 }
278
279 return ext_man_size;
280}
281
83ee7ab1
DB
282/*
283 * IPC Firmware ready.
284 */
285static void sof_get_windows(struct snd_sof_dev *sdev)
286{
287 struct sof_ipc_window_elem *elem;
288 u32 outbox_offset = 0;
289 u32 stream_offset = 0;
290 u32 inbox_offset = 0;
291 u32 outbox_size = 0;
292 u32 stream_size = 0;
293 u32 inbox_size = 0;
294 int window_offset;
295 int bar;
296 int i;
297
298 if (!sdev->info_window) {
299 dev_err(sdev->dev, "error: have no window info\n");
300 return;
301 }
302
303 bar = snd_sof_dsp_get_bar_index(sdev, SOF_FW_BLK_TYPE_SRAM);
304 if (bar < 0) {
305 dev_err(sdev->dev, "error: have no bar mapping\n");
306 return;
307 }
308
309 for (i = 0; i < sdev->info_window->num_windows; i++) {
310 elem = &sdev->info_window->window[i];
311
312 window_offset = snd_sof_dsp_get_window_offset(sdev, elem->id);
313 if (window_offset < 0) {
314 dev_warn(sdev->dev, "warn: no offset for window %d\n",
315 elem->id);
316 continue;
317 }
318
319 switch (elem->type) {
320 case SOF_IPC_REGION_UPBOX:
321 inbox_offset = window_offset + elem->offset;
322 inbox_size = elem->size;
323 snd_sof_debugfs_io_item(sdev,
324 sdev->bar[bar] +
325 inbox_offset,
326 elem->size, "inbox",
327 SOF_DEBUGFS_ACCESS_D0_ONLY);
328 break;
329 case SOF_IPC_REGION_DOWNBOX:
330 outbox_offset = window_offset + elem->offset;
331 outbox_size = elem->size;
332 snd_sof_debugfs_io_item(sdev,
333 sdev->bar[bar] +
334 outbox_offset,
335 elem->size, "outbox",
336 SOF_DEBUGFS_ACCESS_D0_ONLY);
337 break;
338 case SOF_IPC_REGION_TRACE:
339 snd_sof_debugfs_io_item(sdev,
340 sdev->bar[bar] +
341 window_offset +
342 elem->offset,
343 elem->size, "etrace",
344 SOF_DEBUGFS_ACCESS_D0_ONLY);
345 break;
346 case SOF_IPC_REGION_DEBUG:
347 snd_sof_debugfs_io_item(sdev,
348 sdev->bar[bar] +
349 window_offset +
350 elem->offset,
351 elem->size, "debug",
352 SOF_DEBUGFS_ACCESS_D0_ONLY);
353 break;
354 case SOF_IPC_REGION_STREAM:
355 stream_offset = window_offset + elem->offset;
356 stream_size = elem->size;
357 snd_sof_debugfs_io_item(sdev,
358 sdev->bar[bar] +
359 stream_offset,
360 elem->size, "stream",
361 SOF_DEBUGFS_ACCESS_D0_ONLY);
362 break;
363 case SOF_IPC_REGION_REGS:
364 snd_sof_debugfs_io_item(sdev,
365 sdev->bar[bar] +
366 window_offset +
367 elem->offset,
368 elem->size, "regs",
369 SOF_DEBUGFS_ACCESS_D0_ONLY);
370 break;
371 case SOF_IPC_REGION_EXCEPTION:
372 sdev->dsp_oops_offset = window_offset + elem->offset;
373 snd_sof_debugfs_io_item(sdev,
374 sdev->bar[bar] +
375 window_offset +
376 elem->offset,
377 elem->size, "exception",
378 SOF_DEBUGFS_ACCESS_D0_ONLY);
379 break;
380 default:
381 dev_err(sdev->dev, "error: get illegal window info\n");
382 return;
383 }
384 }
385
386 if (outbox_size == 0 || inbox_size == 0) {
387 dev_err(sdev->dev, "error: get illegal mailbox window\n");
388 return;
389 }
390
391 snd_sof_dsp_mailbox_init(sdev, inbox_offset, inbox_size,
392 outbox_offset, outbox_size);
393 sdev->stream_box.offset = stream_offset;
394 sdev->stream_box.size = stream_size;
395
396 dev_dbg(sdev->dev, " mailbox upstream 0x%x - size 0x%x\n",
397 inbox_offset, inbox_size);
398 dev_dbg(sdev->dev, " mailbox downstream 0x%x - size 0x%x\n",
399 outbox_offset, outbox_size);
400 dev_dbg(sdev->dev, " stream region 0x%x - size 0x%x\n",
401 stream_offset, stream_size);
402}
403
404/* check for ABI compatibility and create memory windows on first boot */
405int sof_fw_ready(struct snd_sof_dev *sdev, u32 msg_id)
406{
407 struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready;
408 int offset;
409 int bar;
410 int ret;
411
412 /* mailbox must be on 4k boundary */
413 offset = snd_sof_dsp_get_mailbox_offset(sdev);
414 if (offset < 0) {
415 dev_err(sdev->dev, "error: have no mailbox offset\n");
416 return offset;
417 }
418
419 bar = snd_sof_dsp_get_bar_index(sdev, SOF_FW_BLK_TYPE_SRAM);
420 if (bar < 0) {
421 dev_err(sdev->dev, "error: have no bar mapping\n");
422 return -EINVAL;
423 }
424
425 dev_dbg(sdev->dev, "ipc: DSP is ready 0x%8.8x offset 0x%x\n",
426 msg_id, offset);
427
428 /* no need to re-check version/ABI for subsequent boots */
429 if (!sdev->first_boot)
430 return 0;
431
432 /* copy data from the DSP FW ready offset */
433 sof_block_read(sdev, bar, offset, fw_ready, sizeof(*fw_ready));
434
435 /* make sure ABI version is compatible */
436 ret = snd_sof_ipc_valid(sdev);
437 if (ret < 0)
438 return ret;
439
440 /* now check for extended data */
441 snd_sof_fw_parse_ext_data(sdev, bar, offset +
442 sizeof(struct sof_ipc_fw_ready));
443
444 sof_get_windows(sdev);
445
446 return 0;
447}
448EXPORT_SYMBOL(sof_fw_ready);
449
70cd5254
LG
450/* generic module parser for mmaped DSPs */
451int snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev,
452 struct snd_sof_mod_hdr *module)
453{
454 struct snd_sof_blk_hdr *block;
7198879e 455 int count, bar;
70cd5254
LG
456 u32 offset;
457 size_t remaining;
458
459 dev_dbg(sdev->dev, "new module size 0x%x blocks 0x%x type 0x%x\n",
460 module->size, module->num_blocks, module->type);
461
462 block = (struct snd_sof_blk_hdr *)((u8 *)module + sizeof(*module));
463
464 /* module->size doesn't include header size */
465 remaining = module->size;
466 for (count = 0; count < module->num_blocks; count++) {
467 /* check for wrap */
468 if (remaining < sizeof(*block)) {
469 dev_err(sdev->dev, "error: not enough data remaining\n");
470 return -EINVAL;
471 }
472
473 /* minus header size of block */
474 remaining -= sizeof(*block);
475
476 if (block->size == 0) {
477 dev_warn(sdev->dev,
478 "warning: block %d size zero\n", count);
479 dev_warn(sdev->dev, " type 0x%x offset 0x%x\n",
480 block->type, block->offset);
481 continue;
482 }
483
484 switch (block->type) {
485 case SOF_FW_BLK_TYPE_RSRVD0:
441c58cf 486 case SOF_FW_BLK_TYPE_ROM...SOF_FW_BLK_TYPE_RSRVD14:
70cd5254
LG
487 continue; /* not handled atm */
488 case SOF_FW_BLK_TYPE_IRAM:
489 case SOF_FW_BLK_TYPE_DRAM:
441c58cf 490 case SOF_FW_BLK_TYPE_SRAM:
70cd5254 491 offset = block->offset;
7198879e
DB
492 bar = snd_sof_dsp_get_bar_index(sdev, block->type);
493 if (bar < 0) {
494 dev_err(sdev->dev,
495 "error: no BAR mapping for block type 0x%x\n",
496 block->type);
497 return bar;
498 }
70cd5254
LG
499 break;
500 default:
501 dev_err(sdev->dev, "error: bad type 0x%x for block 0x%x\n",
502 block->type, count);
503 return -EINVAL;
504 }
505
506 dev_dbg(sdev->dev,
507 "block %d type 0x%x size 0x%x ==> offset 0x%x\n",
508 count, block->type, block->size, offset);
509
510 /* checking block->size to avoid unaligned access */
511 if (block->size % sizeof(u32)) {
512 dev_err(sdev->dev, "error: invalid block size 0x%x\n",
513 block->size);
514 return -EINVAL;
515 }
7198879e 516 snd_sof_dsp_block_write(sdev, bar, offset,
70cd5254
LG
517 block + 1, block->size);
518
519 if (remaining < block->size) {
520 dev_err(sdev->dev, "error: not enough data remaining\n");
521 return -EINVAL;
522 }
523
524 /* minus body size of block */
525 remaining -= block->size;
526 /* next block */
527 block = (struct snd_sof_blk_hdr *)((u8 *)block + sizeof(*block)
528 + block->size);
529 }
530
531 return 0;
532}
533EXPORT_SYMBOL(snd_sof_parse_module_memcpy);
534
92be17a5
KT
535static int check_header(struct snd_sof_dev *sdev, const struct firmware *fw,
536 size_t fw_offset)
70cd5254
LG
537{
538 struct snd_sof_fw_header *header;
92be17a5
KT
539 size_t fw_size = fw->size - fw_offset;
540
523773b9 541 if (fw->size <= fw_offset) {
92be17a5
KT
542 dev_err(sdev->dev, "error: firmware size must be greater than firmware offset\n");
543 return -EINVAL;
544 }
70cd5254
LG
545
546 /* Read the header information from the data pointer */
92be17a5 547 header = (struct snd_sof_fw_header *)(fw->data + fw_offset);
70cd5254
LG
548
549 /* verify FW sig */
550 if (strncmp(header->sig, SND_SOF_FW_SIG, SND_SOF_FW_SIG_SIZE) != 0) {
551 dev_err(sdev->dev, "error: invalid firmware signature\n");
552 return -EINVAL;
553 }
554
555 /* check size is valid */
92be17a5 556 if (fw_size != header->file_size + sizeof(*header)) {
70cd5254 557 dev_err(sdev->dev, "error: invalid filesize mismatch got 0x%zx expected 0x%zx\n",
92be17a5 558 fw_size, header->file_size + sizeof(*header));
70cd5254
LG
559 return -EINVAL;
560 }
561
562 dev_dbg(sdev->dev, "header size=0x%x modules=0x%x abi=0x%x size=%zu\n",
563 header->file_size, header->num_modules,
564 header->abi, sizeof(*header));
565
566 return 0;
567}
568
92be17a5
KT
569static int load_modules(struct snd_sof_dev *sdev, const struct firmware *fw,
570 size_t fw_offset)
70cd5254
LG
571{
572 struct snd_sof_fw_header *header;
573 struct snd_sof_mod_hdr *module;
574 int (*load_module)(struct snd_sof_dev *sof_dev,
575 struct snd_sof_mod_hdr *hdr);
576 int ret, count;
577 size_t remaining;
578
92be17a5 579 header = (struct snd_sof_fw_header *)(fw->data + fw_offset);
70cd5254
LG
580 load_module = sof_ops(sdev)->load_module;
581 if (!load_module)
582 return -EINVAL;
583
584 /* parse each module */
92be17a5
KT
585 module = (struct snd_sof_mod_hdr *)(fw->data + fw_offset +
586 sizeof(*header));
587 remaining = fw->size - sizeof(*header) - fw_offset;
70cd5254
LG
588 /* check for wrap */
589 if (remaining > fw->size) {
590 dev_err(sdev->dev, "error: fw size smaller than header size\n");
591 return -EINVAL;
592 }
593
594 for (count = 0; count < header->num_modules; count++) {
595 /* check for wrap */
596 if (remaining < sizeof(*module)) {
597 dev_err(sdev->dev, "error: not enough data remaining\n");
598 return -EINVAL;
599 }
600
601 /* minus header size of module */
602 remaining -= sizeof(*module);
603
604 /* module */
605 ret = load_module(sdev, module);
606 if (ret < 0) {
607 dev_err(sdev->dev, "error: invalid module %d\n", count);
608 return ret;
609 }
610
611 if (remaining < module->size) {
612 dev_err(sdev->dev, "error: not enough data remaining\n");
613 return -EINVAL;
614 }
615
616 /* minus body size of module */
617 remaining -= module->size;
618 module = (struct snd_sof_mod_hdr *)((u8 *)module
619 + sizeof(*module) + module->size);
620 }
621
622 return 0;
623}
624
625int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev)
626{
627 struct snd_sof_pdata *plat_data = sdev->pdata;
628 const char *fw_filename;
a80cf198 629 ssize_t ext_man_size;
70cd5254
LG
630 int ret;
631
70cd5254
LG
632 /* Don't request firmware again if firmware is already requested */
633 if (plat_data->fw)
634 return 0;
635
636 fw_filename = kasprintf(GFP_KERNEL, "%s/%s",
637 plat_data->fw_filename_prefix,
638 plat_data->fw_filename);
639 if (!fw_filename)
640 return -ENOMEM;
641
642 ret = request_firmware(&plat_data->fw, fw_filename, sdev->dev);
643
644 if (ret < 0) {
645 dev_err(sdev->dev, "error: request firmware %s failed err: %d\n",
646 fw_filename, ret);
a80cf198 647 goto err;
490a625b
PLB
648 } else {
649 dev_dbg(sdev->dev, "request_firmware %s successful\n",
650 fw_filename);
70cd5254
LG
651 }
652
a80cf198
KT
653 /* check for extended manifest */
654 ext_man_size = snd_sof_fw_ext_man_parse(sdev, plat_data->fw);
655 if (ext_man_size > 0) {
656 /* when no error occurred, drop extended manifest */
657 plat_data->fw_offset = ext_man_size;
658 } else if (!ext_man_size) {
659 /* No extended manifest, so nothing to skip during FW load */
660 dev_dbg(sdev->dev, "firmware doesn't contain extended manifest\n");
661 } else {
662 ret = ext_man_size;
663 dev_err(sdev->dev, "error: firmware %s contains unsupported or invalid extended manifest: %d\n",
664 fw_filename, ret);
665 }
666
667err:
70cd5254
LG
668 kfree(fw_filename);
669
670 return ret;
671}
672EXPORT_SYMBOL(snd_sof_load_firmware_raw);
673
674int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev)
675{
676 struct snd_sof_pdata *plat_data = sdev->pdata;
677 int ret;
678
679 ret = snd_sof_load_firmware_raw(sdev);
680 if (ret < 0)
681 return ret;
682
683 /* make sure the FW header and file is valid */
92be17a5 684 ret = check_header(sdev, plat_data->fw, plat_data->fw_offset);
70cd5254
LG
685 if (ret < 0) {
686 dev_err(sdev->dev, "error: invalid FW header\n");
687 goto error;
688 }
689
690 /* prepare the DSP for FW loading */
691 ret = snd_sof_dsp_reset(sdev);
692 if (ret < 0) {
693 dev_err(sdev->dev, "error: failed to reset DSP\n");
694 goto error;
695 }
696
697 /* parse and load firmware modules to DSP */
92be17a5 698 ret = load_modules(sdev, plat_data->fw, plat_data->fw_offset);
70cd5254
LG
699 if (ret < 0) {
700 dev_err(sdev->dev, "error: invalid FW modules\n");
701 goto error;
702 }
703
704 return 0;
705
706error:
707 release_firmware(plat_data->fw);
708 plat_data->fw = NULL;
709 return ret;
710
711}
712EXPORT_SYMBOL(snd_sof_load_firmware_memcpy);
713
714int snd_sof_load_firmware(struct snd_sof_dev *sdev)
715{
716 dev_dbg(sdev->dev, "loading firmware\n");
717
718 if (sof_ops(sdev)->load_firmware)
719 return sof_ops(sdev)->load_firmware(sdev);
720 return 0;
721}
722EXPORT_SYMBOL(snd_sof_load_firmware);
723
724int snd_sof_run_firmware(struct snd_sof_dev *sdev)
725{
726 int ret;
727 int init_core_mask;
728
729 init_waitqueue_head(&sdev->boot_wait);
70cd5254 730
5c9714f6 731 /* create read-only fw_version debugfs to store boot version info */
70cd5254
LG
732 if (sdev->first_boot) {
733 ret = snd_sof_debugfs_buf_item(sdev, &sdev->fw_version,
734 sizeof(sdev->fw_version),
5c9714f6 735 "fw_version", 0444);
70cd5254
LG
736 /* errors are only due to memory allocation, not debugfs */
737 if (ret < 0) {
738 dev_err(sdev->dev, "error: snd_sof_debugfs_buf_item failed\n");
739 return ret;
740 }
741 }
742
743 /* perform pre fw run operations */
744 ret = snd_sof_dsp_pre_fw_run(sdev);
745 if (ret < 0) {
746 dev_err(sdev->dev, "error: failed pre fw run op\n");
747 return ret;
748 }
749
750 dev_dbg(sdev->dev, "booting DSP firmware\n");
751
752 /* boot the firmware on the DSP */
753 ret = snd_sof_dsp_run(sdev);
754 if (ret < 0) {
755 dev_err(sdev->dev, "error: failed to reset DSP\n");
756 return ret;
757 }
758
759 init_core_mask = ret;
760
6ca5cecb
RS
761 /*
762 * now wait for the DSP to boot. There are 3 possible outcomes:
763 * 1. Boot wait times out indicating FW boot failure.
764 * 2. FW boots successfully and fw_ready op succeeds.
765 * 3. FW boots but fw_ready op fails.
766 */
767 ret = wait_event_timeout(sdev->boot_wait,
768 sdev->fw_state > SOF_FW_BOOT_IN_PROGRESS,
70cd5254
LG
769 msecs_to_jiffies(sdev->boot_timeout));
770 if (ret == 0) {
771 dev_err(sdev->dev, "error: firmware boot failure\n");
772 snd_sof_dsp_dbg_dump(sdev, SOF_DBG_REGS | SOF_DBG_MBOX |
773 SOF_DBG_TEXT | SOF_DBG_PCI);
6ca5cecb 774 sdev->fw_state = SOF_FW_BOOT_FAILED;
70cd5254
LG
775 return -EIO;
776 }
777
6ca5cecb 778 if (sdev->fw_state == SOF_FW_BOOT_COMPLETE)
904f353d 779 dev_dbg(sdev->dev, "firmware boot complete\n");
6ca5cecb
RS
780 else
781 return -EIO; /* FW boots but fw_ready op failed */
70cd5254
LG
782
783 /* perform post fw run operations */
784 ret = snd_sof_dsp_post_fw_run(sdev);
785 if (ret < 0) {
786 dev_err(sdev->dev, "error: failed post fw run op\n");
787 return ret;
788 }
789
790 /* fw boot is complete. Update the active cores mask */
791 sdev->enabled_cores_mask = init_core_mask;
792
793 return 0;
794}
795EXPORT_SYMBOL(snd_sof_run_firmware);
796
797void snd_sof_fw_unload(struct snd_sof_dev *sdev)
798{
799 /* TODO: support module unloading at runtime */
800}
801EXPORT_SYMBOL(snd_sof_fw_unload);