root/sound/soc/intel/atom/sst/sst_loader.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. memcpy32_toio
  2. memcpy32_fromio
  3. intel_sst_reset_dsp_mrfld
  4. sst_start_mrfld
  5. sst_validate_fw_image
  6. sst_fill_memcpy_list
  7. sst_parse_module_memcpy
  8. sst_parse_fw_memcpy
  9. sst_do_memcpy
  10. sst_memcpy_free_resources
  11. sst_cache_and_parse_fw
  12. sst_firmware_load_cb
  13. sst_request_fw
  14. sst_dccm_config_write
  15. sst_post_download_mrfld
  16. sst_load_fw

   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 #include "../../common/sst-dsp.h"
  33 
  34 void memcpy32_toio(void __iomem *dst, const void *src, int count)
  35 {
  36         /* __iowrite32_copy uses 32-bit count values so divide by 4 for
  37          * right count in words
  38          */
  39         __iowrite32_copy(dst, src, count / 4);
  40 }
  41 
  42 void memcpy32_fromio(void *dst, const void __iomem *src, int count)
  43 {
  44         /* __ioread32_copy uses 32-bit count values so divide by 4 for
  45          * right count in words
  46          */
  47         __ioread32_copy(dst, src, count / 4);
  48 }
  49 
  50 /**
  51  * intel_sst_reset_dsp_mrfld - Resetting SST DSP
  52  *
  53  * This resets DSP in case of MRFLD platfroms
  54  */
  55 int 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_merrifield - Start the SST DSP processor
  80  *
  81  * This starts the DSP in MERRIFIELD platfroms
  82  */
  83 int sst_start_mrfld(struct intel_sst_drv *sst_drv_ctx)
  84 {
  85         union config_status_reg_mrfld csr;
  86 
  87         dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP in mrfld LALALALA\n");
  88         csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
  89         dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
  90 
  91         csr.full |= 0x7;
  92         sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
  93 
  94         csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
  95         dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
  96 
  97         csr.part.xt_snoop = 1;
  98         csr.full &= ~(0x5);
  99         sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
 100 
 101         csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
 102         dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP_merrifield:%llx\n",
 103                         csr.full);
 104         return 0;
 105 }
 106 
 107 static int sst_validate_fw_image(struct intel_sst_drv *ctx, unsigned long size,
 108                 struct fw_module_header **module, u32 *num_modules)
 109 {
 110         struct sst_fw_header *header;
 111         const void *sst_fw_in_mem = ctx->fw_in_mem;
 112 
 113         dev_dbg(ctx->dev, "Enter\n");
 114 
 115         /* Read the header information from the data pointer */
 116         header = (struct sst_fw_header *)sst_fw_in_mem;
 117         dev_dbg(ctx->dev,
 118                 "header sign=%s size=%x modules=%x fmt=%x size=%zx\n",
 119                 header->signature, header->file_size, header->modules,
 120                 header->file_format, sizeof(*header));
 121 
 122         /* verify FW */
 123         if ((strncmp(header->signature, SST_FW_SIGN, 4) != 0) ||
 124                 (size != header->file_size + sizeof(*header))) {
 125                 /* Invalid FW signature */
 126                 dev_err(ctx->dev, "InvalidFW sign/filesize mismatch\n");
 127                 return -EINVAL;
 128         }
 129         *num_modules = header->modules;
 130         *module = (void *)sst_fw_in_mem + sizeof(*header);
 131 
 132         return 0;
 133 }
 134 
 135 /*
 136  * sst_fill_memcpy_list - Fill the memcpy list
 137  *
 138  * @memcpy_list: List to be filled
 139  * @destn: Destination addr to be filled in the list
 140  * @src: Source addr to be filled in the list
 141  * @size: Size to be filled in the list
 142  *
 143  * Adds the node to the list after required fields
 144  * are populated in the node
 145  */
 146 static int sst_fill_memcpy_list(struct list_head *memcpy_list,
 147                         void *destn, const void *src, u32 size, bool is_io)
 148 {
 149         struct sst_memcpy_list *listnode;
 150 
 151         listnode = kzalloc(sizeof(*listnode), GFP_KERNEL);
 152         if (listnode == NULL)
 153                 return -ENOMEM;
 154         listnode->dstn = destn;
 155         listnode->src = src;
 156         listnode->size = size;
 157         listnode->is_io = is_io;
 158         list_add_tail(&listnode->memcpylist, memcpy_list);
 159 
 160         return 0;
 161 }
 162 
 163 /**
 164  * sst_parse_module_memcpy - Parse audio FW modules and populate the memcpy list
 165  *
 166  * @sst_drv_ctx         : driver context
 167  * @module              : FW module header
 168  * @memcpy_list : Pointer to the list to be populated
 169  * Create the memcpy list as the number of block to be copied
 170  * returns error or 0 if module sizes are proper
 171  */
 172 static int sst_parse_module_memcpy(struct intel_sst_drv *sst_drv_ctx,
 173                 struct fw_module_header *module, struct list_head *memcpy_list)
 174 {
 175         struct fw_block_info *block;
 176         u32 count;
 177         int ret_val = 0;
 178         void __iomem *ram_iomem;
 179 
 180         dev_dbg(sst_drv_ctx->dev, "module sign %s size %x blocks %x type %x\n",
 181                         module->signature, module->mod_size,
 182                         module->blocks, module->type);
 183         dev_dbg(sst_drv_ctx->dev, "module entrypoint 0x%x\n", module->entry_point);
 184 
 185         block = (void *)module + sizeof(*module);
 186 
 187         for (count = 0; count < module->blocks; count++) {
 188                 if (block->size <= 0) {
 189                         dev_err(sst_drv_ctx->dev, "block size invalid\n");
 190                         return -EINVAL;
 191                 }
 192                 switch (block->type) {
 193                 case SST_IRAM:
 194                         ram_iomem = sst_drv_ctx->iram;
 195                         break;
 196                 case SST_DRAM:
 197                         ram_iomem = sst_drv_ctx->dram;
 198                         break;
 199                 case SST_DDR:
 200                         ram_iomem = sst_drv_ctx->ddr;
 201                         break;
 202                 case SST_CUSTOM_INFO:
 203                         block = (void *)block + sizeof(*block) + block->size;
 204                         continue;
 205                 default:
 206                         dev_err(sst_drv_ctx->dev, "wrong ram type0x%x in block0x%x\n",
 207                                         block->type, count);
 208                         return -EINVAL;
 209                 }
 210 
 211                 ret_val = sst_fill_memcpy_list(memcpy_list,
 212                                 ram_iomem + block->ram_offset,
 213                                 (void *)block + sizeof(*block), block->size, 1);
 214                 if (ret_val)
 215                         return ret_val;
 216 
 217                 block = (void *)block + sizeof(*block) + block->size;
 218         }
 219         return 0;
 220 }
 221 
 222 /**
 223  * sst_parse_fw_memcpy - parse the firmware image & populate the list for memcpy
 224  *
 225  * @ctx                 : pointer to drv context
 226  * @size                : size of the firmware
 227  * @fw_list             : pointer to list_head to be populated
 228  * This function parses the FW image and saves the parsed image in the list
 229  * for memcpy
 230  */
 231 static int sst_parse_fw_memcpy(struct intel_sst_drv *ctx, unsigned long size,
 232                                 struct list_head *fw_list)
 233 {
 234         struct fw_module_header *module;
 235         u32 count, num_modules;
 236         int ret_val;
 237 
 238         ret_val = sst_validate_fw_image(ctx, size, &module, &num_modules);
 239         if (ret_val)
 240                 return ret_val;
 241 
 242         for (count = 0; count < num_modules; count++) {
 243                 ret_val = sst_parse_module_memcpy(ctx, module, fw_list);
 244                 if (ret_val)
 245                         return ret_val;
 246                 module = (void *)module + sizeof(*module) + module->mod_size;
 247         }
 248 
 249         return 0;
 250 }
 251 
 252 /**
 253  * sst_do_memcpy - function initiates the memcpy
 254  *
 255  * @memcpy_list: Pter to memcpy list on which the memcpy needs to be initiated
 256  *
 257  * Triggers the memcpy
 258  */
 259 static void sst_do_memcpy(struct list_head *memcpy_list)
 260 {
 261         struct sst_memcpy_list *listnode;
 262 
 263         list_for_each_entry(listnode, memcpy_list, memcpylist) {
 264                 if (listnode->is_io)
 265                         memcpy32_toio((void __iomem *)listnode->dstn,
 266                                         listnode->src, listnode->size);
 267                 else
 268                         memcpy(listnode->dstn, listnode->src, listnode->size);
 269         }
 270 }
 271 
 272 void sst_memcpy_free_resources(struct intel_sst_drv *sst_drv_ctx)
 273 {
 274         struct sst_memcpy_list *listnode, *tmplistnode;
 275 
 276         /* Free the list */
 277         if (!list_empty(&sst_drv_ctx->memcpy_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 }
 285 
 286 static int sst_cache_and_parse_fw(struct intel_sst_drv *sst,
 287                 const struct firmware *fw)
 288 {
 289         int retval = 0;
 290 
 291         sst->fw_in_mem = kzalloc(fw->size, GFP_KERNEL);
 292         if (!sst->fw_in_mem) {
 293                 retval = -ENOMEM;
 294                 goto end_release;
 295         }
 296         dev_dbg(sst->dev, "copied fw to %p", sst->fw_in_mem);
 297         dev_dbg(sst->dev, "phys: %lx", (unsigned long)virt_to_phys(sst->fw_in_mem));
 298         memcpy(sst->fw_in_mem, fw->data, fw->size);
 299         retval = sst_parse_fw_memcpy(sst, fw->size, &sst->memcpy_list);
 300         if (retval) {
 301                 dev_err(sst->dev, "Failed to parse fw\n");
 302                 kfree(sst->fw_in_mem);
 303                 sst->fw_in_mem = NULL;
 304         }
 305 
 306 end_release:
 307         release_firmware(fw);
 308         return retval;
 309 
 310 }
 311 
 312 void sst_firmware_load_cb(const struct firmware *fw, void *context)
 313 {
 314         struct intel_sst_drv *ctx = context;
 315 
 316         dev_dbg(ctx->dev, "Enter\n");
 317 
 318         if (fw == NULL) {
 319                 dev_err(ctx->dev, "request fw failed\n");
 320                 return;
 321         }
 322 
 323         mutex_lock(&ctx->sst_lock);
 324 
 325         if (ctx->sst_state != SST_RESET ||
 326                         ctx->fw_in_mem != NULL) {
 327                 release_firmware(fw);
 328                 mutex_unlock(&ctx->sst_lock);
 329                 return;
 330         }
 331 
 332         dev_dbg(ctx->dev, "Request Fw completed\n");
 333         sst_cache_and_parse_fw(ctx, fw);
 334         mutex_unlock(&ctx->sst_lock);
 335 }
 336 
 337 /*
 338  * sst_request_fw - requests audio fw from kernel and saves a copy
 339  *
 340  * This function requests the SST FW from the kernel, parses it and
 341  * saves a copy in the driver context
 342  */
 343 static int sst_request_fw(struct intel_sst_drv *sst)
 344 {
 345         int retval = 0;
 346         const struct firmware *fw;
 347 
 348         retval = request_firmware(&fw, sst->firmware_name, sst->dev);
 349         if (retval) {
 350                 dev_err(sst->dev, "request fw failed %d\n", retval);
 351                 return retval;
 352         }
 353         if (fw == NULL) {
 354                 dev_err(sst->dev, "fw is returning as null\n");
 355                 return -EINVAL;
 356         }
 357         mutex_lock(&sst->sst_lock);
 358         retval = sst_cache_and_parse_fw(sst, fw);
 359         mutex_unlock(&sst->sst_lock);
 360 
 361         return retval;
 362 }
 363 
 364 /*
 365  * Writing the DDR physical base to DCCM offset
 366  * so that FW can use it to setup TLB
 367  */
 368 static void sst_dccm_config_write(void __iomem *dram_base,
 369                 unsigned int ddr_base)
 370 {
 371         void __iomem *addr;
 372         u32 bss_reset = 0;
 373 
 374         addr = (void __iomem *)(dram_base + MRFLD_FW_DDR_BASE_OFFSET);
 375         memcpy32_toio(addr, (void *)&ddr_base, sizeof(u32));
 376         bss_reset |= (1 << MRFLD_FW_BSS_RESET_BIT);
 377         addr = (void __iomem *)(dram_base + MRFLD_FW_FEATURE_BASE_OFFSET);
 378         memcpy32_toio(addr, &bss_reset, sizeof(u32));
 379 
 380 }
 381 
 382 void sst_post_download_mrfld(struct intel_sst_drv *ctx)
 383 {
 384         sst_dccm_config_write(ctx->dram, ctx->ddr_base);
 385         dev_dbg(ctx->dev, "config written to DCCM\n");
 386 }
 387 
 388 /**
 389  * sst_load_fw - function to load FW into DSP
 390  * Transfers the FW to DSP using dma/memcpy
 391  */
 392 int sst_load_fw(struct intel_sst_drv *sst_drv_ctx)
 393 {
 394         int ret_val = 0;
 395         struct sst_block *block;
 396 
 397         dev_dbg(sst_drv_ctx->dev, "sst_load_fw\n");
 398 
 399         if (sst_drv_ctx->sst_state !=  SST_RESET ||
 400                         sst_drv_ctx->sst_state == SST_SHUTDOWN)
 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         pm_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 
 443 restore:
 444         /* Re-enable Deeper C-states beyond C6 */
 445         pm_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 

/* [<][>][^][v][top][bottom][index][help] */