331 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2//3// Copyright(c) 2021-2022 Intel Corporation4//5// Authors: Cezary Rojewski <cezary.rojewski@intel.com>6// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>7//8 9#include <sound/hdaudio_ext.h>10#include "avs.h"11#include "registers.h"12#include "trace.h"13 14#define AVS_ADSPCS_INTERVAL_US 50015#define AVS_ADSPCS_TIMEOUT_US 5000016#define AVS_ADSPCS_DELAY_US 100017 18int avs_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool power)19{20 u32 value, mask, reg;21 int ret;22 23 value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS);24 trace_avs_dsp_core_op(value, core_mask, "power", power);25 26 mask = AVS_ADSPCS_SPA_MASK(core_mask);27 value = power ? mask : 0;28 29 snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value);30 /* Delay the polling to avoid false positives. */31 usleep_range(AVS_ADSPCS_DELAY_US, 2 * AVS_ADSPCS_DELAY_US);32 33 mask = AVS_ADSPCS_CPA_MASK(core_mask);34 value = power ? mask : 0;35 36 ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS,37 reg, (reg & mask) == value,38 AVS_ADSPCS_INTERVAL_US,39 AVS_ADSPCS_TIMEOUT_US);40 if (ret)41 dev_err(adev->dev, "core_mask %d power %s failed: %d\n",42 core_mask, power ? "on" : "off", ret);43 44 return ret;45}46 47int avs_dsp_core_reset(struct avs_dev *adev, u32 core_mask, bool reset)48{49 u32 value, mask, reg;50 int ret;51 52 value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS);53 trace_avs_dsp_core_op(value, core_mask, "reset", reset);54 55 mask = AVS_ADSPCS_CRST_MASK(core_mask);56 value = reset ? mask : 0;57 58 snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value);59 60 ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS,61 reg, (reg & mask) == value,62 AVS_ADSPCS_INTERVAL_US,63 AVS_ADSPCS_TIMEOUT_US);64 if (ret)65 dev_err(adev->dev, "core_mask %d %s reset failed: %d\n",66 core_mask, reset ? "enter" : "exit", ret);67 68 return ret;69}70 71int avs_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stall)72{73 u32 value, mask, reg;74 int ret;75 76 value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS);77 trace_avs_dsp_core_op(value, core_mask, "stall", stall);78 79 mask = AVS_ADSPCS_CSTALL_MASK(core_mask);80 value = stall ? mask : 0;81 82 snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value);83 84 ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS,85 reg, (reg & mask) == value,86 AVS_ADSPCS_INTERVAL_US,87 AVS_ADSPCS_TIMEOUT_US);88 if (ret) {89 dev_err(adev->dev, "core_mask %d %sstall failed: %d\n",90 core_mask, stall ? "" : "un", ret);91 return ret;92 }93 94 /* Give HW time to propagate the change. */95 usleep_range(AVS_ADSPCS_DELAY_US, 2 * AVS_ADSPCS_DELAY_US);96 return 0;97}98 99int avs_dsp_core_enable(struct avs_dev *adev, u32 core_mask)100{101 int ret;102 103 ret = avs_dsp_op(adev, power, core_mask, true);104 if (ret)105 return ret;106 107 ret = avs_dsp_op(adev, reset, core_mask, false);108 if (ret)109 return ret;110 111 return avs_dsp_op(adev, stall, core_mask, false);112}113 114int avs_dsp_core_disable(struct avs_dev *adev, u32 core_mask)115{116 /* No error checks to allow for complete DSP shutdown. */117 avs_dsp_op(adev, stall, core_mask, true);118 avs_dsp_op(adev, reset, core_mask, true);119 120 return avs_dsp_op(adev, power, core_mask, false);121}122 123static int avs_dsp_enable(struct avs_dev *adev, u32 core_mask)124{125 u32 mask;126 int ret;127 128 ret = avs_dsp_core_enable(adev, core_mask);129 if (ret < 0)130 return ret;131 132 mask = core_mask & ~AVS_MAIN_CORE_MASK;133 if (!mask)134 /*135 * without main core, fw is dead anyway136 * so setting D0 for it is futile.137 */138 return 0;139 140 ret = avs_ipc_set_dx(adev, mask, true);141 return AVS_IPC_RET(ret);142}143 144static int avs_dsp_disable(struct avs_dev *adev, u32 core_mask)145{146 int ret;147 148 ret = avs_ipc_set_dx(adev, core_mask, false);149 if (ret)150 return AVS_IPC_RET(ret);151 152 return avs_dsp_core_disable(adev, core_mask);153}154 155static int avs_dsp_get_core(struct avs_dev *adev, u32 core_id)156{157 u32 mask;158 int ret;159 160 mask = BIT_MASK(core_id);161 if (mask == AVS_MAIN_CORE_MASK)162 /* nothing to do for main core */163 return 0;164 if (core_id >= adev->hw_cfg.dsp_cores) {165 ret = -EINVAL;166 goto err;167 }168 169 adev->core_refs[core_id]++;170 if (adev->core_refs[core_id] == 1) {171 /*172 * No cores other than main-core can be running for DSP173 * to achieve d0ix. Conscious SET_D0IX IPC failure is permitted,174 * simply d0ix power state will no longer be attempted.175 */176 ret = avs_dsp_disable_d0ix(adev);177 if (ret && ret != -AVS_EIPC)178 goto err_disable_d0ix;179 180 ret = avs_dsp_enable(adev, mask);181 if (ret)182 goto err_enable_dsp;183 }184 185 return 0;186 187err_enable_dsp:188 avs_dsp_enable_d0ix(adev);189err_disable_d0ix:190 adev->core_refs[core_id]--;191err:192 dev_err(adev->dev, "get core %d failed: %d\n", core_id, ret);193 return ret;194}195 196static int avs_dsp_put_core(struct avs_dev *adev, u32 core_id)197{198 u32 mask;199 int ret;200 201 mask = BIT_MASK(core_id);202 if (mask == AVS_MAIN_CORE_MASK)203 /* nothing to do for main core */204 return 0;205 if (core_id >= adev->hw_cfg.dsp_cores) {206 ret = -EINVAL;207 goto err;208 }209 210 adev->core_refs[core_id]--;211 if (!adev->core_refs[core_id]) {212 ret = avs_dsp_disable(adev, mask);213 if (ret)214 goto err;215 216 /* Match disable_d0ix in avs_dsp_get_core(). */217 avs_dsp_enable_d0ix(adev);218 }219 220 return 0;221err:222 dev_err(adev->dev, "put core %d failed: %d\n", core_id, ret);223 return ret;224}225 226int avs_dsp_init_module(struct avs_dev *adev, u16 module_id, u8 ppl_instance_id,227 u8 core_id, u8 domain, void *param, u32 param_size,228 u8 *instance_id)229{230 struct avs_module_entry mentry;231 bool was_loaded = false;232 int ret, id;233 234 id = avs_module_id_alloc(adev, module_id);235 if (id < 0)236 return id;237 238 ret = avs_get_module_id_entry(adev, module_id, &mentry);239 if (ret)240 goto err_mod_entry;241 242 ret = avs_dsp_get_core(adev, core_id);243 if (ret)244 goto err_mod_entry;245 246 /* Load code into memory if this is the first instance. */247 if (!id && !avs_module_entry_is_loaded(&mentry)) {248 ret = avs_dsp_op(adev, transfer_mods, true, &mentry, 1);249 if (ret) {250 dev_err(adev->dev, "load modules failed: %d\n", ret);251 goto err_mod_entry;252 }253 was_loaded = true;254 }255 256 ret = avs_ipc_init_instance(adev, module_id, id, ppl_instance_id,257 core_id, domain, param, param_size);258 if (ret) {259 ret = AVS_IPC_RET(ret);260 goto err_ipc;261 }262 263 *instance_id = id;264 return 0;265 266err_ipc:267 if (was_loaded)268 avs_dsp_op(adev, transfer_mods, false, &mentry, 1);269 avs_dsp_put_core(adev, core_id);270err_mod_entry:271 avs_module_id_free(adev, module_id, id);272 return ret;273}274 275void avs_dsp_delete_module(struct avs_dev *adev, u16 module_id, u8 instance_id,276 u8 ppl_instance_id, u8 core_id)277{278 struct avs_module_entry mentry;279 int ret;280 281 /* Modules not owned by any pipeline need to be freed explicitly. */282 if (ppl_instance_id == INVALID_PIPELINE_ID)283 avs_ipc_delete_instance(adev, module_id, instance_id);284 285 avs_module_id_free(adev, module_id, instance_id);286 287 ret = avs_get_module_id_entry(adev, module_id, &mentry);288 /* Unload occupied memory if this was the last instance. */289 if (!ret && mentry.type.load_type == AVS_MODULE_LOAD_TYPE_LOADABLE) {290 if (avs_is_module_ida_empty(adev, module_id)) {291 ret = avs_dsp_op(adev, transfer_mods, false, &mentry, 1);292 if (ret)293 dev_err(adev->dev, "unload modules failed: %d\n", ret);294 }295 }296 297 avs_dsp_put_core(adev, core_id);298}299 300int avs_dsp_create_pipeline(struct avs_dev *adev, u16 req_size, u8 priority,301 bool lp, u16 attributes, u8 *instance_id)302{303 struct avs_fw_cfg *fw_cfg = &adev->fw_cfg;304 int ret, id;305 306 id = ida_alloc_max(&adev->ppl_ida, fw_cfg->max_ppl_count - 1, GFP_KERNEL);307 if (id < 0)308 return id;309 310 ret = avs_ipc_create_pipeline(adev, req_size, priority, id, lp, attributes);311 if (ret) {312 ida_free(&adev->ppl_ida, id);313 return AVS_IPC_RET(ret);314 }315 316 *instance_id = id;317 return 0;318}319 320int avs_dsp_delete_pipeline(struct avs_dev *adev, u8 instance_id)321{322 int ret;323 324 ret = avs_ipc_delete_pipeline(adev, instance_id);325 if (ret)326 ret = AVS_IPC_RET(ret);327 328 ida_free(&adev->ppl_ida, instance_id);329 return ret;330}331