18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * skl-sst.c - HDA DSP library functions for SKL platform 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2014-15, Intel Corporation. 68c2ecf20Sopenharmony_ci * Author:Rafal Redzimski <rafal.f.redzimski@intel.com> 78c2ecf20Sopenharmony_ci * Jeeja KP <jeeja.kp@intel.com> 88c2ecf20Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/delay.h> 138c2ecf20Sopenharmony_ci#include <linux/device.h> 148c2ecf20Sopenharmony_ci#include <linux/err.h> 158c2ecf20Sopenharmony_ci#include <linux/uuid.h> 168c2ecf20Sopenharmony_ci#include "../common/sst-dsp.h" 178c2ecf20Sopenharmony_ci#include "../common/sst-dsp-priv.h" 188c2ecf20Sopenharmony_ci#include "../common/sst-ipc.h" 198c2ecf20Sopenharmony_ci#include "skl.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define SKL_BASEFW_TIMEOUT 300 228c2ecf20Sopenharmony_ci#define SKL_INIT_TIMEOUT 1000 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* Intel HD Audio SRAM Window 0*/ 258c2ecf20Sopenharmony_ci#define SKL_ADSP_SRAM0_BASE 0x8000 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* Firmware status window */ 288c2ecf20Sopenharmony_ci#define SKL_ADSP_FW_STATUS SKL_ADSP_SRAM0_BASE 298c2ecf20Sopenharmony_ci#define SKL_ADSP_ERROR_CODE (SKL_ADSP_FW_STATUS + 0x4) 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define SKL_NUM_MODULES 1 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic bool skl_check_fw_status(struct sst_dsp *ctx, u32 status) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci u32 cur_sts; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci cur_sts = sst_dsp_shim_read(ctx, SKL_ADSP_FW_STATUS) & SKL_FW_STS_MASK; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci return (cur_sts == status); 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic int skl_transfer_firmware(struct sst_dsp *ctx, 438c2ecf20Sopenharmony_ci const void *basefw, u32 base_fw_size) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci int ret = 0; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci ret = ctx->cl_dev.ops.cl_copy_to_dmabuf(ctx, basefw, base_fw_size, 488c2ecf20Sopenharmony_ci true); 498c2ecf20Sopenharmony_ci if (ret < 0) 508c2ecf20Sopenharmony_ci return ret; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci ret = sst_dsp_register_poll(ctx, 538c2ecf20Sopenharmony_ci SKL_ADSP_FW_STATUS, 548c2ecf20Sopenharmony_ci SKL_FW_STS_MASK, 558c2ecf20Sopenharmony_ci SKL_FW_RFW_START, 568c2ecf20Sopenharmony_ci SKL_BASEFW_TIMEOUT, 578c2ecf20Sopenharmony_ci "Firmware boot"); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci ctx->cl_dev.ops.cl_stop_dma(ctx); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci return ret; 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define SKL_ADSP_FW_BIN_HDR_OFFSET 0x284 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic int skl_load_base_firmware(struct sst_dsp *ctx) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci int ret = 0, i; 698c2ecf20Sopenharmony_ci struct skl_dev *skl = ctx->thread_context; 708c2ecf20Sopenharmony_ci struct firmware stripped_fw; 718c2ecf20Sopenharmony_ci u32 reg; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci skl->boot_complete = false; 748c2ecf20Sopenharmony_ci init_waitqueue_head(&skl->boot_wait); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if (ctx->fw == NULL) { 778c2ecf20Sopenharmony_ci ret = request_firmware(&ctx->fw, ctx->fw_name, ctx->dev); 788c2ecf20Sopenharmony_ci if (ret < 0) { 798c2ecf20Sopenharmony_ci dev_err(ctx->dev, "Request firmware failed %d\n", ret); 808c2ecf20Sopenharmony_ci return -EIO; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* prase uuids on first boot */ 858c2ecf20Sopenharmony_ci if (skl->is_first_boot) { 868c2ecf20Sopenharmony_ci ret = snd_skl_parse_uuids(ctx, ctx->fw, SKL_ADSP_FW_BIN_HDR_OFFSET, 0); 878c2ecf20Sopenharmony_ci if (ret < 0) { 888c2ecf20Sopenharmony_ci dev_err(ctx->dev, "UUID parsing err: %d\n", ret); 898c2ecf20Sopenharmony_ci release_firmware(ctx->fw); 908c2ecf20Sopenharmony_ci skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); 918c2ecf20Sopenharmony_ci return ret; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* check for extended manifest */ 968c2ecf20Sopenharmony_ci stripped_fw.data = ctx->fw->data; 978c2ecf20Sopenharmony_ci stripped_fw.size = ctx->fw->size; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci skl_dsp_strip_extended_manifest(&stripped_fw); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci ret = skl_dsp_boot(ctx); 1028c2ecf20Sopenharmony_ci if (ret < 0) { 1038c2ecf20Sopenharmony_ci dev_err(ctx->dev, "Boot dsp core failed ret: %d\n", ret); 1048c2ecf20Sopenharmony_ci goto skl_load_base_firmware_failed; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci ret = skl_cldma_prepare(ctx); 1088c2ecf20Sopenharmony_ci if (ret < 0) { 1098c2ecf20Sopenharmony_ci dev_err(ctx->dev, "CL dma prepare failed : %d\n", ret); 1108c2ecf20Sopenharmony_ci goto skl_load_base_firmware_failed; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* enable Interrupt */ 1148c2ecf20Sopenharmony_ci skl_ipc_int_enable(ctx); 1158c2ecf20Sopenharmony_ci skl_ipc_op_int_enable(ctx); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci /* check ROM Status */ 1188c2ecf20Sopenharmony_ci for (i = SKL_INIT_TIMEOUT; i > 0; --i) { 1198c2ecf20Sopenharmony_ci if (skl_check_fw_status(ctx, SKL_FW_INIT)) { 1208c2ecf20Sopenharmony_ci dev_dbg(ctx->dev, 1218c2ecf20Sopenharmony_ci "ROM loaded, we can continue with FW loading\n"); 1228c2ecf20Sopenharmony_ci break; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci mdelay(1); 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci if (!i) { 1278c2ecf20Sopenharmony_ci reg = sst_dsp_shim_read(ctx, SKL_ADSP_FW_STATUS); 1288c2ecf20Sopenharmony_ci dev_err(ctx->dev, 1298c2ecf20Sopenharmony_ci "Timeout waiting for ROM init done, reg:0x%x\n", reg); 1308c2ecf20Sopenharmony_ci ret = -EIO; 1318c2ecf20Sopenharmony_ci goto transfer_firmware_failed; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci ret = skl_transfer_firmware(ctx, stripped_fw.data, stripped_fw.size); 1358c2ecf20Sopenharmony_ci if (ret < 0) { 1368c2ecf20Sopenharmony_ci dev_err(ctx->dev, "Transfer firmware failed%d\n", ret); 1378c2ecf20Sopenharmony_ci goto transfer_firmware_failed; 1388c2ecf20Sopenharmony_ci } else { 1398c2ecf20Sopenharmony_ci ret = wait_event_timeout(skl->boot_wait, skl->boot_complete, 1408c2ecf20Sopenharmony_ci msecs_to_jiffies(SKL_IPC_BOOT_MSECS)); 1418c2ecf20Sopenharmony_ci if (ret == 0) { 1428c2ecf20Sopenharmony_ci dev_err(ctx->dev, "DSP boot failed, FW Ready timed-out\n"); 1438c2ecf20Sopenharmony_ci ret = -EIO; 1448c2ecf20Sopenharmony_ci goto transfer_firmware_failed; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci dev_dbg(ctx->dev, "Download firmware successful%d\n", ret); 1488c2ecf20Sopenharmony_ci skl->fw_loaded = true; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci return 0; 1518c2ecf20Sopenharmony_citransfer_firmware_failed: 1528c2ecf20Sopenharmony_ci ctx->cl_dev.ops.cl_cleanup_controller(ctx); 1538c2ecf20Sopenharmony_ciskl_load_base_firmware_failed: 1548c2ecf20Sopenharmony_ci skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); 1558c2ecf20Sopenharmony_ci release_firmware(ctx->fw); 1568c2ecf20Sopenharmony_ci ctx->fw = NULL; 1578c2ecf20Sopenharmony_ci return ret; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic int skl_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci int ret; 1638c2ecf20Sopenharmony_ci struct skl_ipc_dxstate_info dx; 1648c2ecf20Sopenharmony_ci struct skl_dev *skl = ctx->thread_context; 1658c2ecf20Sopenharmony_ci unsigned int core_mask = SKL_DSP_CORE_MASK(core_id); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* If core0 is being turned on, we need to load the FW */ 1688c2ecf20Sopenharmony_ci if (core_id == SKL_DSP_CORE0_ID) { 1698c2ecf20Sopenharmony_ci ret = skl_load_base_firmware(ctx); 1708c2ecf20Sopenharmony_ci if (ret < 0) { 1718c2ecf20Sopenharmony_ci dev_err(ctx->dev, "unable to load firmware\n"); 1728c2ecf20Sopenharmony_ci return ret; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* load libs as they are also lost on D3 */ 1768c2ecf20Sopenharmony_ci if (skl->lib_count > 1) { 1778c2ecf20Sopenharmony_ci ret = ctx->fw_ops.load_library(ctx, skl->lib_info, 1788c2ecf20Sopenharmony_ci skl->lib_count); 1798c2ecf20Sopenharmony_ci if (ret < 0) { 1808c2ecf20Sopenharmony_ci dev_err(ctx->dev, "reload libs failed: %d\n", 1818c2ecf20Sopenharmony_ci ret); 1828c2ecf20Sopenharmony_ci return ret; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* 1898c2ecf20Sopenharmony_ci * If any core other than core 0 is being moved to D0, enable the 1908c2ecf20Sopenharmony_ci * core and send the set dx IPC for the core. 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_ci if (core_id != SKL_DSP_CORE0_ID) { 1938c2ecf20Sopenharmony_ci ret = skl_dsp_enable_core(ctx, core_mask); 1948c2ecf20Sopenharmony_ci if (ret < 0) 1958c2ecf20Sopenharmony_ci return ret; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci dx.core_mask = core_mask; 1988c2ecf20Sopenharmony_ci dx.dx_mask = core_mask; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID, 2018c2ecf20Sopenharmony_ci SKL_BASE_FW_MODULE_ID, &dx); 2028c2ecf20Sopenharmony_ci if (ret < 0) { 2038c2ecf20Sopenharmony_ci dev_err(ctx->dev, "Failed to set dsp to D0:core id= %d\n", 2048c2ecf20Sopenharmony_ci core_id); 2058c2ecf20Sopenharmony_ci skl_dsp_disable_core(ctx, core_mask); 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci skl->cores.state[core_id] = SKL_DSP_RUNNING; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci return 0; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic int skl_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci int ret; 2178c2ecf20Sopenharmony_ci struct skl_ipc_dxstate_info dx; 2188c2ecf20Sopenharmony_ci struct skl_dev *skl = ctx->thread_context; 2198c2ecf20Sopenharmony_ci unsigned int core_mask = SKL_DSP_CORE_MASK(core_id); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci dx.core_mask = core_mask; 2228c2ecf20Sopenharmony_ci dx.dx_mask = SKL_IPC_D3_MASK; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID, SKL_BASE_FW_MODULE_ID, &dx); 2258c2ecf20Sopenharmony_ci if (ret < 0) 2268c2ecf20Sopenharmony_ci dev_err(ctx->dev, "set Dx core %d fail: %d\n", core_id, ret); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (core_id == SKL_DSP_CORE0_ID) { 2298c2ecf20Sopenharmony_ci /* disable Interrupt */ 2308c2ecf20Sopenharmony_ci ctx->cl_dev.ops.cl_cleanup_controller(ctx); 2318c2ecf20Sopenharmony_ci skl_cldma_int_disable(ctx); 2328c2ecf20Sopenharmony_ci skl_ipc_op_int_disable(ctx); 2338c2ecf20Sopenharmony_ci skl_ipc_int_disable(ctx); 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci ret = skl_dsp_disable_core(ctx, core_mask); 2378c2ecf20Sopenharmony_ci if (ret < 0) 2388c2ecf20Sopenharmony_ci return ret; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci skl->cores.state[core_id] = SKL_DSP_RESET; 2418c2ecf20Sopenharmony_ci return ret; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic unsigned int skl_get_errorcode(struct sst_dsp *ctx) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci return sst_dsp_shim_read(ctx, SKL_ADSP_ERROR_CODE); 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci/* 2508c2ecf20Sopenharmony_ci * since get/set_module are called from DAPM context, 2518c2ecf20Sopenharmony_ci * we don't need lock for usage count 2528c2ecf20Sopenharmony_ci */ 2538c2ecf20Sopenharmony_cistatic int skl_get_module(struct sst_dsp *ctx, u16 mod_id) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci struct skl_module_table *module; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci list_for_each_entry(module, &ctx->module_list, list) { 2588c2ecf20Sopenharmony_ci if (module->mod_info->mod_id == mod_id) 2598c2ecf20Sopenharmony_ci return ++module->usage_cnt; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci return -EINVAL; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic int skl_put_module(struct sst_dsp *ctx, u16 mod_id) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci struct skl_module_table *module; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci list_for_each_entry(module, &ctx->module_list, list) { 2708c2ecf20Sopenharmony_ci if (module->mod_info->mod_id == mod_id) 2718c2ecf20Sopenharmony_ci return --module->usage_cnt; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci return -EINVAL; 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic struct skl_module_table *skl_fill_module_table(struct sst_dsp *ctx, 2788c2ecf20Sopenharmony_ci char *mod_name, int mod_id) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci const struct firmware *fw; 2818c2ecf20Sopenharmony_ci struct skl_module_table *skl_module; 2828c2ecf20Sopenharmony_ci unsigned int size; 2838c2ecf20Sopenharmony_ci int ret; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci ret = request_firmware(&fw, mod_name, ctx->dev); 2868c2ecf20Sopenharmony_ci if (ret < 0) { 2878c2ecf20Sopenharmony_ci dev_err(ctx->dev, "Request Module %s failed :%d\n", 2888c2ecf20Sopenharmony_ci mod_name, ret); 2898c2ecf20Sopenharmony_ci return NULL; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci skl_module = devm_kzalloc(ctx->dev, sizeof(*skl_module), GFP_KERNEL); 2938c2ecf20Sopenharmony_ci if (skl_module == NULL) { 2948c2ecf20Sopenharmony_ci release_firmware(fw); 2958c2ecf20Sopenharmony_ci return NULL; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci size = sizeof(*skl_module->mod_info); 2998c2ecf20Sopenharmony_ci skl_module->mod_info = devm_kzalloc(ctx->dev, size, GFP_KERNEL); 3008c2ecf20Sopenharmony_ci if (skl_module->mod_info == NULL) { 3018c2ecf20Sopenharmony_ci release_firmware(fw); 3028c2ecf20Sopenharmony_ci return NULL; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci skl_module->mod_info->mod_id = mod_id; 3068c2ecf20Sopenharmony_ci skl_module->mod_info->fw = fw; 3078c2ecf20Sopenharmony_ci list_add(&skl_module->list, &ctx->module_list); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci return skl_module; 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci/* get a module from it's unique ID */ 3138c2ecf20Sopenharmony_cistatic struct skl_module_table *skl_module_get_from_id( 3148c2ecf20Sopenharmony_ci struct sst_dsp *ctx, u16 mod_id) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci struct skl_module_table *module; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (list_empty(&ctx->module_list)) { 3198c2ecf20Sopenharmony_ci dev_err(ctx->dev, "Module list is empty\n"); 3208c2ecf20Sopenharmony_ci return NULL; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci list_for_each_entry(module, &ctx->module_list, list) { 3248c2ecf20Sopenharmony_ci if (module->mod_info->mod_id == mod_id) 3258c2ecf20Sopenharmony_ci return module; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci return NULL; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic int skl_transfer_module(struct sst_dsp *ctx, const void *data, 3328c2ecf20Sopenharmony_ci u32 size, u16 mod_id, u8 table_id, bool is_module) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci int ret, bytes_left, curr_pos; 3358c2ecf20Sopenharmony_ci struct skl_dev *skl = ctx->thread_context; 3368c2ecf20Sopenharmony_ci skl->mod_load_complete = false; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci bytes_left = ctx->cl_dev.ops.cl_copy_to_dmabuf(ctx, data, size, false); 3398c2ecf20Sopenharmony_ci if (bytes_left < 0) 3408c2ecf20Sopenharmony_ci return bytes_left; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* check is_module flag to load module or library */ 3438c2ecf20Sopenharmony_ci if (is_module) 3448c2ecf20Sopenharmony_ci ret = skl_ipc_load_modules(&skl->ipc, SKL_NUM_MODULES, &mod_id); 3458c2ecf20Sopenharmony_ci else 3468c2ecf20Sopenharmony_ci ret = skl_sst_ipc_load_library(&skl->ipc, 0, table_id, false); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (ret < 0) { 3498c2ecf20Sopenharmony_ci dev_err(ctx->dev, "Failed to Load %s with err %d\n", 3508c2ecf20Sopenharmony_ci is_module ? "module" : "lib", ret); 3518c2ecf20Sopenharmony_ci goto out; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci /* 3558c2ecf20Sopenharmony_ci * if bytes_left > 0 then wait for BDL complete interrupt and 3568c2ecf20Sopenharmony_ci * copy the next chunk till bytes_left is 0. if bytes_left is 3578c2ecf20Sopenharmony_ci * zero, then wait for load module IPC reply 3588c2ecf20Sopenharmony_ci */ 3598c2ecf20Sopenharmony_ci while (bytes_left > 0) { 3608c2ecf20Sopenharmony_ci curr_pos = size - bytes_left; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci ret = skl_cldma_wait_interruptible(ctx); 3638c2ecf20Sopenharmony_ci if (ret < 0) 3648c2ecf20Sopenharmony_ci goto out; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci bytes_left = ctx->cl_dev.ops.cl_copy_to_dmabuf(ctx, 3678c2ecf20Sopenharmony_ci data + curr_pos, 3688c2ecf20Sopenharmony_ci bytes_left, false); 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci ret = wait_event_timeout(skl->mod_load_wait, skl->mod_load_complete, 3728c2ecf20Sopenharmony_ci msecs_to_jiffies(SKL_IPC_BOOT_MSECS)); 3738c2ecf20Sopenharmony_ci if (ret == 0 || !skl->mod_load_status) { 3748c2ecf20Sopenharmony_ci dev_err(ctx->dev, "Module Load failed\n"); 3758c2ecf20Sopenharmony_ci ret = -EIO; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ciout: 3798c2ecf20Sopenharmony_ci ctx->cl_dev.ops.cl_stop_dma(ctx); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci return ret; 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic int 3858c2ecf20Sopenharmony_ciskl_load_library(struct sst_dsp *ctx, struct skl_lib_info *linfo, int lib_count) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci struct skl_dev *skl = ctx->thread_context; 3888c2ecf20Sopenharmony_ci struct firmware stripped_fw; 3898c2ecf20Sopenharmony_ci int ret, i; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci /* library indices start from 1 to N. 0 represents base FW */ 3928c2ecf20Sopenharmony_ci for (i = 1; i < lib_count; i++) { 3938c2ecf20Sopenharmony_ci ret = skl_prepare_lib_load(skl, &skl->lib_info[i], &stripped_fw, 3948c2ecf20Sopenharmony_ci SKL_ADSP_FW_BIN_HDR_OFFSET, i); 3958c2ecf20Sopenharmony_ci if (ret < 0) 3968c2ecf20Sopenharmony_ci goto load_library_failed; 3978c2ecf20Sopenharmony_ci ret = skl_transfer_module(ctx, stripped_fw.data, 3988c2ecf20Sopenharmony_ci stripped_fw.size, 0, i, false); 3998c2ecf20Sopenharmony_ci if (ret < 0) 4008c2ecf20Sopenharmony_ci goto load_library_failed; 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci return 0; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ciload_library_failed: 4068c2ecf20Sopenharmony_ci skl_release_library(linfo, lib_count); 4078c2ecf20Sopenharmony_ci return ret; 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic int skl_load_module(struct sst_dsp *ctx, u16 mod_id, u8 *guid) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci struct skl_module_table *module_entry = NULL; 4138c2ecf20Sopenharmony_ci int ret = 0; 4148c2ecf20Sopenharmony_ci char mod_name[64]; /* guid str = 32 chars + 4 hyphens */ 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci snprintf(mod_name, sizeof(mod_name), "intel/dsp_fw_%pUL.bin", guid); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci module_entry = skl_module_get_from_id(ctx, mod_id); 4198c2ecf20Sopenharmony_ci if (module_entry == NULL) { 4208c2ecf20Sopenharmony_ci module_entry = skl_fill_module_table(ctx, mod_name, mod_id); 4218c2ecf20Sopenharmony_ci if (module_entry == NULL) { 4228c2ecf20Sopenharmony_ci dev_err(ctx->dev, "Failed to Load module\n"); 4238c2ecf20Sopenharmony_ci return -EINVAL; 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (!module_entry->usage_cnt) { 4288c2ecf20Sopenharmony_ci ret = skl_transfer_module(ctx, module_entry->mod_info->fw->data, 4298c2ecf20Sopenharmony_ci module_entry->mod_info->fw->size, 4308c2ecf20Sopenharmony_ci mod_id, 0, true); 4318c2ecf20Sopenharmony_ci if (ret < 0) { 4328c2ecf20Sopenharmony_ci dev_err(ctx->dev, "Failed to Load module\n"); 4338c2ecf20Sopenharmony_ci return ret; 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci ret = skl_get_module(ctx, mod_id); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci return ret; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic int skl_unload_module(struct sst_dsp *ctx, u16 mod_id) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci int usage_cnt; 4458c2ecf20Sopenharmony_ci struct skl_dev *skl = ctx->thread_context; 4468c2ecf20Sopenharmony_ci int ret = 0; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci usage_cnt = skl_put_module(ctx, mod_id); 4498c2ecf20Sopenharmony_ci if (usage_cnt < 0) { 4508c2ecf20Sopenharmony_ci dev_err(ctx->dev, "Module bad usage cnt!:%d\n", usage_cnt); 4518c2ecf20Sopenharmony_ci return -EIO; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci /* if module is used by others return, no need to unload */ 4558c2ecf20Sopenharmony_ci if (usage_cnt > 0) 4568c2ecf20Sopenharmony_ci return 0; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci ret = skl_ipc_unload_modules(&skl->ipc, 4598c2ecf20Sopenharmony_ci SKL_NUM_MODULES, &mod_id); 4608c2ecf20Sopenharmony_ci if (ret < 0) { 4618c2ecf20Sopenharmony_ci dev_err(ctx->dev, "Failed to UnLoad module\n"); 4628c2ecf20Sopenharmony_ci skl_get_module(ctx, mod_id); 4638c2ecf20Sopenharmony_ci return ret; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci return ret; 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_civoid skl_clear_module_cnt(struct sst_dsp *ctx) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci struct skl_module_table *module; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci if (list_empty(&ctx->module_list)) 4748c2ecf20Sopenharmony_ci return; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci list_for_each_entry(module, &ctx->module_list, list) { 4778c2ecf20Sopenharmony_ci module->usage_cnt = 0; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(skl_clear_module_cnt); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic void skl_clear_module_table(struct sst_dsp *ctx) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci struct skl_module_table *module, *tmp; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci if (list_empty(&ctx->module_list)) 4878c2ecf20Sopenharmony_ci return; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci list_for_each_entry_safe(module, tmp, &ctx->module_list, list) { 4908c2ecf20Sopenharmony_ci list_del(&module->list); 4918c2ecf20Sopenharmony_ci release_firmware(module->mod_info->fw); 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cistatic const struct skl_dsp_fw_ops skl_fw_ops = { 4968c2ecf20Sopenharmony_ci .set_state_D0 = skl_set_dsp_D0, 4978c2ecf20Sopenharmony_ci .set_state_D3 = skl_set_dsp_D3, 4988c2ecf20Sopenharmony_ci .load_fw = skl_load_base_firmware, 4998c2ecf20Sopenharmony_ci .get_fw_errcode = skl_get_errorcode, 5008c2ecf20Sopenharmony_ci .load_library = skl_load_library, 5018c2ecf20Sopenharmony_ci .load_mod = skl_load_module, 5028c2ecf20Sopenharmony_ci .unload_mod = skl_unload_module, 5038c2ecf20Sopenharmony_ci}; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic struct sst_ops skl_ops = { 5068c2ecf20Sopenharmony_ci .irq_handler = skl_dsp_sst_interrupt, 5078c2ecf20Sopenharmony_ci .write = sst_shim32_write, 5088c2ecf20Sopenharmony_ci .read = sst_shim32_read, 5098c2ecf20Sopenharmony_ci .free = skl_dsp_free, 5108c2ecf20Sopenharmony_ci}; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_cistatic struct sst_dsp_device skl_dev = { 5138c2ecf20Sopenharmony_ci .thread = skl_dsp_irq_thread_handler, 5148c2ecf20Sopenharmony_ci .ops = &skl_ops, 5158c2ecf20Sopenharmony_ci}; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ciint skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, 5188c2ecf20Sopenharmony_ci const char *fw_name, struct skl_dsp_loader_ops dsp_ops, 5198c2ecf20Sopenharmony_ci struct skl_dev **dsp) 5208c2ecf20Sopenharmony_ci{ 5218c2ecf20Sopenharmony_ci struct skl_dev *skl; 5228c2ecf20Sopenharmony_ci struct sst_dsp *sst; 5238c2ecf20Sopenharmony_ci int ret; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci ret = skl_sst_ctx_init(dev, irq, fw_name, dsp_ops, dsp, &skl_dev); 5268c2ecf20Sopenharmony_ci if (ret < 0) { 5278c2ecf20Sopenharmony_ci dev_err(dev, "%s: no device\n", __func__); 5288c2ecf20Sopenharmony_ci return ret; 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci skl = *dsp; 5328c2ecf20Sopenharmony_ci sst = skl->dsp; 5338c2ecf20Sopenharmony_ci sst->addr.lpe = mmio_base; 5348c2ecf20Sopenharmony_ci sst->addr.shim = mmio_base; 5358c2ecf20Sopenharmony_ci sst->addr.sram0_base = SKL_ADSP_SRAM0_BASE; 5368c2ecf20Sopenharmony_ci sst->addr.sram1_base = SKL_ADSP_SRAM1_BASE; 5378c2ecf20Sopenharmony_ci sst->addr.w0_stat_sz = SKL_ADSP_W0_STAT_SZ; 5388c2ecf20Sopenharmony_ci sst->addr.w0_up_sz = SKL_ADSP_W0_UP_SZ; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci sst_dsp_mailbox_init(sst, (SKL_ADSP_SRAM0_BASE + SKL_ADSP_W0_STAT_SZ), 5418c2ecf20Sopenharmony_ci SKL_ADSP_W0_UP_SZ, SKL_ADSP_SRAM1_BASE, SKL_ADSP_W1_SZ); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci ret = skl_ipc_init(dev, skl); 5448c2ecf20Sopenharmony_ci if (ret) { 5458c2ecf20Sopenharmony_ci skl_dsp_free(sst); 5468c2ecf20Sopenharmony_ci return ret; 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci sst->fw_ops = skl_fw_ops; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci return skl_dsp_acquire_irq(sst); 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(skl_sst_dsp_init); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ciint skl_sst_init_fw(struct device *dev, struct skl_dev *skl) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci int ret; 5588c2ecf20Sopenharmony_ci struct sst_dsp *sst = skl->dsp; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci ret = sst->fw_ops.load_fw(sst); 5618c2ecf20Sopenharmony_ci if (ret < 0) { 5628c2ecf20Sopenharmony_ci dev_err(dev, "Load base fw failed : %d\n", ret); 5638c2ecf20Sopenharmony_ci return ret; 5648c2ecf20Sopenharmony_ci } 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci skl_dsp_init_core_state(sst); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci if (skl->lib_count > 1) { 5698c2ecf20Sopenharmony_ci ret = sst->fw_ops.load_library(sst, skl->lib_info, 5708c2ecf20Sopenharmony_ci skl->lib_count); 5718c2ecf20Sopenharmony_ci if (ret < 0) { 5728c2ecf20Sopenharmony_ci dev_err(dev, "Load Library failed : %x\n", ret); 5738c2ecf20Sopenharmony_ci return ret; 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci skl->is_first_boot = false; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci return 0; 5798c2ecf20Sopenharmony_ci} 5808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(skl_sst_init_fw); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_civoid skl_sst_dsp_cleanup(struct device *dev, struct skl_dev *skl) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci if (skl->dsp->fw) 5868c2ecf20Sopenharmony_ci release_firmware(skl->dsp->fw); 5878c2ecf20Sopenharmony_ci skl_clear_module_table(skl->dsp); 5888c2ecf20Sopenharmony_ci skl_freeup_uuid_list(skl); 5898c2ecf20Sopenharmony_ci skl_ipc_free(&skl->ipc); 5908c2ecf20Sopenharmony_ci skl->dsp->ops->free(skl->dsp); 5918c2ecf20Sopenharmony_ci if (skl->boot_complete) { 5928c2ecf20Sopenharmony_ci skl->dsp->cl_dev.ops.cl_cleanup_controller(skl->dsp); 5938c2ecf20Sopenharmony_ci skl_cldma_int_disable(skl->dsp); 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci} 5968c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(skl_sst_dsp_cleanup); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 5998c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Intel Skylake IPC driver"); 600