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 | #define AVS_ADSPCS_DELAY_US 1000 |
17 | |
18 | int 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(reg: value, mask: core_mask, op: "power" , flag: 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, max: 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 | |
47 | int 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(reg: value, mask: core_mask, op: "reset" , flag: 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 | |
71 | int 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(reg: value, mask: core_mask, op: "stall" , flag: 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, max: 2 * AVS_ADSPCS_DELAY_US); |
96 | return 0; |
97 | } |
98 | |
99 | int 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 | |
114 | int 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 | |
123 | static 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 anyway |
136 | * so setting D0 for it is futile. |
137 | */ |
138 | return 0; |
139 | |
140 | ret = avs_ipc_set_dx(adev, core_mask: mask, powerup: true); |
141 | return AVS_IPC_RET(ret); |
142 | } |
143 | |
144 | static 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, powerup: false); |
149 | if (ret) |
150 | return AVS_IPC_RET(ret); |
151 | |
152 | return avs_dsp_core_disable(adev, core_mask); |
153 | } |
154 | |
155 | static 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 DSP |
173 | * 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, core_mask: mask); |
181 | if (ret) |
182 | goto err_enable_dsp; |
183 | } |
184 | |
185 | return 0; |
186 | |
187 | err_enable_dsp: |
188 | avs_dsp_enable_d0ix(adev); |
189 | err_disable_d0ix: |
190 | adev->core_refs[core_id]--; |
191 | err: |
192 | dev_err(adev->dev, "get core %d failed: %d\n" , core_id, ret); |
193 | return ret; |
194 | } |
195 | |
196 | static 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, core_mask: 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; |
221 | err: |
222 | dev_err(adev->dev, "put core %d failed: %d\n" , core_id, ret); |
223 | return ret; |
224 | } |
225 | |
226 | int 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, entry: &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: &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, instance_id: id, ppl_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 | |
266 | err_ipc: |
267 | if (was_loaded) |
268 | avs_dsp_op(adev, transfer_mods, false, &mentry, 1); |
269 | avs_dsp_put_core(adev, core_id); |
270 | err_mod_entry: |
271 | avs_module_id_free(adev, module_id, instance_id: id); |
272 | return ret; |
273 | } |
274 | |
275 | void 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, entry: &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 | |
300 | int 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(ida: &adev->ppl_ida, max: 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, instance_id: 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 | |
320 | int 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, id: instance_id); |
329 | return ret; |
330 | } |
331 | |