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