162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Authors: Cezary Rojewski <cezary.rojewski@intel.com> 662306a36Sopenharmony_ci// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> 762306a36Sopenharmony_ci// 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/firmware.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <sound/hdaudio.h> 1362306a36Sopenharmony_ci#include <sound/hdaudio_ext.h> 1462306a36Sopenharmony_ci#include "avs.h" 1562306a36Sopenharmony_ci#include "cldma.h" 1662306a36Sopenharmony_ci#include "messages.h" 1762306a36Sopenharmony_ci#include "registers.h" 1862306a36Sopenharmony_ci#include "topology.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define AVS_ROM_STS_MASK 0xFF 2162306a36Sopenharmony_ci#define AVS_ROM_INIT_DONE 0x1 2262306a36Sopenharmony_ci#define SKL_ROM_BASEFW_ENTERED 0xF 2362306a36Sopenharmony_ci#define APL_ROM_FW_ENTERED 0x5 2462306a36Sopenharmony_ci#define AVS_ROM_INIT_POLLING_US 5 2562306a36Sopenharmony_ci#define SKL_ROM_INIT_TIMEOUT_US 1000000 2662306a36Sopenharmony_ci#define APL_ROM_INIT_TIMEOUT_US 300000 2762306a36Sopenharmony_ci#define APL_ROM_INIT_RETRIES 3 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define AVS_FW_INIT_POLLING_US 500 3062306a36Sopenharmony_ci#define AVS_FW_INIT_TIMEOUT_MS 3000 3162306a36Sopenharmony_ci#define AVS_FW_INIT_TIMEOUT_US (AVS_FW_INIT_TIMEOUT_MS * 1000) 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define AVS_CLDMA_START_DELAY_MS 100 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define AVS_ROOT_DIR "intel/avs" 3662306a36Sopenharmony_ci#define AVS_BASEFW_FILENAME "dsp_basefw.bin" 3762306a36Sopenharmony_ci#define AVS_EXT_MANIFEST_MAGIC 0x31454124 3862306a36Sopenharmony_ci#define SKL_MANIFEST_MAGIC 0x00000006 3962306a36Sopenharmony_ci#define SKL_ADSPFW_OFFSET 0x284 4062306a36Sopenharmony_ci#define APL_MANIFEST_MAGIC 0x44504324 4162306a36Sopenharmony_ci#define APL_ADSPFW_OFFSET 0x2000 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* Occasionally, engineering (release candidate) firmware is provided for testing. */ 4462306a36Sopenharmony_cistatic bool debug_ignore_fw_version; 4562306a36Sopenharmony_cimodule_param_named(ignore_fw_version, debug_ignore_fw_version, bool, 0444); 4662306a36Sopenharmony_ciMODULE_PARM_DESC(ignore_fw_version, "Ignore firmware version check 0=no (default), 1=yes"); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define AVS_LIB_NAME_SIZE 8 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistruct avs_fw_manifest { 5162306a36Sopenharmony_ci u32 id; 5262306a36Sopenharmony_ci u32 len; 5362306a36Sopenharmony_ci char name[AVS_LIB_NAME_SIZE]; 5462306a36Sopenharmony_ci u32 preload_page_count; 5562306a36Sopenharmony_ci u32 img_flags; 5662306a36Sopenharmony_ci u32 feature_mask; 5762306a36Sopenharmony_ci struct avs_fw_version version; 5862306a36Sopenharmony_ci} __packed; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistruct avs_fw_ext_manifest { 6162306a36Sopenharmony_ci u32 id; 6262306a36Sopenharmony_ci u32 len; 6362306a36Sopenharmony_ci u16 version_major; 6462306a36Sopenharmony_ci u16 version_minor; 6562306a36Sopenharmony_ci u32 entries; 6662306a36Sopenharmony_ci} __packed; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic int avs_fw_ext_manifest_strip(struct firmware *fw) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci struct avs_fw_ext_manifest *man; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (fw->size < sizeof(*man)) 7362306a36Sopenharmony_ci return -EINVAL; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci man = (struct avs_fw_ext_manifest *)fw->data; 7662306a36Sopenharmony_ci if (man->id == AVS_EXT_MANIFEST_MAGIC) { 7762306a36Sopenharmony_ci fw->data += man->len; 7862306a36Sopenharmony_ci fw->size -= man->len; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci return 0; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic int avs_fw_manifest_offset(struct firmware *fw) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci /* Header type found in first DWORD of fw binary. */ 8762306a36Sopenharmony_ci u32 magic = *(u32 *)fw->data; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci switch (magic) { 9062306a36Sopenharmony_ci case SKL_MANIFEST_MAGIC: 9162306a36Sopenharmony_ci return SKL_ADSPFW_OFFSET; 9262306a36Sopenharmony_ci case APL_MANIFEST_MAGIC: 9362306a36Sopenharmony_ci return APL_ADSPFW_OFFSET; 9462306a36Sopenharmony_ci default: 9562306a36Sopenharmony_ci return -EINVAL; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic int avs_fw_manifest_strip_verify(struct avs_dev *adev, struct firmware *fw, 10062306a36Sopenharmony_ci const struct avs_fw_version *min) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct avs_fw_manifest *man; 10362306a36Sopenharmony_ci int offset, ret; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci ret = avs_fw_ext_manifest_strip(fw); 10662306a36Sopenharmony_ci if (ret) 10762306a36Sopenharmony_ci return ret; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci offset = avs_fw_manifest_offset(fw); 11062306a36Sopenharmony_ci if (offset < 0) 11162306a36Sopenharmony_ci return offset; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (fw->size < offset + sizeof(*man)) 11462306a36Sopenharmony_ci return -EINVAL; 11562306a36Sopenharmony_ci if (!min) 11662306a36Sopenharmony_ci return 0; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci man = (struct avs_fw_manifest *)(fw->data + offset); 11962306a36Sopenharmony_ci if (man->version.major != min->major || 12062306a36Sopenharmony_ci man->version.minor != min->minor || 12162306a36Sopenharmony_ci man->version.hotfix != min->hotfix || 12262306a36Sopenharmony_ci man->version.build < min->build) { 12362306a36Sopenharmony_ci dev_warn(adev->dev, "bad FW version %d.%d.%d.%d, expected %d.%d.%d.%d or newer\n", 12462306a36Sopenharmony_ci man->version.major, man->version.minor, 12562306a36Sopenharmony_ci man->version.hotfix, man->version.build, 12662306a36Sopenharmony_ci min->major, min->minor, min->hotfix, min->build); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (!debug_ignore_fw_version) 12962306a36Sopenharmony_ci return -EINVAL; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ciint avs_cldma_load_basefw(struct avs_dev *adev, struct firmware *fw) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci struct hda_cldma *cl = &code_loader; 13862306a36Sopenharmony_ci unsigned int reg; 13962306a36Sopenharmony_ci int ret; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci ret = avs_dsp_op(adev, power, AVS_MAIN_CORE_MASK, true); 14262306a36Sopenharmony_ci if (ret < 0) 14362306a36Sopenharmony_ci return ret; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci ret = avs_dsp_op(adev, reset, AVS_MAIN_CORE_MASK, false); 14662306a36Sopenharmony_ci if (ret < 0) 14762306a36Sopenharmony_ci return ret; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci ret = hda_cldma_reset(cl); 15062306a36Sopenharmony_ci if (ret < 0) { 15162306a36Sopenharmony_ci dev_err(adev->dev, "cldma reset failed: %d\n", ret); 15262306a36Sopenharmony_ci return ret; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci hda_cldma_setup(cl); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci ret = avs_dsp_op(adev, stall, AVS_MAIN_CORE_MASK, false); 15762306a36Sopenharmony_ci if (ret < 0) 15862306a36Sopenharmony_ci return ret; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci reinit_completion(&adev->fw_ready); 16162306a36Sopenharmony_ci avs_dsp_op(adev, int_control, true); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* await ROM init */ 16462306a36Sopenharmony_ci ret = snd_hdac_adsp_readl_poll(adev, AVS_FW_REG_STATUS(adev), reg, 16562306a36Sopenharmony_ci (reg & AVS_ROM_INIT_DONE) == AVS_ROM_INIT_DONE, 16662306a36Sopenharmony_ci AVS_ROM_INIT_POLLING_US, SKL_ROM_INIT_TIMEOUT_US); 16762306a36Sopenharmony_ci if (ret < 0) { 16862306a36Sopenharmony_ci dev_err(adev->dev, "rom init timeout: %d\n", ret); 16962306a36Sopenharmony_ci avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK); 17062306a36Sopenharmony_ci return ret; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci hda_cldma_set_data(cl, (void *)fw->data, fw->size); 17462306a36Sopenharmony_ci /* transfer firmware */ 17562306a36Sopenharmony_ci hda_cldma_transfer(cl, 0); 17662306a36Sopenharmony_ci ret = snd_hdac_adsp_readl_poll(adev, AVS_FW_REG_STATUS(adev), reg, 17762306a36Sopenharmony_ci (reg & AVS_ROM_STS_MASK) == SKL_ROM_BASEFW_ENTERED, 17862306a36Sopenharmony_ci AVS_FW_INIT_POLLING_US, AVS_FW_INIT_TIMEOUT_US); 17962306a36Sopenharmony_ci hda_cldma_stop(cl); 18062306a36Sopenharmony_ci if (ret < 0) { 18162306a36Sopenharmony_ci dev_err(adev->dev, "transfer fw failed: %d\n", ret); 18262306a36Sopenharmony_ci avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK); 18362306a36Sopenharmony_ci return ret; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci return 0; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ciint avs_cldma_load_library(struct avs_dev *adev, struct firmware *lib, u32 id) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci struct hda_cldma *cl = &code_loader; 19262306a36Sopenharmony_ci int ret; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci hda_cldma_set_data(cl, (void *)lib->data, lib->size); 19562306a36Sopenharmony_ci /* transfer modules manifest */ 19662306a36Sopenharmony_ci hda_cldma_transfer(cl, msecs_to_jiffies(AVS_CLDMA_START_DELAY_MS)); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* DMA id ignored as there is only ever one code-loader DMA */ 19962306a36Sopenharmony_ci ret = avs_ipc_load_library(adev, 0, id); 20062306a36Sopenharmony_ci hda_cldma_stop(cl); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (ret) { 20362306a36Sopenharmony_ci ret = AVS_IPC_RET(ret); 20462306a36Sopenharmony_ci dev_err(adev->dev, "transfer lib %d failed: %d\n", id, ret); 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci return ret; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic int avs_cldma_load_module(struct avs_dev *adev, struct avs_module_entry *mentry) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci struct hda_cldma *cl = &code_loader; 21362306a36Sopenharmony_ci const struct firmware *mod; 21462306a36Sopenharmony_ci char *mod_name; 21562306a36Sopenharmony_ci int ret; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci mod_name = kasprintf(GFP_KERNEL, "%s/%s/dsp_mod_%pUL.bin", AVS_ROOT_DIR, 21862306a36Sopenharmony_ci adev->spec->name, mentry->uuid.b); 21962306a36Sopenharmony_ci if (!mod_name) 22062306a36Sopenharmony_ci return -ENOMEM; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci ret = avs_request_firmware(adev, &mod, mod_name); 22362306a36Sopenharmony_ci kfree(mod_name); 22462306a36Sopenharmony_ci if (ret < 0) 22562306a36Sopenharmony_ci return ret; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci avs_hda_power_gating_enable(adev, false); 22862306a36Sopenharmony_ci avs_hda_clock_gating_enable(adev, false); 22962306a36Sopenharmony_ci avs_hda_l1sen_enable(adev, false); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci hda_cldma_set_data(cl, (void *)mod->data, mod->size); 23262306a36Sopenharmony_ci hda_cldma_transfer(cl, msecs_to_jiffies(AVS_CLDMA_START_DELAY_MS)); 23362306a36Sopenharmony_ci ret = avs_ipc_load_modules(adev, &mentry->module_id, 1); 23462306a36Sopenharmony_ci hda_cldma_stop(cl); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci avs_hda_l1sen_enable(adev, true); 23762306a36Sopenharmony_ci avs_hda_clock_gating_enable(adev, true); 23862306a36Sopenharmony_ci avs_hda_power_gating_enable(adev, true); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci if (ret) { 24162306a36Sopenharmony_ci dev_err(adev->dev, "load module %d failed: %d\n", mentry->module_id, ret); 24262306a36Sopenharmony_ci avs_release_last_firmware(adev); 24362306a36Sopenharmony_ci return AVS_IPC_RET(ret); 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci return 0; 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ciint avs_cldma_transfer_modules(struct avs_dev *adev, bool load, 25062306a36Sopenharmony_ci struct avs_module_entry *mods, u32 num_mods) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci u16 *mod_ids; 25362306a36Sopenharmony_ci int ret, i; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* Either load to DSP or unload them to free space. */ 25662306a36Sopenharmony_ci if (load) { 25762306a36Sopenharmony_ci for (i = 0; i < num_mods; i++) { 25862306a36Sopenharmony_ci ret = avs_cldma_load_module(adev, &mods[i]); 25962306a36Sopenharmony_ci if (ret) 26062306a36Sopenharmony_ci return ret; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci return 0; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci mod_ids = kcalloc(num_mods, sizeof(u16), GFP_KERNEL); 26762306a36Sopenharmony_ci if (!mod_ids) 26862306a36Sopenharmony_ci return -ENOMEM; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci for (i = 0; i < num_mods; i++) 27162306a36Sopenharmony_ci mod_ids[i] = mods[i].module_id; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci ret = avs_ipc_unload_modules(adev, mod_ids, num_mods); 27462306a36Sopenharmony_ci kfree(mod_ids); 27562306a36Sopenharmony_ci if (ret) 27662306a36Sopenharmony_ci return AVS_IPC_RET(ret); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci return 0; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic int 28262306a36Sopenharmony_ciavs_hda_init_rom(struct avs_dev *adev, unsigned int dma_id, bool purge) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci const struct avs_spec *const spec = adev->spec; 28562306a36Sopenharmony_ci unsigned int corex_mask, reg; 28662306a36Sopenharmony_ci int ret; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci corex_mask = spec->core_init_mask & ~AVS_MAIN_CORE_MASK; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci ret = avs_dsp_op(adev, power, spec->core_init_mask, true); 29162306a36Sopenharmony_ci if (ret < 0) 29262306a36Sopenharmony_ci goto err; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci ret = avs_dsp_op(adev, reset, AVS_MAIN_CORE_MASK, false); 29562306a36Sopenharmony_ci if (ret < 0) 29662306a36Sopenharmony_ci goto err; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci reinit_completion(&adev->fw_ready); 29962306a36Sopenharmony_ci avs_dsp_op(adev, int_control, true); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* set boot config */ 30262306a36Sopenharmony_ci ret = avs_ipc_set_boot_config(adev, dma_id, purge); 30362306a36Sopenharmony_ci if (ret) { 30462306a36Sopenharmony_ci ret = AVS_IPC_RET(ret); 30562306a36Sopenharmony_ci goto err; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* await ROM init */ 30962306a36Sopenharmony_ci ret = snd_hdac_adsp_readq_poll(adev, spec->rom_status, reg, 31062306a36Sopenharmony_ci (reg & 0xF) == AVS_ROM_INIT_DONE || 31162306a36Sopenharmony_ci (reg & 0xF) == APL_ROM_FW_ENTERED, 31262306a36Sopenharmony_ci AVS_ROM_INIT_POLLING_US, APL_ROM_INIT_TIMEOUT_US); 31362306a36Sopenharmony_ci if (ret < 0) { 31462306a36Sopenharmony_ci dev_err(adev->dev, "rom init timeout: %d\n", ret); 31562306a36Sopenharmony_ci goto err; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* power down non-main cores */ 31962306a36Sopenharmony_ci if (corex_mask) { 32062306a36Sopenharmony_ci ret = avs_dsp_op(adev, power, corex_mask, false); 32162306a36Sopenharmony_ci if (ret < 0) 32262306a36Sopenharmony_ci goto err; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci return 0; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cierr: 32862306a36Sopenharmony_ci avs_dsp_core_disable(adev, spec->core_init_mask); 32962306a36Sopenharmony_ci return ret; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic int avs_imr_load_basefw(struct avs_dev *adev) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci int ret; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci /* DMA id ignored when flashing from IMR as no transfer occurs. */ 33762306a36Sopenharmony_ci ret = avs_hda_init_rom(adev, 0, false); 33862306a36Sopenharmony_ci if (ret < 0) { 33962306a36Sopenharmony_ci dev_err(adev->dev, "rom init failed: %d\n", ret); 34062306a36Sopenharmony_ci return ret; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci ret = wait_for_completion_timeout(&adev->fw_ready, 34462306a36Sopenharmony_ci msecs_to_jiffies(AVS_FW_INIT_TIMEOUT_MS)); 34562306a36Sopenharmony_ci if (!ret) { 34662306a36Sopenharmony_ci dev_err(adev->dev, "firmware ready timeout\n"); 34762306a36Sopenharmony_ci avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK); 34862306a36Sopenharmony_ci return -ETIMEDOUT; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci return 0; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ciint avs_hda_load_basefw(struct avs_dev *adev, struct firmware *fw) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci struct snd_pcm_substream substream; 35762306a36Sopenharmony_ci struct snd_dma_buffer dmab; 35862306a36Sopenharmony_ci struct hdac_ext_stream *estream; 35962306a36Sopenharmony_ci struct hdac_stream *hstream; 36062306a36Sopenharmony_ci struct hdac_bus *bus = &adev->base.core; 36162306a36Sopenharmony_ci unsigned int sdfmt, reg; 36262306a36Sopenharmony_ci int ret, i; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* configure hda dma */ 36562306a36Sopenharmony_ci memset(&substream, 0, sizeof(substream)); 36662306a36Sopenharmony_ci substream.stream = SNDRV_PCM_STREAM_PLAYBACK; 36762306a36Sopenharmony_ci estream = snd_hdac_ext_stream_assign(bus, &substream, 36862306a36Sopenharmony_ci HDAC_EXT_STREAM_TYPE_HOST); 36962306a36Sopenharmony_ci if (!estream) 37062306a36Sopenharmony_ci return -ENODEV; 37162306a36Sopenharmony_ci hstream = hdac_stream(estream); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* code loading performed with default format */ 37462306a36Sopenharmony_ci sdfmt = snd_hdac_calc_stream_format(48000, 1, SNDRV_PCM_FORMAT_S32_LE, 32, 0); 37562306a36Sopenharmony_ci ret = snd_hdac_dsp_prepare(hstream, sdfmt, fw->size, &dmab); 37662306a36Sopenharmony_ci if (ret < 0) 37762306a36Sopenharmony_ci goto release_stream; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci /* enable SPIB for hda stream */ 38062306a36Sopenharmony_ci snd_hdac_stream_spbcap_enable(bus, true, hstream->index); 38162306a36Sopenharmony_ci ret = snd_hdac_stream_set_spib(bus, hstream, fw->size); 38262306a36Sopenharmony_ci if (ret) 38362306a36Sopenharmony_ci goto cleanup_resources; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci memcpy(dmab.area, fw->data, fw->size); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci for (i = 0; i < APL_ROM_INIT_RETRIES; i++) { 38862306a36Sopenharmony_ci unsigned int dma_id = hstream->stream_tag - 1; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci ret = avs_hda_init_rom(adev, dma_id, true); 39162306a36Sopenharmony_ci if (!ret) 39262306a36Sopenharmony_ci break; 39362306a36Sopenharmony_ci dev_info(adev->dev, "#%d rom init fail: %d\n", i + 1, ret); 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci if (ret < 0) 39662306a36Sopenharmony_ci goto cleanup_resources; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* transfer firmware */ 39962306a36Sopenharmony_ci snd_hdac_dsp_trigger(hstream, true); 40062306a36Sopenharmony_ci ret = snd_hdac_adsp_readl_poll(adev, AVS_FW_REG_STATUS(adev), reg, 40162306a36Sopenharmony_ci (reg & AVS_ROM_STS_MASK) == APL_ROM_FW_ENTERED, 40262306a36Sopenharmony_ci AVS_FW_INIT_POLLING_US, AVS_FW_INIT_TIMEOUT_US); 40362306a36Sopenharmony_ci snd_hdac_dsp_trigger(hstream, false); 40462306a36Sopenharmony_ci if (ret < 0) { 40562306a36Sopenharmony_ci dev_err(adev->dev, "transfer fw failed: %d\n", ret); 40662306a36Sopenharmony_ci avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK); 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cicleanup_resources: 41062306a36Sopenharmony_ci /* disable SPIB for hda stream */ 41162306a36Sopenharmony_ci snd_hdac_stream_spbcap_enable(bus, false, hstream->index); 41262306a36Sopenharmony_ci snd_hdac_stream_set_spib(bus, hstream, 0); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci snd_hdac_dsp_cleanup(hstream, &dmab); 41562306a36Sopenharmony_cirelease_stream: 41662306a36Sopenharmony_ci snd_hdac_ext_stream_release(estream, HDAC_EXT_STREAM_TYPE_HOST); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci return ret; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ciint avs_hda_load_library(struct avs_dev *adev, struct firmware *lib, u32 id) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci struct snd_pcm_substream substream; 42462306a36Sopenharmony_ci struct snd_dma_buffer dmab; 42562306a36Sopenharmony_ci struct hdac_ext_stream *estream; 42662306a36Sopenharmony_ci struct hdac_stream *stream; 42762306a36Sopenharmony_ci struct hdac_bus *bus = &adev->base.core; 42862306a36Sopenharmony_ci unsigned int sdfmt; 42962306a36Sopenharmony_ci int ret; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* configure hda dma */ 43262306a36Sopenharmony_ci memset(&substream, 0, sizeof(substream)); 43362306a36Sopenharmony_ci substream.stream = SNDRV_PCM_STREAM_PLAYBACK; 43462306a36Sopenharmony_ci estream = snd_hdac_ext_stream_assign(bus, &substream, 43562306a36Sopenharmony_ci HDAC_EXT_STREAM_TYPE_HOST); 43662306a36Sopenharmony_ci if (!estream) 43762306a36Sopenharmony_ci return -ENODEV; 43862306a36Sopenharmony_ci stream = hdac_stream(estream); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci /* code loading performed with default format */ 44162306a36Sopenharmony_ci sdfmt = snd_hdac_calc_stream_format(48000, 1, SNDRV_PCM_FORMAT_S32_LE, 32, 0); 44262306a36Sopenharmony_ci ret = snd_hdac_dsp_prepare(stream, sdfmt, lib->size, &dmab); 44362306a36Sopenharmony_ci if (ret < 0) 44462306a36Sopenharmony_ci goto release_stream; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci /* enable SPIB for hda stream */ 44762306a36Sopenharmony_ci snd_hdac_stream_spbcap_enable(bus, true, stream->index); 44862306a36Sopenharmony_ci snd_hdac_stream_set_spib(bus, stream, lib->size); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci memcpy(dmab.area, lib->data, lib->size); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci /* transfer firmware */ 45362306a36Sopenharmony_ci snd_hdac_dsp_trigger(stream, true); 45462306a36Sopenharmony_ci ret = avs_ipc_load_library(adev, stream->stream_tag - 1, id); 45562306a36Sopenharmony_ci snd_hdac_dsp_trigger(stream, false); 45662306a36Sopenharmony_ci if (ret) { 45762306a36Sopenharmony_ci dev_err(adev->dev, "transfer lib %d failed: %d\n", id, ret); 45862306a36Sopenharmony_ci ret = AVS_IPC_RET(ret); 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci /* disable SPIB for hda stream */ 46262306a36Sopenharmony_ci snd_hdac_stream_spbcap_enable(bus, false, stream->index); 46362306a36Sopenharmony_ci snd_hdac_stream_set_spib(bus, stream, 0); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci snd_hdac_dsp_cleanup(stream, &dmab); 46662306a36Sopenharmony_cirelease_stream: 46762306a36Sopenharmony_ci snd_hdac_ext_stream_release(estream, HDAC_EXT_STREAM_TYPE_HOST); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci return ret; 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ciint avs_hda_transfer_modules(struct avs_dev *adev, bool load, 47362306a36Sopenharmony_ci struct avs_module_entry *mods, u32 num_mods) 47462306a36Sopenharmony_ci{ 47562306a36Sopenharmony_ci /* 47662306a36Sopenharmony_ci * All platforms without CLDMA are equipped with IMR, 47762306a36Sopenharmony_ci * and thus the module transferring is offloaded to DSP. 47862306a36Sopenharmony_ci */ 47962306a36Sopenharmony_ci return 0; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ciint avs_dsp_load_libraries(struct avs_dev *adev, struct avs_tplg_library *libs, u32 num_libs) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci int start, id, i = 0; 48562306a36Sopenharmony_ci int ret; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci /* Calculate the id to assign for the next lib. */ 48862306a36Sopenharmony_ci for (id = 0; id < adev->fw_cfg.max_libs_count; id++) 48962306a36Sopenharmony_ci if (adev->lib_names[id][0] == '\0') 49062306a36Sopenharmony_ci break; 49162306a36Sopenharmony_ci if (id + num_libs >= adev->fw_cfg.max_libs_count) 49262306a36Sopenharmony_ci return -EINVAL; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci start = id; 49562306a36Sopenharmony_ci while (i < num_libs) { 49662306a36Sopenharmony_ci struct avs_fw_manifest *man; 49762306a36Sopenharmony_ci const struct firmware *fw; 49862306a36Sopenharmony_ci struct firmware stripped_fw; 49962306a36Sopenharmony_ci char *filename; 50062306a36Sopenharmony_ci int j; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci filename = kasprintf(GFP_KERNEL, "%s/%s/%s", AVS_ROOT_DIR, adev->spec->name, 50362306a36Sopenharmony_ci libs[i].name); 50462306a36Sopenharmony_ci if (!filename) 50562306a36Sopenharmony_ci return -ENOMEM; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci /* 50862306a36Sopenharmony_ci * If any call after this one fails, requested firmware is not released with 50962306a36Sopenharmony_ci * avs_release_last_firmware() as failing to load code results in need for reload 51062306a36Sopenharmony_ci * of entire driver module. And then avs_release_firmwares() is in place already. 51162306a36Sopenharmony_ci */ 51262306a36Sopenharmony_ci ret = avs_request_firmware(adev, &fw, filename); 51362306a36Sopenharmony_ci kfree(filename); 51462306a36Sopenharmony_ci if (ret < 0) 51562306a36Sopenharmony_ci return ret; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci stripped_fw = *fw; 51862306a36Sopenharmony_ci ret = avs_fw_manifest_strip_verify(adev, &stripped_fw, NULL); 51962306a36Sopenharmony_ci if (ret) { 52062306a36Sopenharmony_ci dev_err(adev->dev, "invalid library data: %d\n", ret); 52162306a36Sopenharmony_ci return ret; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci ret = avs_fw_manifest_offset(&stripped_fw); 52562306a36Sopenharmony_ci if (ret < 0) 52662306a36Sopenharmony_ci return ret; 52762306a36Sopenharmony_ci man = (struct avs_fw_manifest *)(stripped_fw.data + ret); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* Don't load anything that's already in DSP memory. */ 53062306a36Sopenharmony_ci for (j = 0; j < id; j++) 53162306a36Sopenharmony_ci if (!strncmp(adev->lib_names[j], man->name, AVS_LIB_NAME_SIZE)) 53262306a36Sopenharmony_ci goto next_lib; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci ret = avs_dsp_op(adev, load_lib, &stripped_fw, id); 53562306a36Sopenharmony_ci if (ret) 53662306a36Sopenharmony_ci return ret; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci strncpy(adev->lib_names[id], man->name, AVS_LIB_NAME_SIZE); 53962306a36Sopenharmony_ci id++; 54062306a36Sopenharmony_cinext_lib: 54162306a36Sopenharmony_ci i++; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci return start == id ? 1 : 0; 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistatic int avs_dsp_load_basefw(struct avs_dev *adev) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci const struct avs_fw_version *min_req; 55062306a36Sopenharmony_ci const struct avs_spec *const spec = adev->spec; 55162306a36Sopenharmony_ci const struct firmware *fw; 55262306a36Sopenharmony_ci struct firmware stripped_fw; 55362306a36Sopenharmony_ci char *filename; 55462306a36Sopenharmony_ci int ret; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci filename = kasprintf(GFP_KERNEL, "%s/%s/%s", AVS_ROOT_DIR, spec->name, AVS_BASEFW_FILENAME); 55762306a36Sopenharmony_ci if (!filename) 55862306a36Sopenharmony_ci return -ENOMEM; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci ret = avs_request_firmware(adev, &fw, filename); 56162306a36Sopenharmony_ci kfree(filename); 56262306a36Sopenharmony_ci if (ret < 0) { 56362306a36Sopenharmony_ci dev_err(adev->dev, "request firmware failed: %d\n", ret); 56462306a36Sopenharmony_ci return ret; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci stripped_fw = *fw; 56862306a36Sopenharmony_ci min_req = &adev->spec->min_fw_version; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci ret = avs_fw_manifest_strip_verify(adev, &stripped_fw, min_req); 57162306a36Sopenharmony_ci if (ret < 0) { 57262306a36Sopenharmony_ci dev_err(adev->dev, "invalid firmware data: %d\n", ret); 57362306a36Sopenharmony_ci goto release_fw; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci ret = avs_dsp_op(adev, load_basefw, &stripped_fw); 57762306a36Sopenharmony_ci if (ret < 0) { 57862306a36Sopenharmony_ci dev_err(adev->dev, "basefw load failed: %d\n", ret); 57962306a36Sopenharmony_ci goto release_fw; 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci ret = wait_for_completion_timeout(&adev->fw_ready, 58362306a36Sopenharmony_ci msecs_to_jiffies(AVS_FW_INIT_TIMEOUT_MS)); 58462306a36Sopenharmony_ci if (!ret) { 58562306a36Sopenharmony_ci dev_err(adev->dev, "firmware ready timeout\n"); 58662306a36Sopenharmony_ci avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK); 58762306a36Sopenharmony_ci ret = -ETIMEDOUT; 58862306a36Sopenharmony_ci goto release_fw; 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci return 0; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_cirelease_fw: 59462306a36Sopenharmony_ci avs_release_last_firmware(adev); 59562306a36Sopenharmony_ci return ret; 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ciint avs_dsp_boot_firmware(struct avs_dev *adev, bool purge) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci struct avs_soc_component *acomp; 60162306a36Sopenharmony_ci int ret, i; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci /* Forgo full boot if flash from IMR succeeds. */ 60462306a36Sopenharmony_ci if (!purge && avs_platattr_test(adev, IMR)) { 60562306a36Sopenharmony_ci ret = avs_imr_load_basefw(adev); 60662306a36Sopenharmony_ci if (!ret) 60762306a36Sopenharmony_ci return 0; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci dev_dbg(adev->dev, "firmware flash from imr failed: %d\n", ret); 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci /* Full boot, clear cached data except for basefw (slot 0). */ 61362306a36Sopenharmony_ci for (i = 1; i < adev->fw_cfg.max_libs_count; i++) 61462306a36Sopenharmony_ci memset(adev->lib_names[i], 0, AVS_LIB_NAME_SIZE); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci avs_hda_power_gating_enable(adev, false); 61762306a36Sopenharmony_ci avs_hda_clock_gating_enable(adev, false); 61862306a36Sopenharmony_ci avs_hda_l1sen_enable(adev, false); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci ret = avs_dsp_load_basefw(adev); 62162306a36Sopenharmony_ci if (ret) 62262306a36Sopenharmony_ci goto reenable_gating; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci mutex_lock(&adev->comp_list_mutex); 62562306a36Sopenharmony_ci list_for_each_entry(acomp, &adev->comp_list, node) { 62662306a36Sopenharmony_ci struct avs_tplg *tplg = acomp->tplg; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci ret = avs_dsp_load_libraries(adev, tplg->libs, tplg->num_libs); 62962306a36Sopenharmony_ci if (ret < 0) 63062306a36Sopenharmony_ci break; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci mutex_unlock(&adev->comp_list_mutex); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_cireenable_gating: 63562306a36Sopenharmony_ci avs_hda_l1sen_enable(adev, true); 63662306a36Sopenharmony_ci avs_hda_clock_gating_enable(adev, true); 63762306a36Sopenharmony_ci avs_hda_power_gating_enable(adev, true); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci if (ret < 0) 64062306a36Sopenharmony_ci return ret; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci /* With all code loaded, refresh module information. */ 64362306a36Sopenharmony_ci ret = avs_module_info_init(adev, true); 64462306a36Sopenharmony_ci if (ret) { 64562306a36Sopenharmony_ci dev_err(adev->dev, "init module info failed: %d\n", ret); 64662306a36Sopenharmony_ci return ret; 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci return 0; 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ciint avs_dsp_first_boot_firmware(struct avs_dev *adev) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci int ret, i; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci if (avs_platattr_test(adev, CLDMA)) { 65762306a36Sopenharmony_ci ret = hda_cldma_init(&code_loader, &adev->base.core, 65862306a36Sopenharmony_ci adev->dsp_ba, AVS_CL_DEFAULT_BUFFER_SIZE); 65962306a36Sopenharmony_ci if (ret < 0) { 66062306a36Sopenharmony_ci dev_err(adev->dev, "cldma init failed: %d\n", ret); 66162306a36Sopenharmony_ci return ret; 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci ret = avs_dsp_boot_firmware(adev, true); 66662306a36Sopenharmony_ci if (ret < 0) { 66762306a36Sopenharmony_ci dev_err(adev->dev, "firmware boot failed: %d\n", ret); 66862306a36Sopenharmony_ci return ret; 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci ret = avs_ipc_get_hw_config(adev, &adev->hw_cfg); 67262306a36Sopenharmony_ci if (ret) { 67362306a36Sopenharmony_ci dev_err(adev->dev, "get hw cfg failed: %d\n", ret); 67462306a36Sopenharmony_ci return AVS_IPC_RET(ret); 67562306a36Sopenharmony_ci } 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci ret = avs_ipc_get_fw_config(adev, &adev->fw_cfg); 67862306a36Sopenharmony_ci if (ret) { 67962306a36Sopenharmony_ci dev_err(adev->dev, "get fw cfg failed: %d\n", ret); 68062306a36Sopenharmony_ci return AVS_IPC_RET(ret); 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci adev->core_refs = devm_kcalloc(adev->dev, adev->hw_cfg.dsp_cores, 68462306a36Sopenharmony_ci sizeof(*adev->core_refs), GFP_KERNEL); 68562306a36Sopenharmony_ci adev->lib_names = devm_kcalloc(adev->dev, adev->fw_cfg.max_libs_count, 68662306a36Sopenharmony_ci sizeof(*adev->lib_names), GFP_KERNEL); 68762306a36Sopenharmony_ci if (!adev->core_refs || !adev->lib_names) 68862306a36Sopenharmony_ci return -ENOMEM; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci for (i = 0; i < adev->fw_cfg.max_libs_count; i++) { 69162306a36Sopenharmony_ci adev->lib_names[i] = devm_kzalloc(adev->dev, AVS_LIB_NAME_SIZE, GFP_KERNEL); 69262306a36Sopenharmony_ci if (!adev->lib_names[i]) 69362306a36Sopenharmony_ci return -ENOMEM; 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci /* basefw always occupies slot 0 */ 69762306a36Sopenharmony_ci strcpy(&adev->lib_names[0][0], "BASEFW"); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci ida_init(&adev->ppl_ida); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci return 0; 70262306a36Sopenharmony_ci} 703