linux/sound/soc/intel/atom/sst/sst_loader.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 *  sst_dsp.c - Intel SST Driver for audio engine
   4 *
   5 *  Copyright (C) 2008-14       Intel Corp
   6 *  Authors:    Vinod Koul <vinod.koul@intel.com>
   7 *              Harsha Priya <priya.harsha@intel.com>
   8 *              Dharageswari R <dharageswari.r@intel.com>
   9 *              KP Jeeja <jeeja.kp@intel.com>
  10 *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  11 *
  12 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  13 *
  14 *  This file contains all dsp controlling functions like firmware download,
  15 * setting/resetting dsp cores, etc
  16 */
  17#include <linux/pci.h>
  18#include <linux/delay.h>
  19#include <linux/fs.h>
  20#include <linux/sched.h>
  21#include <linux/firmware.h>
  22#include <linux/dmaengine.h>
  23#include <linux/pm_runtime.h>
  24#include <linux/pm_qos.h>
  25#include <sound/core.h>
  26#include <sound/pcm.h>
  27#include <sound/soc.h>
  28#include <sound/compress_driver.h>
  29#include <asm/platform_sst_audio.h>
  30#include "../sst-mfld-platform.h"
  31#include "sst.h"
  32
  33void memcpy32_toio(void __iomem *dst, const void *src, int count)
  34{
  35        /* __iowrite32_copy uses 32-bit count values so divide by 4 for
  36         * right count in words
  37         */
  38        __iowrite32_copy(dst, src, count / 4);
  39}
  40
  41void memcpy32_fromio(void *dst, const void __iomem *src, int count)
  42{
  43        /* __ioread32_copy uses 32-bit count values so divide by 4 for
  44         * right count in words
  45         */
  46        __ioread32_copy(dst, src, count / 4);
  47}
  48
  49/**
  50 * intel_sst_reset_dsp_mrfld - Resetting SST DSP
  51 * @sst_drv_ctx: intel_sst_drv context pointer
  52 *
  53 * This resets DSP in case of MRFLD platfroms
  54 */
  55int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *sst_drv_ctx)
  56{
  57        union config_status_reg_mrfld csr;
  58
  59        dev_dbg(sst_drv_ctx->dev, "sst: Resetting the DSP in mrfld\n");
  60        csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
  61
  62        dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
  63
  64        csr.full |= 0x7;
  65        sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
  66        csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
  67
  68        dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
  69
  70        csr.full &= ~(0x1);
  71        sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
  72
  73        csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
  74        dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
  75        return 0;
  76}
  77
  78/**
  79 * sst_start_mrfld - Start the SST DSP processor
  80 * @sst_drv_ctx: intel_sst_drv context pointer
  81 *
  82 * This starts the DSP in MERRIFIELD platfroms
  83 */
  84int sst_start_mrfld(struct intel_sst_drv *sst_drv_ctx)
  85{
  86        union config_status_reg_mrfld csr;
  87
  88        dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP in mrfld LALALALA\n");
  89        csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
  90        dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
  91
  92        csr.full |= 0x7;
  93        sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
  94
  95        csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
  96        dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
  97
  98        csr.part.xt_snoop = 1;
  99        csr.full &= ~(0x5);
 100        sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
 101
 102        csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
 103        dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP_merrifield:%llx\n",
 104                        csr.full);
 105        return 0;
 106}
 107
 108static int sst_validate_fw_image(struct intel_sst_drv *ctx, unsigned long size,
 109                struct fw_module_header **module, u32 *num_modules)
 110{
 111        struct sst_fw_header *header;
 112        const void *sst_fw_in_mem = ctx->fw_in_mem;
 113
 114        dev_dbg(ctx->dev, "Enter\n");
 115
 116        /* Read the header information from the data pointer */
 117        header = (struct sst_fw_header *)sst_fw_in_mem;
 118        dev_dbg(ctx->dev,
 119                "header sign=%s size=%x modules=%x fmt=%x size=%zx\n",
 120                header->signature, header->file_size, header->modules,
 121                header->file_format, sizeof(*header));
 122
 123        /* verify FW */
 124        if ((strncmp(header->signature, SST_FW_SIGN, 4) != 0) ||
 125                (size != header->file_size + sizeof(*header))) {
 126                /* Invalid FW signature */
 127                dev_err(ctx->dev, "InvalidFW sign/filesize mismatch\n");
 128                return -EINVAL;
 129        }
 130        *num_modules = header->modules;
 131        *module = (void *)sst_fw_in_mem + sizeof(*header);
 132
 133        return 0;
 134}
 135
 136/*
 137 * sst_fill_memcpy_list - Fill the memcpy list
 138 *
 139 * @memcpy_list: List to be filled
 140 * @destn: Destination addr to be filled in the list
 141 * @src: Source addr to be filled in the list
 142 * @size: Size to be filled in the list
 143 *
 144 * Adds the node to the list after required fields
 145 * are populated in the node
 146 */
 147static int sst_fill_memcpy_list(struct list_head *memcpy_list,
 148                        void *destn, const void *src, u32 size, bool is_io)
 149{
 150        struct sst_memcpy_list *listnode;
 151
 152        listnode = kzalloc(sizeof(*listnode), GFP_KERNEL);
 153        if (listnode == NULL)
 154                return -ENOMEM;
 155        listnode->dstn = destn;
 156        listnode->src = src;
 157        listnode->size = size;
 158        listnode->is_io = is_io;
 159        list_add_tail(&listnode->memcpylist, memcpy_list);
 160
 161        return 0;
 162}
 163
 164/**
 165 * sst_parse_module_memcpy - Parse audio FW modules and populate the memcpy list
 166 *
 167 * @sst_drv_ctx         : driver context
 168 * @module              : FW module header
 169 * @memcpy_list : Pointer to the list to be populated
 170 * Create the memcpy list as the number of block to be copied
 171 * returns error or 0 if module sizes are proper
 172 */
 173static int sst_parse_module_memcpy(struct intel_sst_drv *sst_drv_ctx,
 174                struct fw_module_header *module, struct list_head *memcpy_list)
 175{
 176        struct fw_block_info *block;
 177        u32 count;
 178        int ret_val = 0;
 179        void __iomem *ram_iomem;
 180
 181        dev_dbg(sst_drv_ctx->dev, "module sign %s size %x blocks %x type %x\n",
 182                        module->signature, module->mod_size,
 183                        module->blocks, module->type);
 184        dev_dbg(sst_drv_ctx->dev, "module entrypoint 0x%x\n", module->entry_point);
 185
 186        block = (void *)module + sizeof(*module);
 187
 188        for (count = 0; count < module->blocks; count++) {
 189                if (block->size <= 0) {
 190                        dev_err(sst_drv_ctx->dev, "block size invalid\n");
 191                        return -EINVAL;
 192                }
 193                switch (block->type) {
 194                case SST_IRAM:
 195                        ram_iomem = sst_drv_ctx->iram;
 196                        break;
 197                case SST_DRAM:
 198                        ram_iomem = sst_drv_ctx->dram;
 199                        break;
 200                case SST_DDR:
 201                        ram_iomem = sst_drv_ctx->ddr;
 202                        break;
 203                case SST_CUSTOM_INFO:
 204                        block = (void *)block + sizeof(*block) + block->size;
 205                        continue;
 206                default:
 207                        dev_err(sst_drv_ctx->dev, "wrong ram type0x%x in block0x%x\n",
 208                                        block->type, count);
 209                        return -EINVAL;
 210                }
 211
 212                ret_val = sst_fill_memcpy_list(memcpy_list,
 213                                ram_iomem + block->ram_offset,
 214                                (void *)block + sizeof(*block), block->size, 1);
 215                if (ret_val)
 216                        return ret_val;
 217
 218                block = (void *)block + sizeof(*block) + block->size;
 219        }
 220        return 0;
 221}
 222
 223/**
 224 * sst_parse_fw_memcpy - parse the firmware image & populate the list for memcpy
 225 *
 226 * @ctx                 : pointer to drv context
 227 * @size                : size of the firmware
 228 * @fw_list             : pointer to list_head to be populated
 229 * This function parses the FW image and saves the parsed image in the list
 230 * for memcpy
 231 */
 232static int sst_parse_fw_memcpy(struct intel_sst_drv *ctx, unsigned long size,
 233                                struct list_head *fw_list)
 234{
 235        struct fw_module_header *module;
 236        u32 count, num_modules;
 237        int ret_val;
 238
 239        ret_val = sst_validate_fw_image(ctx, size, &module, &num_modules);
 240        if (ret_val)
 241                return ret_val;
 242
 243        for (count = 0; count < num_modules; count++) {
 244                ret_val = sst_parse_module_memcpy(ctx, module, fw_list);
 245                if (ret_val)
 246                        return ret_val;
 247                module = (void *)module + sizeof(*module) + module->mod_size;
 248        }
 249
 250        return 0;
 251}
 252
 253/**
 254 * sst_do_memcpy - function initiates the memcpy
 255 *
 256 * @memcpy_list: Pter to memcpy list on which the memcpy needs to be initiated
 257 *
 258 * Triggers the memcpy
 259 */
 260static void sst_do_memcpy(struct list_head *memcpy_list)
 261{
 262        struct sst_memcpy_list *listnode;
 263
 264        list_for_each_entry(listnode, memcpy_list, memcpylist) {
 265                if (listnode->is_io)
 266                        memcpy32_toio((void __iomem *)listnode->dstn,
 267                                        listnode->src, listnode->size);
 268                else
 269                        memcpy(listnode->dstn, listnode->src, listnode->size);
 270        }
 271}
 272
 273void sst_memcpy_free_resources(struct intel_sst_drv *sst_drv_ctx)
 274{
 275        struct sst_memcpy_list *listnode, *tmplistnode;
 276
 277        /* Free the list */
 278        list_for_each_entry_safe(listnode, tmplistnode,
 279                                 &sst_drv_ctx->memcpy_list, memcpylist) {
 280                list_del(&listnode->memcpylist);
 281                kfree(listnode);
 282        }
 283}
 284
 285static int sst_cache_and_parse_fw(struct intel_sst_drv *sst,
 286                const struct firmware *fw)
 287{
 288        int retval = 0;
 289
 290        sst->fw_in_mem = kzalloc(fw->size, GFP_KERNEL);
 291        if (!sst->fw_in_mem) {
 292                retval = -ENOMEM;
 293                goto end_release;
 294        }
 295        dev_dbg(sst->dev, "copied fw to %p", sst->fw_in_mem);
 296        dev_dbg(sst->dev, "phys: %lx", (unsigned long)virt_to_phys(sst->fw_in_mem));
 297        memcpy(sst->fw_in_mem, fw->data, fw->size);
 298        retval = sst_parse_fw_memcpy(sst, fw->size, &sst->memcpy_list);
 299        if (retval) {
 300                dev_err(sst->dev, "Failed to parse fw\n");
 301                kfree(sst->fw_in_mem);
 302                sst->fw_in_mem = NULL;
 303        }
 304
 305end_release:
 306        release_firmware(fw);
 307        return retval;
 308
 309}
 310
 311void sst_firmware_load_cb(const struct firmware *fw, void *context)
 312{
 313        struct intel_sst_drv *ctx = context;
 314
 315        dev_dbg(ctx->dev, "Enter\n");
 316
 317        if (fw == NULL) {
 318                dev_err(ctx->dev, "request fw failed\n");
 319                return;
 320        }
 321
 322        mutex_lock(&ctx->sst_lock);
 323
 324        if (ctx->sst_state != SST_RESET ||
 325                        ctx->fw_in_mem != NULL) {
 326                release_firmware(fw);
 327                mutex_unlock(&ctx->sst_lock);
 328                return;
 329        }
 330
 331        dev_dbg(ctx->dev, "Request Fw completed\n");
 332        sst_cache_and_parse_fw(ctx, fw);
 333        mutex_unlock(&ctx->sst_lock);
 334}
 335
 336/*
 337 * sst_request_fw - requests audio fw from kernel and saves a copy
 338 *
 339 * This function requests the SST FW from the kernel, parses it and
 340 * saves a copy in the driver context
 341 */
 342static int sst_request_fw(struct intel_sst_drv *sst)
 343{
 344        int retval = 0;
 345        const struct firmware *fw;
 346
 347        retval = request_firmware(&fw, sst->firmware_name, sst->dev);
 348        if (retval) {
 349                dev_err(sst->dev, "request fw failed %d\n", retval);
 350                return retval;
 351        }
 352        if (fw == NULL) {
 353                dev_err(sst->dev, "fw is returning as null\n");
 354                return -EINVAL;
 355        }
 356        mutex_lock(&sst->sst_lock);
 357        retval = sst_cache_and_parse_fw(sst, fw);
 358        mutex_unlock(&sst->sst_lock);
 359
 360        return retval;
 361}
 362
 363/*
 364 * Writing the DDR physical base to DCCM offset
 365 * so that FW can use it to setup TLB
 366 */
 367static void sst_dccm_config_write(void __iomem *dram_base,
 368                unsigned int ddr_base)
 369{
 370        void __iomem *addr;
 371        u32 bss_reset = 0;
 372
 373        addr = (void __iomem *)(dram_base + MRFLD_FW_DDR_BASE_OFFSET);
 374        memcpy32_toio(addr, (void *)&ddr_base, sizeof(u32));
 375        bss_reset |= (1 << MRFLD_FW_BSS_RESET_BIT);
 376        addr = (void __iomem *)(dram_base + MRFLD_FW_FEATURE_BASE_OFFSET);
 377        memcpy32_toio(addr, &bss_reset, sizeof(u32));
 378
 379}
 380
 381void sst_post_download_mrfld(struct intel_sst_drv *ctx)
 382{
 383        sst_dccm_config_write(ctx->dram, ctx->ddr_base);
 384        dev_dbg(ctx->dev, "config written to DCCM\n");
 385}
 386
 387/**
 388 * sst_load_fw - function to load FW into DSP
 389 * @sst_drv_ctx: intel_sst_drv context pointer
 390 *
 391 * Transfers the FW to DSP using dma/memcpy
 392 */
 393int sst_load_fw(struct intel_sst_drv *sst_drv_ctx)
 394{
 395        int ret_val = 0;
 396        struct sst_block *block;
 397
 398        dev_dbg(sst_drv_ctx->dev, "sst_load_fw\n");
 399
 400        if (sst_drv_ctx->sst_state !=  SST_RESET)
 401                return -EAGAIN;
 402
 403        if (!sst_drv_ctx->fw_in_mem) {
 404                dev_dbg(sst_drv_ctx->dev, "sst: FW not in memory retry to download\n");
 405                ret_val = sst_request_fw(sst_drv_ctx);
 406                if (ret_val)
 407                        return ret_val;
 408        }
 409
 410        block = sst_create_block(sst_drv_ctx, 0, FW_DWNL_ID);
 411        if (block == NULL)
 412                return -ENOMEM;
 413
 414        /* Prevent C-states beyond C6 */
 415        cpu_latency_qos_update_request(sst_drv_ctx->qos, 0);
 416
 417        sst_drv_ctx->sst_state = SST_FW_LOADING;
 418
 419        ret_val = sst_drv_ctx->ops->reset(sst_drv_ctx);
 420        if (ret_val)
 421                goto restore;
 422
 423        sst_do_memcpy(&sst_drv_ctx->memcpy_list);
 424
 425        /* Write the DRAM/DCCM config before enabling FW */
 426        if (sst_drv_ctx->ops->post_download)
 427                sst_drv_ctx->ops->post_download(sst_drv_ctx);
 428
 429        /* bring sst out of reset */
 430        ret_val = sst_drv_ctx->ops->start(sst_drv_ctx);
 431        if (ret_val)
 432                goto restore;
 433
 434        ret_val = sst_wait_timeout(sst_drv_ctx, block);
 435        if (ret_val) {
 436                dev_err(sst_drv_ctx->dev, "fw download failed %d\n" , ret_val);
 437                /* FW download failed due to timeout */
 438                ret_val = -EBUSY;
 439
 440        }
 441
 442
 443restore:
 444        /* Re-enable Deeper C-states beyond C6 */
 445        cpu_latency_qos_update_request(sst_drv_ctx->qos, PM_QOS_DEFAULT_VALUE);
 446        sst_free_block(sst_drv_ctx, block);
 447        dev_dbg(sst_drv_ctx->dev, "fw load successful!!!\n");
 448
 449        if (sst_drv_ctx->ops->restore_dsp_context)
 450                sst_drv_ctx->ops->restore_dsp_context();
 451        sst_drv_ctx->sst_state = SST_FW_RUNNING;
 452        return ret_val;
 453}
 454
 455
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.