linux/sound/soc/sof/loader.c
<<
>>
Prefs
   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
  18static 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
  44static 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 */
  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 */
 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}
 144EXPORT_SYMBOL(snd_sof_fw_parse_ext_data);
 145
 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
 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
 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
 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) {
 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 */
 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
 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;
 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}
 533EXPORT_SYMBOL(snd_sof_parse_module_memcpy);
 534
 535static 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
 569static 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
 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;
 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
 667err:
 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 */
 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
 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);
 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}
 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);
 802