162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * skl-sst.c - HDA DSP library functions for SKL platform 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014-15, Intel Corporation. 662306a36Sopenharmony_ci * Author:Rafal Redzimski <rafal.f.redzimski@intel.com> 762306a36Sopenharmony_ci * Jeeja KP <jeeja.kp@intel.com> 862306a36Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci#include <linux/device.h> 1462306a36Sopenharmony_ci#include <linux/err.h> 1562306a36Sopenharmony_ci#include <linux/uuid.h> 1662306a36Sopenharmony_ci#include "../common/sst-dsp.h" 1762306a36Sopenharmony_ci#include "../common/sst-dsp-priv.h" 1862306a36Sopenharmony_ci#include "../common/sst-ipc.h" 1962306a36Sopenharmony_ci#include "skl.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define SKL_BASEFW_TIMEOUT 300 2262306a36Sopenharmony_ci#define SKL_INIT_TIMEOUT 1000 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* Intel HD Audio SRAM Window 0*/ 2562306a36Sopenharmony_ci#define SKL_ADSP_SRAM0_BASE 0x8000 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* Firmware status window */ 2862306a36Sopenharmony_ci#define SKL_ADSP_FW_STATUS SKL_ADSP_SRAM0_BASE 2962306a36Sopenharmony_ci#define SKL_ADSP_ERROR_CODE (SKL_ADSP_FW_STATUS + 0x4) 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define SKL_NUM_MODULES 1 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic bool skl_check_fw_status(struct sst_dsp *ctx, u32 status) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci u32 cur_sts; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci cur_sts = sst_dsp_shim_read(ctx, SKL_ADSP_FW_STATUS) & SKL_FW_STS_MASK; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci return (cur_sts == status); 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic int skl_transfer_firmware(struct sst_dsp *ctx, 4362306a36Sopenharmony_ci const void *basefw, u32 base_fw_size) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci int ret = 0; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci ret = ctx->cl_dev.ops.cl_copy_to_dmabuf(ctx, basefw, base_fw_size, 4862306a36Sopenharmony_ci true); 4962306a36Sopenharmony_ci if (ret < 0) 5062306a36Sopenharmony_ci return ret; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci ret = sst_dsp_register_poll(ctx, 5362306a36Sopenharmony_ci SKL_ADSP_FW_STATUS, 5462306a36Sopenharmony_ci SKL_FW_STS_MASK, 5562306a36Sopenharmony_ci SKL_FW_RFW_START, 5662306a36Sopenharmony_ci SKL_BASEFW_TIMEOUT, 5762306a36Sopenharmony_ci "Firmware boot"); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci ctx->cl_dev.ops.cl_stop_dma(ctx); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci return ret; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define SKL_ADSP_FW_BIN_HDR_OFFSET 0x284 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic int skl_load_base_firmware(struct sst_dsp *ctx) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci int ret = 0, i; 6962306a36Sopenharmony_ci struct skl_dev *skl = ctx->thread_context; 7062306a36Sopenharmony_ci struct firmware stripped_fw; 7162306a36Sopenharmony_ci u32 reg; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci skl->boot_complete = false; 7462306a36Sopenharmony_ci init_waitqueue_head(&skl->boot_wait); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (ctx->fw == NULL) { 7762306a36Sopenharmony_ci ret = request_firmware(&ctx->fw, ctx->fw_name, ctx->dev); 7862306a36Sopenharmony_ci if (ret < 0) { 7962306a36Sopenharmony_ci dev_err(ctx->dev, "Request firmware failed %d\n", ret); 8062306a36Sopenharmony_ci return -EIO; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* prase uuids on first boot */ 8562306a36Sopenharmony_ci if (skl->is_first_boot) { 8662306a36Sopenharmony_ci ret = snd_skl_parse_uuids(ctx, ctx->fw, SKL_ADSP_FW_BIN_HDR_OFFSET, 0); 8762306a36Sopenharmony_ci if (ret < 0) { 8862306a36Sopenharmony_ci dev_err(ctx->dev, "UUID parsing err: %d\n", ret); 8962306a36Sopenharmony_ci release_firmware(ctx->fw); 9062306a36Sopenharmony_ci skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); 9162306a36Sopenharmony_ci return ret; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci /* check for extended manifest */ 9662306a36Sopenharmony_ci stripped_fw.data = ctx->fw->data; 9762306a36Sopenharmony_ci stripped_fw.size = ctx->fw->size; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci skl_dsp_strip_extended_manifest(&stripped_fw); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci ret = skl_dsp_boot(ctx); 10262306a36Sopenharmony_ci if (ret < 0) { 10362306a36Sopenharmony_ci dev_err(ctx->dev, "Boot dsp core failed ret: %d\n", ret); 10462306a36Sopenharmony_ci goto skl_load_base_firmware_failed; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci ret = skl_cldma_prepare(ctx); 10862306a36Sopenharmony_ci if (ret < 0) { 10962306a36Sopenharmony_ci dev_err(ctx->dev, "CL dma prepare failed : %d\n", ret); 11062306a36Sopenharmony_ci goto skl_load_base_firmware_failed; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci /* enable Interrupt */ 11462306a36Sopenharmony_ci skl_ipc_int_enable(ctx); 11562306a36Sopenharmony_ci skl_ipc_op_int_enable(ctx); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* check ROM Status */ 11862306a36Sopenharmony_ci for (i = SKL_INIT_TIMEOUT; i > 0; --i) { 11962306a36Sopenharmony_ci if (skl_check_fw_status(ctx, SKL_FW_INIT)) { 12062306a36Sopenharmony_ci dev_dbg(ctx->dev, 12162306a36Sopenharmony_ci "ROM loaded, we can continue with FW loading\n"); 12262306a36Sopenharmony_ci break; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci mdelay(1); 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci if (!i) { 12762306a36Sopenharmony_ci reg = sst_dsp_shim_read(ctx, SKL_ADSP_FW_STATUS); 12862306a36Sopenharmony_ci dev_err(ctx->dev, 12962306a36Sopenharmony_ci "Timeout waiting for ROM init done, reg:0x%x\n", reg); 13062306a36Sopenharmony_ci ret = -EIO; 13162306a36Sopenharmony_ci goto transfer_firmware_failed; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci ret = skl_transfer_firmware(ctx, stripped_fw.data, stripped_fw.size); 13562306a36Sopenharmony_ci if (ret < 0) { 13662306a36Sopenharmony_ci dev_err(ctx->dev, "Transfer firmware failed%d\n", ret); 13762306a36Sopenharmony_ci goto transfer_firmware_failed; 13862306a36Sopenharmony_ci } else { 13962306a36Sopenharmony_ci ret = wait_event_timeout(skl->boot_wait, skl->boot_complete, 14062306a36Sopenharmony_ci msecs_to_jiffies(SKL_IPC_BOOT_MSECS)); 14162306a36Sopenharmony_ci if (ret == 0) { 14262306a36Sopenharmony_ci dev_err(ctx->dev, "DSP boot failed, FW Ready timed-out\n"); 14362306a36Sopenharmony_ci ret = -EIO; 14462306a36Sopenharmony_ci goto transfer_firmware_failed; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci dev_dbg(ctx->dev, "Download firmware successful%d\n", ret); 14862306a36Sopenharmony_ci skl->fw_loaded = true; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci return 0; 15162306a36Sopenharmony_citransfer_firmware_failed: 15262306a36Sopenharmony_ci ctx->cl_dev.ops.cl_cleanup_controller(ctx); 15362306a36Sopenharmony_ciskl_load_base_firmware_failed: 15462306a36Sopenharmony_ci skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); 15562306a36Sopenharmony_ci release_firmware(ctx->fw); 15662306a36Sopenharmony_ci ctx->fw = NULL; 15762306a36Sopenharmony_ci return ret; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic int skl_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci int ret; 16362306a36Sopenharmony_ci struct skl_ipc_dxstate_info dx; 16462306a36Sopenharmony_ci struct skl_dev *skl = ctx->thread_context; 16562306a36Sopenharmony_ci unsigned int core_mask = SKL_DSP_CORE_MASK(core_id); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* If core0 is being turned on, we need to load the FW */ 16862306a36Sopenharmony_ci if (core_id == SKL_DSP_CORE0_ID) { 16962306a36Sopenharmony_ci ret = skl_load_base_firmware(ctx); 17062306a36Sopenharmony_ci if (ret < 0) { 17162306a36Sopenharmony_ci dev_err(ctx->dev, "unable to load firmware\n"); 17262306a36Sopenharmony_ci return ret; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci /* load libs as they are also lost on D3 */ 17662306a36Sopenharmony_ci if (skl->lib_count > 1) { 17762306a36Sopenharmony_ci ret = ctx->fw_ops.load_library(ctx, skl->lib_info, 17862306a36Sopenharmony_ci skl->lib_count); 17962306a36Sopenharmony_ci if (ret < 0) { 18062306a36Sopenharmony_ci dev_err(ctx->dev, "reload libs failed: %d\n", 18162306a36Sopenharmony_ci ret); 18262306a36Sopenharmony_ci return ret; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* 18962306a36Sopenharmony_ci * If any core other than core 0 is being moved to D0, enable the 19062306a36Sopenharmony_ci * core and send the set dx IPC for the core. 19162306a36Sopenharmony_ci */ 19262306a36Sopenharmony_ci if (core_id != SKL_DSP_CORE0_ID) { 19362306a36Sopenharmony_ci ret = skl_dsp_enable_core(ctx, core_mask); 19462306a36Sopenharmony_ci if (ret < 0) 19562306a36Sopenharmony_ci return ret; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci dx.core_mask = core_mask; 19862306a36Sopenharmony_ci dx.dx_mask = core_mask; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID, 20162306a36Sopenharmony_ci SKL_BASE_FW_MODULE_ID, &dx); 20262306a36Sopenharmony_ci if (ret < 0) { 20362306a36Sopenharmony_ci dev_err(ctx->dev, "Failed to set dsp to D0:core id= %d\n", 20462306a36Sopenharmony_ci core_id); 20562306a36Sopenharmony_ci skl_dsp_disable_core(ctx, core_mask); 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci skl->cores.state[core_id] = SKL_DSP_RUNNING; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci return 0; 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic int skl_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci int ret; 21762306a36Sopenharmony_ci struct skl_ipc_dxstate_info dx; 21862306a36Sopenharmony_ci struct skl_dev *skl = ctx->thread_context; 21962306a36Sopenharmony_ci unsigned int core_mask = SKL_DSP_CORE_MASK(core_id); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci dx.core_mask = core_mask; 22262306a36Sopenharmony_ci dx.dx_mask = SKL_IPC_D3_MASK; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID, SKL_BASE_FW_MODULE_ID, &dx); 22562306a36Sopenharmony_ci if (ret < 0) 22662306a36Sopenharmony_ci dev_err(ctx->dev, "set Dx core %d fail: %d\n", core_id, ret); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (core_id == SKL_DSP_CORE0_ID) { 22962306a36Sopenharmony_ci /* disable Interrupt */ 23062306a36Sopenharmony_ci ctx->cl_dev.ops.cl_cleanup_controller(ctx); 23162306a36Sopenharmony_ci skl_cldma_int_disable(ctx); 23262306a36Sopenharmony_ci skl_ipc_op_int_disable(ctx); 23362306a36Sopenharmony_ci skl_ipc_int_disable(ctx); 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci ret = skl_dsp_disable_core(ctx, core_mask); 23762306a36Sopenharmony_ci if (ret < 0) 23862306a36Sopenharmony_ci return ret; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci skl->cores.state[core_id] = SKL_DSP_RESET; 24162306a36Sopenharmony_ci return ret; 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic unsigned int skl_get_errorcode(struct sst_dsp *ctx) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci return sst_dsp_shim_read(ctx, SKL_ADSP_ERROR_CODE); 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci/* 25062306a36Sopenharmony_ci * since get/set_module are called from DAPM context, 25162306a36Sopenharmony_ci * we don't need lock for usage count 25262306a36Sopenharmony_ci */ 25362306a36Sopenharmony_cistatic int skl_get_module(struct sst_dsp *ctx, u16 mod_id) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci struct skl_module_table *module; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci list_for_each_entry(module, &ctx->module_list, list) { 25862306a36Sopenharmony_ci if (module->mod_info->mod_id == mod_id) 25962306a36Sopenharmony_ci return ++module->usage_cnt; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci return -EINVAL; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic int skl_put_module(struct sst_dsp *ctx, u16 mod_id) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci struct skl_module_table *module; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci list_for_each_entry(module, &ctx->module_list, list) { 27062306a36Sopenharmony_ci if (module->mod_info->mod_id == mod_id) 27162306a36Sopenharmony_ci return --module->usage_cnt; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci return -EINVAL; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic struct skl_module_table *skl_fill_module_table(struct sst_dsp *ctx, 27862306a36Sopenharmony_ci char *mod_name, int mod_id) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci const struct firmware *fw; 28162306a36Sopenharmony_ci struct skl_module_table *skl_module; 28262306a36Sopenharmony_ci unsigned int size; 28362306a36Sopenharmony_ci int ret; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci ret = request_firmware(&fw, mod_name, ctx->dev); 28662306a36Sopenharmony_ci if (ret < 0) { 28762306a36Sopenharmony_ci dev_err(ctx->dev, "Request Module %s failed :%d\n", 28862306a36Sopenharmony_ci mod_name, ret); 28962306a36Sopenharmony_ci return NULL; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci skl_module = devm_kzalloc(ctx->dev, sizeof(*skl_module), GFP_KERNEL); 29362306a36Sopenharmony_ci if (skl_module == NULL) { 29462306a36Sopenharmony_ci release_firmware(fw); 29562306a36Sopenharmony_ci return NULL; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci size = sizeof(*skl_module->mod_info); 29962306a36Sopenharmony_ci skl_module->mod_info = devm_kzalloc(ctx->dev, size, GFP_KERNEL); 30062306a36Sopenharmony_ci if (skl_module->mod_info == NULL) { 30162306a36Sopenharmony_ci release_firmware(fw); 30262306a36Sopenharmony_ci return NULL; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci skl_module->mod_info->mod_id = mod_id; 30662306a36Sopenharmony_ci skl_module->mod_info->fw = fw; 30762306a36Sopenharmony_ci list_add(&skl_module->list, &ctx->module_list); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci return skl_module; 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci/* get a module from it's unique ID */ 31362306a36Sopenharmony_cistatic struct skl_module_table *skl_module_get_from_id( 31462306a36Sopenharmony_ci struct sst_dsp *ctx, u16 mod_id) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci struct skl_module_table *module; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (list_empty(&ctx->module_list)) { 31962306a36Sopenharmony_ci dev_err(ctx->dev, "Module list is empty\n"); 32062306a36Sopenharmony_ci return NULL; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci list_for_each_entry(module, &ctx->module_list, list) { 32462306a36Sopenharmony_ci if (module->mod_info->mod_id == mod_id) 32562306a36Sopenharmony_ci return module; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci return NULL; 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_cistatic int skl_transfer_module(struct sst_dsp *ctx, const void *data, 33262306a36Sopenharmony_ci u32 size, u16 mod_id, u8 table_id, bool is_module) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci int ret, bytes_left, curr_pos; 33562306a36Sopenharmony_ci struct skl_dev *skl = ctx->thread_context; 33662306a36Sopenharmony_ci skl->mod_load_complete = false; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci bytes_left = ctx->cl_dev.ops.cl_copy_to_dmabuf(ctx, data, size, false); 33962306a36Sopenharmony_ci if (bytes_left < 0) 34062306a36Sopenharmony_ci return bytes_left; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci /* check is_module flag to load module or library */ 34362306a36Sopenharmony_ci if (is_module) 34462306a36Sopenharmony_ci ret = skl_ipc_load_modules(&skl->ipc, SKL_NUM_MODULES, &mod_id); 34562306a36Sopenharmony_ci else 34662306a36Sopenharmony_ci ret = skl_sst_ipc_load_library(&skl->ipc, 0, table_id, false); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (ret < 0) { 34962306a36Sopenharmony_ci dev_err(ctx->dev, "Failed to Load %s with err %d\n", 35062306a36Sopenharmony_ci is_module ? "module" : "lib", ret); 35162306a36Sopenharmony_ci goto out; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci /* 35562306a36Sopenharmony_ci * if bytes_left > 0 then wait for BDL complete interrupt and 35662306a36Sopenharmony_ci * copy the next chunk till bytes_left is 0. if bytes_left is 35762306a36Sopenharmony_ci * zero, then wait for load module IPC reply 35862306a36Sopenharmony_ci */ 35962306a36Sopenharmony_ci while (bytes_left > 0) { 36062306a36Sopenharmony_ci curr_pos = size - bytes_left; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci ret = skl_cldma_wait_interruptible(ctx); 36362306a36Sopenharmony_ci if (ret < 0) 36462306a36Sopenharmony_ci goto out; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci bytes_left = ctx->cl_dev.ops.cl_copy_to_dmabuf(ctx, 36762306a36Sopenharmony_ci data + curr_pos, 36862306a36Sopenharmony_ci bytes_left, false); 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci ret = wait_event_timeout(skl->mod_load_wait, skl->mod_load_complete, 37262306a36Sopenharmony_ci msecs_to_jiffies(SKL_IPC_BOOT_MSECS)); 37362306a36Sopenharmony_ci if (ret == 0 || !skl->mod_load_status) { 37462306a36Sopenharmony_ci dev_err(ctx->dev, "Module Load failed\n"); 37562306a36Sopenharmony_ci ret = -EIO; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ciout: 37962306a36Sopenharmony_ci ctx->cl_dev.ops.cl_stop_dma(ctx); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci return ret; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic int 38562306a36Sopenharmony_ciskl_load_library(struct sst_dsp *ctx, struct skl_lib_info *linfo, int lib_count) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci struct skl_dev *skl = ctx->thread_context; 38862306a36Sopenharmony_ci struct firmware stripped_fw; 38962306a36Sopenharmony_ci int ret, i; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci /* library indices start from 1 to N. 0 represents base FW */ 39262306a36Sopenharmony_ci for (i = 1; i < lib_count; i++) { 39362306a36Sopenharmony_ci ret = skl_prepare_lib_load(skl, &skl->lib_info[i], &stripped_fw, 39462306a36Sopenharmony_ci SKL_ADSP_FW_BIN_HDR_OFFSET, i); 39562306a36Sopenharmony_ci if (ret < 0) 39662306a36Sopenharmony_ci goto load_library_failed; 39762306a36Sopenharmony_ci ret = skl_transfer_module(ctx, stripped_fw.data, 39862306a36Sopenharmony_ci stripped_fw.size, 0, i, false); 39962306a36Sopenharmony_ci if (ret < 0) 40062306a36Sopenharmony_ci goto load_library_failed; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci return 0; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ciload_library_failed: 40662306a36Sopenharmony_ci skl_release_library(linfo, lib_count); 40762306a36Sopenharmony_ci return ret; 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic int skl_load_module(struct sst_dsp *ctx, u16 mod_id, u8 *guid) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci struct skl_module_table *module_entry = NULL; 41362306a36Sopenharmony_ci int ret = 0; 41462306a36Sopenharmony_ci char mod_name[64]; /* guid str = 32 chars + 4 hyphens */ 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci snprintf(mod_name, sizeof(mod_name), "intel/dsp_fw_%pUL.bin", guid); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci module_entry = skl_module_get_from_id(ctx, mod_id); 41962306a36Sopenharmony_ci if (module_entry == NULL) { 42062306a36Sopenharmony_ci module_entry = skl_fill_module_table(ctx, mod_name, mod_id); 42162306a36Sopenharmony_ci if (module_entry == NULL) { 42262306a36Sopenharmony_ci dev_err(ctx->dev, "Failed to Load module\n"); 42362306a36Sopenharmony_ci return -EINVAL; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (!module_entry->usage_cnt) { 42862306a36Sopenharmony_ci ret = skl_transfer_module(ctx, module_entry->mod_info->fw->data, 42962306a36Sopenharmony_ci module_entry->mod_info->fw->size, 43062306a36Sopenharmony_ci mod_id, 0, true); 43162306a36Sopenharmony_ci if (ret < 0) { 43262306a36Sopenharmony_ci dev_err(ctx->dev, "Failed to Load module\n"); 43362306a36Sopenharmony_ci return ret; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci ret = skl_get_module(ctx, mod_id); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci return ret; 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cistatic int skl_unload_module(struct sst_dsp *ctx, u16 mod_id) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci int usage_cnt; 44562306a36Sopenharmony_ci struct skl_dev *skl = ctx->thread_context; 44662306a36Sopenharmony_ci int ret = 0; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci usage_cnt = skl_put_module(ctx, mod_id); 44962306a36Sopenharmony_ci if (usage_cnt < 0) { 45062306a36Sopenharmony_ci dev_err(ctx->dev, "Module bad usage cnt!:%d\n", usage_cnt); 45162306a36Sopenharmony_ci return -EIO; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci /* if module is used by others return, no need to unload */ 45562306a36Sopenharmony_ci if (usage_cnt > 0) 45662306a36Sopenharmony_ci return 0; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci ret = skl_ipc_unload_modules(&skl->ipc, 45962306a36Sopenharmony_ci SKL_NUM_MODULES, &mod_id); 46062306a36Sopenharmony_ci if (ret < 0) { 46162306a36Sopenharmony_ci dev_err(ctx->dev, "Failed to UnLoad module\n"); 46262306a36Sopenharmony_ci skl_get_module(ctx, mod_id); 46362306a36Sopenharmony_ci return ret; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci return ret; 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_civoid skl_clear_module_cnt(struct sst_dsp *ctx) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci struct skl_module_table *module; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (list_empty(&ctx->module_list)) 47462306a36Sopenharmony_ci return; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci list_for_each_entry(module, &ctx->module_list, list) { 47762306a36Sopenharmony_ci module->usage_cnt = 0; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(skl_clear_module_cnt); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic void skl_clear_module_table(struct sst_dsp *ctx) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci struct skl_module_table *module, *tmp; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if (list_empty(&ctx->module_list)) 48762306a36Sopenharmony_ci return; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci list_for_each_entry_safe(module, tmp, &ctx->module_list, list) { 49062306a36Sopenharmony_ci list_del(&module->list); 49162306a36Sopenharmony_ci release_firmware(module->mod_info->fw); 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic const struct skl_dsp_fw_ops skl_fw_ops = { 49662306a36Sopenharmony_ci .set_state_D0 = skl_set_dsp_D0, 49762306a36Sopenharmony_ci .set_state_D3 = skl_set_dsp_D3, 49862306a36Sopenharmony_ci .load_fw = skl_load_base_firmware, 49962306a36Sopenharmony_ci .get_fw_errcode = skl_get_errorcode, 50062306a36Sopenharmony_ci .load_library = skl_load_library, 50162306a36Sopenharmony_ci .load_mod = skl_load_module, 50262306a36Sopenharmony_ci .unload_mod = skl_unload_module, 50362306a36Sopenharmony_ci}; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistatic struct sst_ops skl_ops = { 50662306a36Sopenharmony_ci .irq_handler = skl_dsp_sst_interrupt, 50762306a36Sopenharmony_ci .write = sst_shim32_write, 50862306a36Sopenharmony_ci .read = sst_shim32_read, 50962306a36Sopenharmony_ci .free = skl_dsp_free, 51062306a36Sopenharmony_ci}; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_cistatic struct sst_dsp_device skl_dev = { 51362306a36Sopenharmony_ci .thread = skl_dsp_irq_thread_handler, 51462306a36Sopenharmony_ci .ops = &skl_ops, 51562306a36Sopenharmony_ci}; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ciint skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, 51862306a36Sopenharmony_ci const char *fw_name, struct skl_dsp_loader_ops dsp_ops, 51962306a36Sopenharmony_ci struct skl_dev **dsp) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci struct skl_dev *skl; 52262306a36Sopenharmony_ci struct sst_dsp *sst; 52362306a36Sopenharmony_ci int ret; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci ret = skl_sst_ctx_init(dev, irq, fw_name, dsp_ops, dsp, &skl_dev); 52662306a36Sopenharmony_ci if (ret < 0) { 52762306a36Sopenharmony_ci dev_err(dev, "%s: no device\n", __func__); 52862306a36Sopenharmony_ci return ret; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci skl = *dsp; 53262306a36Sopenharmony_ci sst = skl->dsp; 53362306a36Sopenharmony_ci sst->addr.lpe = mmio_base; 53462306a36Sopenharmony_ci sst->addr.shim = mmio_base; 53562306a36Sopenharmony_ci sst->addr.sram0_base = SKL_ADSP_SRAM0_BASE; 53662306a36Sopenharmony_ci sst->addr.sram1_base = SKL_ADSP_SRAM1_BASE; 53762306a36Sopenharmony_ci sst->addr.w0_stat_sz = SKL_ADSP_W0_STAT_SZ; 53862306a36Sopenharmony_ci sst->addr.w0_up_sz = SKL_ADSP_W0_UP_SZ; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci sst_dsp_mailbox_init(sst, (SKL_ADSP_SRAM0_BASE + SKL_ADSP_W0_STAT_SZ), 54162306a36Sopenharmony_ci SKL_ADSP_W0_UP_SZ, SKL_ADSP_SRAM1_BASE, SKL_ADSP_W1_SZ); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci ret = skl_ipc_init(dev, skl); 54462306a36Sopenharmony_ci if (ret) { 54562306a36Sopenharmony_ci skl_dsp_free(sst); 54662306a36Sopenharmony_ci return ret; 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci sst->fw_ops = skl_fw_ops; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci return skl_dsp_acquire_irq(sst); 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(skl_sst_dsp_init); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ciint skl_sst_init_fw(struct device *dev, struct skl_dev *skl) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci int ret; 55862306a36Sopenharmony_ci struct sst_dsp *sst = skl->dsp; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci ret = sst->fw_ops.load_fw(sst); 56162306a36Sopenharmony_ci if (ret < 0) { 56262306a36Sopenharmony_ci dev_err(dev, "Load base fw failed : %d\n", ret); 56362306a36Sopenharmony_ci return ret; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci skl_dsp_init_core_state(sst); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci if (skl->lib_count > 1) { 56962306a36Sopenharmony_ci ret = sst->fw_ops.load_library(sst, skl->lib_info, 57062306a36Sopenharmony_ci skl->lib_count); 57162306a36Sopenharmony_ci if (ret < 0) { 57262306a36Sopenharmony_ci dev_err(dev, "Load Library failed : %x\n", ret); 57362306a36Sopenharmony_ci return ret; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci skl->is_first_boot = false; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci return 0; 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(skl_sst_init_fw); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_civoid skl_sst_dsp_cleanup(struct device *dev, struct skl_dev *skl) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (skl->dsp->fw) 58662306a36Sopenharmony_ci release_firmware(skl->dsp->fw); 58762306a36Sopenharmony_ci skl_clear_module_table(skl->dsp); 58862306a36Sopenharmony_ci skl_freeup_uuid_list(skl); 58962306a36Sopenharmony_ci skl_ipc_free(&skl->ipc); 59062306a36Sopenharmony_ci skl->dsp->ops->free(skl->dsp); 59162306a36Sopenharmony_ci if (skl->boot_complete) { 59262306a36Sopenharmony_ci skl->dsp->cl_dev.ops.cl_cleanup_controller(skl->dsp); 59362306a36Sopenharmony_ci skl_cldma_int_disable(skl->dsp); 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(skl_sst_dsp_cleanup); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 59962306a36Sopenharmony_ciMODULE_DESCRIPTION("Intel Skylake IPC driver"); 600