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 
avs_dsp_core_power(struct avs_dev * adev,u32 core_mask,bool power)17 int 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 
avs_dsp_core_reset(struct avs_dev * adev,u32 core_mask,bool reset)44 int 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 
avs_dsp_core_stall(struct avs_dev * adev,u32 core_mask,bool stall)68 int 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 
avs_dsp_core_enable(struct avs_dev * adev,u32 core_mask)92 int 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 
avs_dsp_core_disable(struct avs_dev * adev,u32 core_mask)107 int 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 
avs_dsp_enable(struct avs_dev * adev,u32 core_mask)116 static 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 
avs_dsp_disable(struct avs_dev * adev,u32 core_mask)137 static 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 
avs_dsp_get_core(struct avs_dev * adev,u32 core_id)148 static 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 
180 err_enable_dsp:
181 	avs_dsp_enable_d0ix(adev);
182 err_disable_d0ix:
183 	adev->core_refs[core_id]--;
184 err:
185 	dev_err(adev->dev, "get core %d failed: %d\n", core_id, ret);
186 	return ret;
187 }
188 
avs_dsp_put_core(struct avs_dev * adev,u32 core_id)189 static 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;
214 err:
215 	dev_err(adev->dev, "put core %d failed: %d\n", core_id, ret);
216 	return ret;
217 }
218 
avs_dsp_init_module(struct avs_dev * adev,u16 module_id,u8 ppl_instance_id,u8 core_id,u8 domain,void * param,u32 param_size,u16 * instance_id)219 int 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 
259 err_ipc:
260 	if (was_loaded)
261 		avs_dsp_op(adev, transfer_mods, false, &mentry, 1);
262 	avs_dsp_put_core(adev, core_id);
263 err_mod_entry:
264 	avs_module_id_free(adev, module_id, id);
265 	return ret;
266 }
267 
avs_dsp_delete_module(struct avs_dev * adev,u16 module_id,u16 instance_id,u8 ppl_instance_id,u8 core_id)268 void 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 
avs_dsp_create_pipeline(struct avs_dev * adev,u16 req_size,u8 priority,bool lp,u16 attributes,u8 * instance_id)293 int 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 
avs_dsp_delete_pipeline(struct avs_dev * adev,u8 instance_id)313 int 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 }
324