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