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