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 <sound/hdaudio_ext.h> 1062306a36Sopenharmony_ci#include "avs.h" 1162306a36Sopenharmony_ci#include "registers.h" 1262306a36Sopenharmony_ci#include "trace.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define AVS_ADSPCS_INTERVAL_US 500 1562306a36Sopenharmony_ci#define AVS_ADSPCS_TIMEOUT_US 50000 1662306a36Sopenharmony_ci#define AVS_ADSPCS_DELAY_US 1000 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ciint avs_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool power) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci u32 value, mask, reg; 2162306a36Sopenharmony_ci int ret; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS); 2462306a36Sopenharmony_ci trace_avs_dsp_core_op(value, core_mask, "power", power); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci mask = AVS_ADSPCS_SPA_MASK(core_mask); 2762306a36Sopenharmony_ci value = power ? mask : 0; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value); 3062306a36Sopenharmony_ci /* Delay the polling to avoid false positives. */ 3162306a36Sopenharmony_ci usleep_range(AVS_ADSPCS_DELAY_US, 2 * AVS_ADSPCS_DELAY_US); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci mask = AVS_ADSPCS_CPA_MASK(core_mask); 3462306a36Sopenharmony_ci value = power ? mask : 0; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS, 3762306a36Sopenharmony_ci reg, (reg & mask) == value, 3862306a36Sopenharmony_ci AVS_ADSPCS_INTERVAL_US, 3962306a36Sopenharmony_ci AVS_ADSPCS_TIMEOUT_US); 4062306a36Sopenharmony_ci if (ret) 4162306a36Sopenharmony_ci dev_err(adev->dev, "core_mask %d power %s failed: %d\n", 4262306a36Sopenharmony_ci core_mask, power ? "on" : "off", ret); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci return ret; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ciint avs_dsp_core_reset(struct avs_dev *adev, u32 core_mask, bool reset) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci u32 value, mask, reg; 5062306a36Sopenharmony_ci int ret; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS); 5362306a36Sopenharmony_ci trace_avs_dsp_core_op(value, core_mask, "reset", reset); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci mask = AVS_ADSPCS_CRST_MASK(core_mask); 5662306a36Sopenharmony_ci value = reset ? mask : 0; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS, 6162306a36Sopenharmony_ci reg, (reg & mask) == value, 6262306a36Sopenharmony_ci AVS_ADSPCS_INTERVAL_US, 6362306a36Sopenharmony_ci AVS_ADSPCS_TIMEOUT_US); 6462306a36Sopenharmony_ci if (ret) 6562306a36Sopenharmony_ci dev_err(adev->dev, "core_mask %d %s reset failed: %d\n", 6662306a36Sopenharmony_ci core_mask, reset ? "enter" : "exit", ret); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci return ret; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ciint avs_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stall) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci u32 value, mask, reg; 7462306a36Sopenharmony_ci int ret; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS); 7762306a36Sopenharmony_ci trace_avs_dsp_core_op(value, core_mask, "stall", stall); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci mask = AVS_ADSPCS_CSTALL_MASK(core_mask); 8062306a36Sopenharmony_ci value = stall ? mask : 0; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS, 8562306a36Sopenharmony_ci reg, (reg & mask) == value, 8662306a36Sopenharmony_ci AVS_ADSPCS_INTERVAL_US, 8762306a36Sopenharmony_ci AVS_ADSPCS_TIMEOUT_US); 8862306a36Sopenharmony_ci if (ret) { 8962306a36Sopenharmony_ci dev_err(adev->dev, "core_mask %d %sstall failed: %d\n", 9062306a36Sopenharmony_ci core_mask, stall ? "" : "un", ret); 9162306a36Sopenharmony_ci return ret; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci /* Give HW time to propagate the change. */ 9562306a36Sopenharmony_ci usleep_range(AVS_ADSPCS_DELAY_US, 2 * AVS_ADSPCS_DELAY_US); 9662306a36Sopenharmony_ci return 0; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ciint avs_dsp_core_enable(struct avs_dev *adev, u32 core_mask) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci int ret; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci ret = avs_dsp_op(adev, power, core_mask, true); 10462306a36Sopenharmony_ci if (ret) 10562306a36Sopenharmony_ci return ret; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci ret = avs_dsp_op(adev, reset, core_mask, false); 10862306a36Sopenharmony_ci if (ret) 10962306a36Sopenharmony_ci return ret; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci return avs_dsp_op(adev, stall, core_mask, false); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ciint avs_dsp_core_disable(struct avs_dev *adev, u32 core_mask) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci /* No error checks to allow for complete DSP shutdown. */ 11762306a36Sopenharmony_ci avs_dsp_op(adev, stall, core_mask, true); 11862306a36Sopenharmony_ci avs_dsp_op(adev, reset, core_mask, true); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci return avs_dsp_op(adev, power, core_mask, false); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic int avs_dsp_enable(struct avs_dev *adev, u32 core_mask) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci u32 mask; 12662306a36Sopenharmony_ci int ret; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci ret = avs_dsp_core_enable(adev, core_mask); 12962306a36Sopenharmony_ci if (ret < 0) 13062306a36Sopenharmony_ci return ret; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci mask = core_mask & ~AVS_MAIN_CORE_MASK; 13362306a36Sopenharmony_ci if (!mask) 13462306a36Sopenharmony_ci /* 13562306a36Sopenharmony_ci * without main core, fw is dead anyway 13662306a36Sopenharmony_ci * so setting D0 for it is futile. 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_ci return 0; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci ret = avs_ipc_set_dx(adev, mask, true); 14162306a36Sopenharmony_ci return AVS_IPC_RET(ret); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic int avs_dsp_disable(struct avs_dev *adev, u32 core_mask) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci int ret; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci ret = avs_ipc_set_dx(adev, core_mask, false); 14962306a36Sopenharmony_ci if (ret) 15062306a36Sopenharmony_ci return AVS_IPC_RET(ret); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return avs_dsp_core_disable(adev, core_mask); 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic int avs_dsp_get_core(struct avs_dev *adev, u32 core_id) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci u32 mask; 15862306a36Sopenharmony_ci int ret; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci mask = BIT_MASK(core_id); 16162306a36Sopenharmony_ci if (mask == AVS_MAIN_CORE_MASK) 16262306a36Sopenharmony_ci /* nothing to do for main core */ 16362306a36Sopenharmony_ci return 0; 16462306a36Sopenharmony_ci if (core_id >= adev->hw_cfg.dsp_cores) { 16562306a36Sopenharmony_ci ret = -EINVAL; 16662306a36Sopenharmony_ci goto err; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci adev->core_refs[core_id]++; 17062306a36Sopenharmony_ci if (adev->core_refs[core_id] == 1) { 17162306a36Sopenharmony_ci /* 17262306a36Sopenharmony_ci * No cores other than main-core can be running for DSP 17362306a36Sopenharmony_ci * to achieve d0ix. Conscious SET_D0IX IPC failure is permitted, 17462306a36Sopenharmony_ci * simply d0ix power state will no longer be attempted. 17562306a36Sopenharmony_ci */ 17662306a36Sopenharmony_ci ret = avs_dsp_disable_d0ix(adev); 17762306a36Sopenharmony_ci if (ret && ret != -AVS_EIPC) 17862306a36Sopenharmony_ci goto err_disable_d0ix; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci ret = avs_dsp_enable(adev, mask); 18162306a36Sopenharmony_ci if (ret) 18262306a36Sopenharmony_ci goto err_enable_dsp; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci return 0; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cierr_enable_dsp: 18862306a36Sopenharmony_ci avs_dsp_enable_d0ix(adev); 18962306a36Sopenharmony_cierr_disable_d0ix: 19062306a36Sopenharmony_ci adev->core_refs[core_id]--; 19162306a36Sopenharmony_cierr: 19262306a36Sopenharmony_ci dev_err(adev->dev, "get core %d failed: %d\n", core_id, ret); 19362306a36Sopenharmony_ci return ret; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic int avs_dsp_put_core(struct avs_dev *adev, u32 core_id) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci u32 mask; 19962306a36Sopenharmony_ci int ret; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci mask = BIT_MASK(core_id); 20262306a36Sopenharmony_ci if (mask == AVS_MAIN_CORE_MASK) 20362306a36Sopenharmony_ci /* nothing to do for main core */ 20462306a36Sopenharmony_ci return 0; 20562306a36Sopenharmony_ci if (core_id >= adev->hw_cfg.dsp_cores) { 20662306a36Sopenharmony_ci ret = -EINVAL; 20762306a36Sopenharmony_ci goto err; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci adev->core_refs[core_id]--; 21162306a36Sopenharmony_ci if (!adev->core_refs[core_id]) { 21262306a36Sopenharmony_ci ret = avs_dsp_disable(adev, mask); 21362306a36Sopenharmony_ci if (ret) 21462306a36Sopenharmony_ci goto err; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci /* Match disable_d0ix in avs_dsp_get_core(). */ 21762306a36Sopenharmony_ci avs_dsp_enable_d0ix(adev); 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci return 0; 22162306a36Sopenharmony_cierr: 22262306a36Sopenharmony_ci dev_err(adev->dev, "put core %d failed: %d\n", core_id, ret); 22362306a36Sopenharmony_ci return ret; 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ciint avs_dsp_init_module(struct avs_dev *adev, u16 module_id, u8 ppl_instance_id, 22762306a36Sopenharmony_ci u8 core_id, u8 domain, void *param, u32 param_size, 22862306a36Sopenharmony_ci u8 *instance_id) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct avs_module_entry mentry; 23162306a36Sopenharmony_ci bool was_loaded = false; 23262306a36Sopenharmony_ci int ret, id; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci id = avs_module_id_alloc(adev, module_id); 23562306a36Sopenharmony_ci if (id < 0) 23662306a36Sopenharmony_ci return id; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci ret = avs_get_module_id_entry(adev, module_id, &mentry); 23962306a36Sopenharmony_ci if (ret) 24062306a36Sopenharmony_ci goto err_mod_entry; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci ret = avs_dsp_get_core(adev, core_id); 24362306a36Sopenharmony_ci if (ret) 24462306a36Sopenharmony_ci goto err_mod_entry; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* Load code into memory if this is the first instance. */ 24762306a36Sopenharmony_ci if (!id && !avs_module_entry_is_loaded(&mentry)) { 24862306a36Sopenharmony_ci ret = avs_dsp_op(adev, transfer_mods, true, &mentry, 1); 24962306a36Sopenharmony_ci if (ret) { 25062306a36Sopenharmony_ci dev_err(adev->dev, "load modules failed: %d\n", ret); 25162306a36Sopenharmony_ci goto err_mod_entry; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci was_loaded = true; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci ret = avs_ipc_init_instance(adev, module_id, id, ppl_instance_id, 25762306a36Sopenharmony_ci core_id, domain, param, param_size); 25862306a36Sopenharmony_ci if (ret) { 25962306a36Sopenharmony_ci ret = AVS_IPC_RET(ret); 26062306a36Sopenharmony_ci goto err_ipc; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci *instance_id = id; 26462306a36Sopenharmony_ci return 0; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cierr_ipc: 26762306a36Sopenharmony_ci if (was_loaded) 26862306a36Sopenharmony_ci avs_dsp_op(adev, transfer_mods, false, &mentry, 1); 26962306a36Sopenharmony_ci avs_dsp_put_core(adev, core_id); 27062306a36Sopenharmony_cierr_mod_entry: 27162306a36Sopenharmony_ci avs_module_id_free(adev, module_id, id); 27262306a36Sopenharmony_ci return ret; 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_civoid avs_dsp_delete_module(struct avs_dev *adev, u16 module_id, u8 instance_id, 27662306a36Sopenharmony_ci u8 ppl_instance_id, u8 core_id) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci struct avs_module_entry mentry; 27962306a36Sopenharmony_ci int ret; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci /* Modules not owned by any pipeline need to be freed explicitly. */ 28262306a36Sopenharmony_ci if (ppl_instance_id == INVALID_PIPELINE_ID) 28362306a36Sopenharmony_ci avs_ipc_delete_instance(adev, module_id, instance_id); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci avs_module_id_free(adev, module_id, instance_id); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci ret = avs_get_module_id_entry(adev, module_id, &mentry); 28862306a36Sopenharmony_ci /* Unload occupied memory if this was the last instance. */ 28962306a36Sopenharmony_ci if (!ret && mentry.type.load_type == AVS_MODULE_LOAD_TYPE_LOADABLE) { 29062306a36Sopenharmony_ci if (avs_is_module_ida_empty(adev, module_id)) { 29162306a36Sopenharmony_ci ret = avs_dsp_op(adev, transfer_mods, false, &mentry, 1); 29262306a36Sopenharmony_ci if (ret) 29362306a36Sopenharmony_ci dev_err(adev->dev, "unload modules failed: %d\n", ret); 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci avs_dsp_put_core(adev, core_id); 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ciint avs_dsp_create_pipeline(struct avs_dev *adev, u16 req_size, u8 priority, 30162306a36Sopenharmony_ci bool lp, u16 attributes, u8 *instance_id) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci struct avs_fw_cfg *fw_cfg = &adev->fw_cfg; 30462306a36Sopenharmony_ci int ret, id; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci id = ida_alloc_max(&adev->ppl_ida, fw_cfg->max_ppl_count - 1, GFP_KERNEL); 30762306a36Sopenharmony_ci if (id < 0) 30862306a36Sopenharmony_ci return id; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci ret = avs_ipc_create_pipeline(adev, req_size, priority, id, lp, attributes); 31162306a36Sopenharmony_ci if (ret) { 31262306a36Sopenharmony_ci ida_free(&adev->ppl_ida, id); 31362306a36Sopenharmony_ci return AVS_IPC_RET(ret); 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci *instance_id = id; 31762306a36Sopenharmony_ci return 0; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ciint avs_dsp_delete_pipeline(struct avs_dev *adev, u8 instance_id) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci int ret; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci ret = avs_ipc_delete_pipeline(adev, instance_id); 32562306a36Sopenharmony_ci if (ret) 32662306a36Sopenharmony_ci ret = AVS_IPC_RET(ret); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci ida_free(&adev->ppl_ida, instance_id); 32962306a36Sopenharmony_ci return ret; 33062306a36Sopenharmony_ci} 331