Path: blob/master/sound/soc/intel/atom/sst/sst_loader.c
26516 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* sst_dsp.c - Intel SST Driver for audio engine3*4* Copyright (C) 2008-14 Intel Corp5* Authors: Vinod Koul <[email protected]>6* Harsha Priya <[email protected]>7* Dharageswari R <[email protected]>8* KP Jeeja <[email protected]>9* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~10*11* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~12*13* This file contains all dsp controlling functions like firmware download,14* setting/resetting dsp cores, etc15*/16#include <linux/pci.h>17#include <linux/delay.h>18#include <linux/fs.h>19#include <linux/sched.h>20#include <linux/firmware.h>21#include <linux/dmaengine.h>22#include <linux/pm_qos.h>23#include <sound/core.h>24#include <sound/pcm.h>25#include <sound/soc.h>26#include <sound/compress_driver.h>27#include <asm/platform_sst_audio.h>28#include "../sst-mfld-platform.h"29#include "sst.h"3031void memcpy32_toio(void __iomem *dst, const void *src, int count)32{33/* __iowrite32_copy uses 32-bit count values so divide by 4 for34* right count in words35*/36__iowrite32_copy(dst, src, count / 4);37}3839void memcpy32_fromio(void *dst, const void __iomem *src, int count)40{41/* __ioread32_copy uses 32-bit count values so divide by 4 for42* right count in words43*/44__ioread32_copy(dst, src, count / 4);45}4647/**48* intel_sst_reset_dsp_mrfld - Resetting SST DSP49* @sst_drv_ctx: intel_sst_drv context pointer50*51* This resets DSP in case of MRFLD platfroms52*/53int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *sst_drv_ctx)54{55union config_status_reg_mrfld csr;5657dev_dbg(sst_drv_ctx->dev, "sst: Resetting the DSP in mrfld\n");58csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);5960dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);6162csr.full |= 0x7;63sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);64csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);6566dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);6768csr.full &= ~(0x1);69sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);7071csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);72dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);73return 0;74}7576/**77* sst_start_mrfld - Start the SST DSP processor78* @sst_drv_ctx: intel_sst_drv context pointer79*80* This starts the DSP in MERRIFIELD platfroms81*/82int sst_start_mrfld(struct intel_sst_drv *sst_drv_ctx)83{84union config_status_reg_mrfld csr;8586dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP in mrfld LALALALA\n");87csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);88dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);8990csr.full |= 0x7;91sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);9293csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);94dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);9596csr.part.xt_snoop = 1;97csr.full &= ~(0x5);98sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);99100csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);101dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP_merrifield:%llx\n",102csr.full);103return 0;104}105106static int sst_validate_fw_image(struct intel_sst_drv *ctx, unsigned long size,107struct fw_module_header **module, u32 *num_modules)108{109struct sst_fw_header *header;110const void *sst_fw_in_mem = ctx->fw_in_mem;111112dev_dbg(ctx->dev, "Enter\n");113114/* Read the header information from the data pointer */115header = (struct sst_fw_header *)sst_fw_in_mem;116dev_dbg(ctx->dev,117"header sign=%s size=%x modules=%x fmt=%x size=%zx\n",118header->signature, header->file_size, header->modules,119header->file_format, sizeof(*header));120121/* verify FW */122if ((strncmp(header->signature, SST_FW_SIGN, 4) != 0) ||123(size != header->file_size + sizeof(*header))) {124/* Invalid FW signature */125dev_err(ctx->dev, "InvalidFW sign/filesize mismatch\n");126return -EINVAL;127}128*num_modules = header->modules;129*module = (void *)sst_fw_in_mem + sizeof(*header);130131return 0;132}133134/*135* sst_fill_memcpy_list - Fill the memcpy list136*137* @memcpy_list: List to be filled138* @destn: Destination addr to be filled in the list139* @src: Source addr to be filled in the list140* @size: Size to be filled in the list141*142* Adds the node to the list after required fields143* are populated in the node144*/145static int sst_fill_memcpy_list(struct list_head *memcpy_list,146void *destn, const void *src, u32 size, bool is_io)147{148struct sst_memcpy_list *listnode;149150listnode = kzalloc(sizeof(*listnode), GFP_KERNEL);151if (listnode == NULL)152return -ENOMEM;153listnode->dstn = destn;154listnode->src = src;155listnode->size = size;156listnode->is_io = is_io;157list_add_tail(&listnode->memcpylist, memcpy_list);158159return 0;160}161162/**163* sst_parse_module_memcpy - Parse audio FW modules and populate the memcpy list164*165* @sst_drv_ctx : driver context166* @module : FW module header167* @memcpy_list : Pointer to the list to be populated168* Create the memcpy list as the number of block to be copied169* returns error or 0 if module sizes are proper170*/171static int sst_parse_module_memcpy(struct intel_sst_drv *sst_drv_ctx,172struct fw_module_header *module, struct list_head *memcpy_list)173{174struct fw_block_info *block;175u32 count;176int ret_val = 0;177void __iomem *ram_iomem;178179dev_dbg(sst_drv_ctx->dev, "module sign %s size %x blocks %x type %x\n",180module->signature, module->mod_size,181module->blocks, module->type);182dev_dbg(sst_drv_ctx->dev, "module entrypoint 0x%x\n", module->entry_point);183184block = (void *)module + sizeof(*module);185186for (count = 0; count < module->blocks; count++) {187if (block->size <= 0) {188dev_err(sst_drv_ctx->dev, "block size invalid\n");189return -EINVAL;190}191switch (block->type) {192case SST_IRAM:193ram_iomem = sst_drv_ctx->iram;194break;195case SST_DRAM:196ram_iomem = sst_drv_ctx->dram;197break;198case SST_DDR:199ram_iomem = sst_drv_ctx->ddr;200break;201case SST_CUSTOM_INFO:202block = (void *)block + sizeof(*block) + block->size;203continue;204default:205dev_err(sst_drv_ctx->dev, "wrong ram type0x%x in block0x%x\n",206block->type, count);207return -EINVAL;208}209210ret_val = sst_fill_memcpy_list(memcpy_list,211ram_iomem + block->ram_offset,212(void *)block + sizeof(*block), block->size, 1);213if (ret_val)214return ret_val;215216block = (void *)block + sizeof(*block) + block->size;217}218return 0;219}220221/**222* sst_parse_fw_memcpy - parse the firmware image & populate the list for memcpy223*224* @ctx : pointer to drv context225* @size : size of the firmware226* @fw_list : pointer to list_head to be populated227* This function parses the FW image and saves the parsed image in the list228* for memcpy229*/230static int sst_parse_fw_memcpy(struct intel_sst_drv *ctx, unsigned long size,231struct list_head *fw_list)232{233struct fw_module_header *module;234u32 count, num_modules;235int ret_val;236237ret_val = sst_validate_fw_image(ctx, size, &module, &num_modules);238if (ret_val)239return ret_val;240241for (count = 0; count < num_modules; count++) {242ret_val = sst_parse_module_memcpy(ctx, module, fw_list);243if (ret_val)244return ret_val;245module = (void *)module + sizeof(*module) + module->mod_size;246}247248return 0;249}250251/**252* sst_do_memcpy - function initiates the memcpy253*254* @memcpy_list: Pter to memcpy list on which the memcpy needs to be initiated255*256* Triggers the memcpy257*/258static void sst_do_memcpy(struct list_head *memcpy_list)259{260struct sst_memcpy_list *listnode;261262list_for_each_entry(listnode, memcpy_list, memcpylist) {263if (listnode->is_io)264memcpy32_toio((void __iomem *)listnode->dstn,265listnode->src, listnode->size);266else267memcpy(listnode->dstn, listnode->src, listnode->size);268}269}270271void sst_memcpy_free_resources(struct intel_sst_drv *sst_drv_ctx)272{273struct sst_memcpy_list *listnode, *tmplistnode;274275/* Free the list */276list_for_each_entry_safe(listnode, tmplistnode,277&sst_drv_ctx->memcpy_list, memcpylist) {278list_del(&listnode->memcpylist);279kfree(listnode);280}281}282283static int sst_cache_and_parse_fw(struct intel_sst_drv *sst,284const struct firmware *fw)285{286int retval = 0;287288sst->fw_in_mem = kzalloc(fw->size, GFP_KERNEL);289if (!sst->fw_in_mem) {290retval = -ENOMEM;291goto end_release;292}293dev_dbg(sst->dev, "copied fw to %p", sst->fw_in_mem);294dev_dbg(sst->dev, "phys: %lx", (unsigned long)virt_to_phys(sst->fw_in_mem));295memcpy(sst->fw_in_mem, fw->data, fw->size);296retval = sst_parse_fw_memcpy(sst, fw->size, &sst->memcpy_list);297if (retval) {298dev_err(sst->dev, "Failed to parse fw\n");299kfree(sst->fw_in_mem);300sst->fw_in_mem = NULL;301}302303end_release:304release_firmware(fw);305return retval;306307}308309void sst_firmware_load_cb(const struct firmware *fw, void *context)310{311struct intel_sst_drv *ctx = context;312313dev_dbg(ctx->dev, "Enter\n");314315if (fw == NULL) {316dev_err(ctx->dev, "request fw failed\n");317return;318}319320mutex_lock(&ctx->sst_lock);321322if (ctx->sst_state != SST_RESET ||323ctx->fw_in_mem != NULL) {324release_firmware(fw);325mutex_unlock(&ctx->sst_lock);326return;327}328329dev_dbg(ctx->dev, "Request Fw completed\n");330sst_cache_and_parse_fw(ctx, fw);331mutex_unlock(&ctx->sst_lock);332}333334/*335* sst_request_fw - requests audio fw from kernel and saves a copy336*337* This function requests the SST FW from the kernel, parses it and338* saves a copy in the driver context339*/340static int sst_request_fw(struct intel_sst_drv *sst)341{342int retval = 0;343const struct firmware *fw;344345retval = request_firmware(&fw, sst->firmware_name, sst->dev);346if (retval) {347dev_err(sst->dev, "request fw failed %d\n", retval);348return retval;349}350if (fw == NULL) {351dev_err(sst->dev, "fw is returning as null\n");352return -EINVAL;353}354mutex_lock(&sst->sst_lock);355retval = sst_cache_and_parse_fw(sst, fw);356mutex_unlock(&sst->sst_lock);357358return retval;359}360361/*362* Writing the DDR physical base to DCCM offset363* so that FW can use it to setup TLB364*/365static void sst_dccm_config_write(void __iomem *dram_base,366unsigned int ddr_base)367{368void __iomem *addr;369u32 bss_reset = 0;370371addr = (void __iomem *)(dram_base + MRFLD_FW_DDR_BASE_OFFSET);372memcpy32_toio(addr, (void *)&ddr_base, sizeof(u32));373bss_reset |= (1 << MRFLD_FW_BSS_RESET_BIT);374addr = (void __iomem *)(dram_base + MRFLD_FW_FEATURE_BASE_OFFSET);375memcpy32_toio(addr, &bss_reset, sizeof(u32));376377}378379void sst_post_download_mrfld(struct intel_sst_drv *ctx)380{381sst_dccm_config_write(ctx->dram, ctx->ddr_base);382dev_dbg(ctx->dev, "config written to DCCM\n");383}384385/**386* sst_load_fw - function to load FW into DSP387* @sst_drv_ctx: intel_sst_drv context pointer388*389* Transfers the FW to DSP using dma/memcpy390*/391int sst_load_fw(struct intel_sst_drv *sst_drv_ctx)392{393int ret_val = 0;394struct sst_block *block;395396dev_dbg(sst_drv_ctx->dev, "sst_load_fw\n");397398if (sst_drv_ctx->sst_state != SST_RESET)399return -EAGAIN;400401if (!sst_drv_ctx->fw_in_mem) {402dev_dbg(sst_drv_ctx->dev, "sst: FW not in memory retry to download\n");403ret_val = sst_request_fw(sst_drv_ctx);404if (ret_val)405return ret_val;406}407408block = sst_create_block(sst_drv_ctx, 0, FW_DWNL_ID);409if (block == NULL)410return -ENOMEM;411412/* Prevent C-states beyond C6 */413cpu_latency_qos_update_request(sst_drv_ctx->qos, 0);414415sst_drv_ctx->sst_state = SST_FW_LOADING;416417ret_val = sst_drv_ctx->ops->reset(sst_drv_ctx);418if (ret_val)419goto restore;420421sst_do_memcpy(&sst_drv_ctx->memcpy_list);422423/* Write the DRAM/DCCM config before enabling FW */424if (sst_drv_ctx->ops->post_download)425sst_drv_ctx->ops->post_download(sst_drv_ctx);426427/* bring sst out of reset */428ret_val = sst_drv_ctx->ops->start(sst_drv_ctx);429if (ret_val)430goto restore;431432ret_val = sst_wait_timeout(sst_drv_ctx, block);433if (ret_val) {434dev_err(sst_drv_ctx->dev, "fw download failed %d\n" , ret_val);435/* FW download failed due to timeout */436ret_val = -EBUSY;437438}439440441restore:442/* Re-enable Deeper C-states beyond C6 */443cpu_latency_qos_update_request(sst_drv_ctx->qos, PM_QOS_DEFAULT_VALUE);444sst_free_block(sst_drv_ctx, block);445dev_dbg(sst_drv_ctx->dev, "fw load successful!!!\n");446447if (sst_drv_ctx->ops->restore_dsp_context)448sst_drv_ctx->ops->restore_dsp_context();449sst_drv_ctx->sst_state = SST_FW_RUNNING;450return ret_val;451}452453454455