ASoC: Intel: SOF: merge COMETLAKE_LP and COMETLAKE_H
[sfrench/cifs-2.6.git] / sound / soc / sof / loader.c
1 // SPDX-License-Identifier: (GPL-2.0-only 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 <sound/sof/ext_manifest.h>
16 #include "ops.h"
17
18 static int get_ext_windows(struct snd_sof_dev *sdev,
19                            const struct sof_ipc_ext_data_hdr *ext_hdr)
20 {
21         const struct sof_ipc_window *w =
22                 container_of(ext_hdr, struct sof_ipc_window, ext_hdr);
23         size_t w_size = struct_size(w, window, w->num_windows);
24
25         if (w->num_windows == 0 || w->num_windows > SOF_IPC_MAX_ELEMS)
26                 return -EINVAL;
27
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
36         /* keep a local copy of the data */
37         sdev->info_window = kmemdup(w, w_size, GFP_KERNEL);
38         if (!sdev->info_window)
39                 return -ENOMEM;
40
41         return 0;
42 }
43
44 static int get_cc_info(struct snd_sof_dev *sdev,
45                        const struct sof_ipc_ext_data_hdr *ext_hdr)
46 {
47         int ret;
48
49         const struct sof_ipc_cc_version *cc =
50                 container_of(ext_hdr, struct sof_ipc_cc_version, ext_hdr);
51
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
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
88 /* parse the extended FW boot data structures from FW boot message */
89 int 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 */
106                 snd_sof_dsp_block_read(sdev, bar, offset + sizeof(*ext_hdr),
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) {
115                 case SOF_IPC_EXT_WINDOW:
116                         ret = get_ext_windows(sdev, ext_hdr);
117                         break;
118                 case SOF_IPC_EXT_CC_INFO:
119                         ret = get_cc_info(sdev, ext_hdr);
120                         break;
121                 default:
122                         dev_warn(sdev->dev, "warning: unknown ext header type %d size 0x%x\n",
123                                  ext_hdr->type, ext_hdr->hdr.size);
124                         ret = 0;
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 }
144 EXPORT_SYMBOL(snd_sof_fw_parse_ext_data);
145
146 static 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
159 static 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
169 static 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
179 static 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 */
205 static 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) {
249                 case SOF_EXT_MAN_ELEM_FW_VERSION:
250                         ret = ext_man_get_fw_version(sdev, elem_hdr);
251                         break;
252                 case SOF_EXT_MAN_ELEM_WINDOW:
253                         ret = ext_man_get_windows(sdev, elem_hdr);
254                         break;
255                 case SOF_EXT_MAN_ELEM_CC_VERSION:
256                         ret = ext_man_get_cc_info(sdev, elem_hdr);
257                         break;
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
282 /*
283  * IPC Firmware ready.
284  */
285 static 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 */
405 int 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 }
448 EXPORT_SYMBOL(sof_fw_ready);
449
450 /* generic module parser for mmaped DSPs */
451 int 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;
455         int count, bar;
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:
486                 case SOF_FW_BLK_TYPE_ROM...SOF_FW_BLK_TYPE_RSRVD14:
487                         continue;       /* not handled atm */
488                 case SOF_FW_BLK_TYPE_IRAM:
489                 case SOF_FW_BLK_TYPE_DRAM:
490                 case SOF_FW_BLK_TYPE_SRAM:
491                         offset = block->offset;
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                         }
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                 }
516                 snd_sof_dsp_block_write(sdev, bar, offset,
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 }
533 EXPORT_SYMBOL(snd_sof_parse_module_memcpy);
534
535 static int check_header(struct snd_sof_dev *sdev, const struct firmware *fw,
536                         size_t fw_offset)
537 {
538         struct snd_sof_fw_header *header;
539         size_t fw_size = fw->size - fw_offset;
540
541         if (fw->size <= fw_offset) {
542                 dev_err(sdev->dev, "error: firmware size must be greater than firmware offset\n");
543                 return -EINVAL;
544         }
545
546         /* Read the header information from the data pointer */
547         header = (struct snd_sof_fw_header *)(fw->data + fw_offset);
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 */
556         if (fw_size != header->file_size + sizeof(*header)) {
557                 dev_err(sdev->dev, "error: invalid filesize mismatch got 0x%zx expected 0x%zx\n",
558                         fw_size, header->file_size + sizeof(*header));
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
569 static int load_modules(struct snd_sof_dev *sdev, const struct firmware *fw,
570                         size_t fw_offset)
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
579         header = (struct snd_sof_fw_header *)(fw->data + fw_offset);
580         load_module = sof_ops(sdev)->load_module;
581         if (!load_module)
582                 return -EINVAL;
583
584         /* parse each module */
585         module = (struct snd_sof_mod_hdr *)(fw->data + fw_offset +
586                                             sizeof(*header));
587         remaining = fw->size - sizeof(*header) - fw_offset;
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
625 int 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;
629         ssize_t ext_man_size;
630         int ret;
631
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);
647                 goto err;
648         } else {
649                 dev_dbg(sdev->dev, "request_firmware %s successful\n",
650                         fw_filename);
651         }
652
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
667 err:
668         kfree(fw_filename);
669
670         return ret;
671 }
672 EXPORT_SYMBOL(snd_sof_load_firmware_raw);
673
674 int 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 */
684         ret = check_header(sdev, plat_data->fw, plat_data->fw_offset);
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 */
698         ret = load_modules(sdev, plat_data->fw, plat_data->fw_offset);
699         if (ret < 0) {
700                 dev_err(sdev->dev, "error: invalid FW modules\n");
701                 goto error;
702         }
703
704         return 0;
705
706 error:
707         release_firmware(plat_data->fw);
708         plat_data->fw = NULL;
709         return ret;
710
711 }
712 EXPORT_SYMBOL(snd_sof_load_firmware_memcpy);
713
714 int 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 }
722 EXPORT_SYMBOL(snd_sof_load_firmware);
723
724 int 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);
730
731         /* create read-only fw_version debugfs to store boot version info */
732         if (sdev->first_boot) {
733                 ret = snd_sof_debugfs_buf_item(sdev, &sdev->fw_version,
734                                                sizeof(sdev->fw_version),
735                                                "fw_version", 0444);
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
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,
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);
774                 sdev->fw_state = SOF_FW_BOOT_FAILED;
775                 return -EIO;
776         }
777
778         if (sdev->fw_state == SOF_FW_BOOT_COMPLETE)
779                 dev_dbg(sdev->dev, "firmware boot complete\n");
780         else
781                 return -EIO; /* FW boots but fw_ready op failed */
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 }
795 EXPORT_SYMBOL(snd_sof_run_firmware);
796
797 void snd_sof_fw_unload(struct snd_sof_dev *sdev)
798 {
799         /* TODO: support module unloading at runtime */
800 }
801 EXPORT_SYMBOL(snd_sof_fw_unload);