19fe51c55SCezary Rojewski // SPDX-License-Identifier: GPL-2.0-only
29fe51c55SCezary Rojewski //
39fe51c55SCezary Rojewski // Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
49fe51c55SCezary Rojewski //
59fe51c55SCezary Rojewski // Authors: Cezary Rojewski <cezary.rojewski@intel.com>
69fe51c55SCezary Rojewski // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
79fe51c55SCezary Rojewski //
89fe51c55SCezary Rojewski
99fe51c55SCezary Rojewski #include <sound/hdaudio_ext.h>
109fe51c55SCezary Rojewski #include "avs.h"
119fe51c55SCezary Rojewski #include "registers.h"
1269b23b39SCezary Rojewski #include "trace.h"
139fe51c55SCezary Rojewski
149fe51c55SCezary Rojewski #define AVS_ADSPCS_INTERVAL_US 500
159fe51c55SCezary Rojewski #define AVS_ADSPCS_TIMEOUT_US 50000
168192d24cSCezary Rojewski #define AVS_ADSPCS_DELAY_US 1000
179fe51c55SCezary Rojewski
avs_dsp_core_power(struct avs_dev * adev,u32 core_mask,bool power)189fe51c55SCezary Rojewski int avs_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool power)
199fe51c55SCezary Rojewski {
209fe51c55SCezary Rojewski u32 value, mask, reg;
219fe51c55SCezary Rojewski int ret;
229fe51c55SCezary Rojewski
2369b23b39SCezary Rojewski value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS);
2469b23b39SCezary Rojewski trace_avs_dsp_core_op(value, core_mask, "power", power);
2569b23b39SCezary Rojewski
269fe51c55SCezary Rojewski mask = AVS_ADSPCS_SPA_MASK(core_mask);
279fe51c55SCezary Rojewski value = power ? mask : 0;
289fe51c55SCezary Rojewski
299fe51c55SCezary Rojewski snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value);
308192d24cSCezary Rojewski /* Delay the polling to avoid false positives. */
318192d24cSCezary Rojewski usleep_range(AVS_ADSPCS_DELAY_US, 2 * AVS_ADSPCS_DELAY_US);
329fe51c55SCezary Rojewski
339fe51c55SCezary Rojewski mask = AVS_ADSPCS_CPA_MASK(core_mask);
349fe51c55SCezary Rojewski value = power ? mask : 0;
359fe51c55SCezary Rojewski
369fe51c55SCezary Rojewski ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS,
379fe51c55SCezary Rojewski reg, (reg & mask) == value,
389fe51c55SCezary Rojewski AVS_ADSPCS_INTERVAL_US,
399fe51c55SCezary Rojewski AVS_ADSPCS_TIMEOUT_US);
409fe51c55SCezary Rojewski if (ret)
419fe51c55SCezary Rojewski dev_err(adev->dev, "core_mask %d power %s failed: %d\n",
429fe51c55SCezary Rojewski core_mask, power ? "on" : "off", ret);
439fe51c55SCezary Rojewski
449fe51c55SCezary Rojewski return ret;
459fe51c55SCezary Rojewski }
469fe51c55SCezary Rojewski
avs_dsp_core_reset(struct avs_dev * adev,u32 core_mask,bool reset)479fe51c55SCezary Rojewski int avs_dsp_core_reset(struct avs_dev *adev, u32 core_mask, bool reset)
489fe51c55SCezary Rojewski {
499fe51c55SCezary Rojewski u32 value, mask, reg;
509fe51c55SCezary Rojewski int ret;
519fe51c55SCezary Rojewski
5269b23b39SCezary Rojewski value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS);
5369b23b39SCezary Rojewski trace_avs_dsp_core_op(value, core_mask, "reset", reset);
5469b23b39SCezary Rojewski
559fe51c55SCezary Rojewski mask = AVS_ADSPCS_CRST_MASK(core_mask);
569fe51c55SCezary Rojewski value = reset ? mask : 0;
579fe51c55SCezary Rojewski
589fe51c55SCezary Rojewski snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value);
599fe51c55SCezary Rojewski
609fe51c55SCezary Rojewski ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS,
619fe51c55SCezary Rojewski reg, (reg & mask) == value,
629fe51c55SCezary Rojewski AVS_ADSPCS_INTERVAL_US,
639fe51c55SCezary Rojewski AVS_ADSPCS_TIMEOUT_US);
649fe51c55SCezary Rojewski if (ret)
659fe51c55SCezary Rojewski dev_err(adev->dev, "core_mask %d %s reset failed: %d\n",
669fe51c55SCezary Rojewski core_mask, reset ? "enter" : "exit", ret);
679fe51c55SCezary Rojewski
689fe51c55SCezary Rojewski return ret;
699fe51c55SCezary Rojewski }
709fe51c55SCezary Rojewski
avs_dsp_core_stall(struct avs_dev * adev,u32 core_mask,bool stall)719fe51c55SCezary Rojewski int avs_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stall)
729fe51c55SCezary Rojewski {
739fe51c55SCezary Rojewski u32 value, mask, reg;
749fe51c55SCezary Rojewski int ret;
759fe51c55SCezary Rojewski
7669b23b39SCezary Rojewski value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS);
7769b23b39SCezary Rojewski trace_avs_dsp_core_op(value, core_mask, "stall", stall);
7869b23b39SCezary Rojewski
799fe51c55SCezary Rojewski mask = AVS_ADSPCS_CSTALL_MASK(core_mask);
809fe51c55SCezary Rojewski value = stall ? mask : 0;
819fe51c55SCezary Rojewski
829fe51c55SCezary Rojewski snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value);
839fe51c55SCezary Rojewski
849fe51c55SCezary Rojewski ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS,
859fe51c55SCezary Rojewski reg, (reg & mask) == value,
869fe51c55SCezary Rojewski AVS_ADSPCS_INTERVAL_US,
879fe51c55SCezary Rojewski AVS_ADSPCS_TIMEOUT_US);
888192d24cSCezary Rojewski if (ret) {
899fe51c55SCezary Rojewski dev_err(adev->dev, "core_mask %d %sstall failed: %d\n",
909fe51c55SCezary Rojewski core_mask, stall ? "" : "un", ret);
919fe51c55SCezary Rojewski return ret;
929fe51c55SCezary Rojewski }
939fe51c55SCezary Rojewski
948192d24cSCezary Rojewski /* Give HW time to propagate the change. */
958192d24cSCezary Rojewski usleep_range(AVS_ADSPCS_DELAY_US, 2 * AVS_ADSPCS_DELAY_US);
968192d24cSCezary Rojewski return 0;
978192d24cSCezary Rojewski }
988192d24cSCezary Rojewski
avs_dsp_core_enable(struct avs_dev * adev,u32 core_mask)999fe51c55SCezary Rojewski int avs_dsp_core_enable(struct avs_dev *adev, u32 core_mask)
1009fe51c55SCezary Rojewski {
1019fe51c55SCezary Rojewski int ret;
1029fe51c55SCezary Rojewski
1039fe51c55SCezary Rojewski ret = avs_dsp_op(adev, power, core_mask, true);
1049fe51c55SCezary Rojewski if (ret)
1059fe51c55SCezary Rojewski return ret;
1069fe51c55SCezary Rojewski
1079fe51c55SCezary Rojewski ret = avs_dsp_op(adev, reset, core_mask, false);
1089fe51c55SCezary Rojewski if (ret)
1099fe51c55SCezary Rojewski return ret;
1109fe51c55SCezary Rojewski
1119fe51c55SCezary Rojewski return avs_dsp_op(adev, stall, core_mask, false);
1129fe51c55SCezary Rojewski }
1139fe51c55SCezary Rojewski
avs_dsp_core_disable(struct avs_dev * adev,u32 core_mask)1149fe51c55SCezary Rojewski int avs_dsp_core_disable(struct avs_dev *adev, u32 core_mask)
1159fe51c55SCezary Rojewski {
1169fe51c55SCezary Rojewski /* No error checks to allow for complete DSP shutdown. */
1179fe51c55SCezary Rojewski avs_dsp_op(adev, stall, core_mask, true);
1189fe51c55SCezary Rojewski avs_dsp_op(adev, reset, core_mask, true);
1199fe51c55SCezary Rojewski
1209fe51c55SCezary Rojewski return avs_dsp_op(adev, power, core_mask, false);
1219fe51c55SCezary Rojewski }
1229fe51c55SCezary Rojewski
avs_dsp_enable(struct avs_dev * adev,u32 core_mask)123215e67b2SCezary Rojewski static int avs_dsp_enable(struct avs_dev *adev, u32 core_mask)
124215e67b2SCezary Rojewski {
125215e67b2SCezary Rojewski u32 mask;
126215e67b2SCezary Rojewski int ret;
127215e67b2SCezary Rojewski
128215e67b2SCezary Rojewski ret = avs_dsp_core_enable(adev, core_mask);
129215e67b2SCezary Rojewski if (ret < 0)
130215e67b2SCezary Rojewski return ret;
131215e67b2SCezary Rojewski
132215e67b2SCezary Rojewski mask = core_mask & ~AVS_MAIN_CORE_MASK;
133215e67b2SCezary Rojewski if (!mask)
134215e67b2SCezary Rojewski /*
135215e67b2SCezary Rojewski * without main core, fw is dead anyway
136215e67b2SCezary Rojewski * so setting D0 for it is futile.
137215e67b2SCezary Rojewski */
138215e67b2SCezary Rojewski return 0;
139215e67b2SCezary Rojewski
140215e67b2SCezary Rojewski ret = avs_ipc_set_dx(adev, mask, true);
141215e67b2SCezary Rojewski return AVS_IPC_RET(ret);
142215e67b2SCezary Rojewski }
143215e67b2SCezary Rojewski
avs_dsp_disable(struct avs_dev * adev,u32 core_mask)144215e67b2SCezary Rojewski static int avs_dsp_disable(struct avs_dev *adev, u32 core_mask)
145215e67b2SCezary Rojewski {
146215e67b2SCezary Rojewski int ret;
147215e67b2SCezary Rojewski
148215e67b2SCezary Rojewski ret = avs_ipc_set_dx(adev, core_mask, false);
149215e67b2SCezary Rojewski if (ret)
150215e67b2SCezary Rojewski return AVS_IPC_RET(ret);
151215e67b2SCezary Rojewski
152215e67b2SCezary Rojewski return avs_dsp_core_disable(adev, core_mask);
153215e67b2SCezary Rojewski }
154215e67b2SCezary Rojewski
avs_dsp_get_core(struct avs_dev * adev,u32 core_id)155215e67b2SCezary Rojewski static int avs_dsp_get_core(struct avs_dev *adev, u32 core_id)
156215e67b2SCezary Rojewski {
157215e67b2SCezary Rojewski u32 mask;
158215e67b2SCezary Rojewski int ret;
159215e67b2SCezary Rojewski
160215e67b2SCezary Rojewski mask = BIT_MASK(core_id);
161215e67b2SCezary Rojewski if (mask == AVS_MAIN_CORE_MASK)
162215e67b2SCezary Rojewski /* nothing to do for main core */
163215e67b2SCezary Rojewski return 0;
164215e67b2SCezary Rojewski if (core_id >= adev->hw_cfg.dsp_cores) {
165215e67b2SCezary Rojewski ret = -EINVAL;
166215e67b2SCezary Rojewski goto err;
167215e67b2SCezary Rojewski }
168215e67b2SCezary Rojewski
169215e67b2SCezary Rojewski adev->core_refs[core_id]++;
170215e67b2SCezary Rojewski if (adev->core_refs[core_id] == 1) {
171335c4cbdSCezary Rojewski /*
172335c4cbdSCezary Rojewski * No cores other than main-core can be running for DSP
173335c4cbdSCezary Rojewski * to achieve d0ix. Conscious SET_D0IX IPC failure is permitted,
174335c4cbdSCezary Rojewski * simply d0ix power state will no longer be attempted.
175335c4cbdSCezary Rojewski */
176335c4cbdSCezary Rojewski ret = avs_dsp_disable_d0ix(adev);
177335c4cbdSCezary Rojewski if (ret && ret != -AVS_EIPC)
178335c4cbdSCezary Rojewski goto err_disable_d0ix;
179335c4cbdSCezary Rojewski
180215e67b2SCezary Rojewski ret = avs_dsp_enable(adev, mask);
181215e67b2SCezary Rojewski if (ret)
182215e67b2SCezary Rojewski goto err_enable_dsp;
183215e67b2SCezary Rojewski }
184215e67b2SCezary Rojewski
185215e67b2SCezary Rojewski return 0;
186215e67b2SCezary Rojewski
187215e67b2SCezary Rojewski err_enable_dsp:
188335c4cbdSCezary Rojewski avs_dsp_enable_d0ix(adev);
189335c4cbdSCezary Rojewski err_disable_d0ix:
190215e67b2SCezary Rojewski adev->core_refs[core_id]--;
191215e67b2SCezary Rojewski err:
192215e67b2SCezary Rojewski dev_err(adev->dev, "get core %d failed: %d\n", core_id, ret);
193215e67b2SCezary Rojewski return ret;
194215e67b2SCezary Rojewski }
195215e67b2SCezary Rojewski
avs_dsp_put_core(struct avs_dev * adev,u32 core_id)196215e67b2SCezary Rojewski static int avs_dsp_put_core(struct avs_dev *adev, u32 core_id)
197215e67b2SCezary Rojewski {
198215e67b2SCezary Rojewski u32 mask;
199215e67b2SCezary Rojewski int ret;
200215e67b2SCezary Rojewski
201215e67b2SCezary Rojewski mask = BIT_MASK(core_id);
202215e67b2SCezary Rojewski if (mask == AVS_MAIN_CORE_MASK)
203215e67b2SCezary Rojewski /* nothing to do for main core */
204215e67b2SCezary Rojewski return 0;
205215e67b2SCezary Rojewski if (core_id >= adev->hw_cfg.dsp_cores) {
206215e67b2SCezary Rojewski ret = -EINVAL;
207215e67b2SCezary Rojewski goto err;
208215e67b2SCezary Rojewski }
209215e67b2SCezary Rojewski
210215e67b2SCezary Rojewski adev->core_refs[core_id]--;
211215e67b2SCezary Rojewski if (!adev->core_refs[core_id]) {
212215e67b2SCezary Rojewski ret = avs_dsp_disable(adev, mask);
213215e67b2SCezary Rojewski if (ret)
214215e67b2SCezary Rojewski goto err;
215335c4cbdSCezary Rojewski
216335c4cbdSCezary Rojewski /* Match disable_d0ix in avs_dsp_get_core(). */
217335c4cbdSCezary Rojewski avs_dsp_enable_d0ix(adev);
218215e67b2SCezary Rojewski }
219215e67b2SCezary Rojewski
220215e67b2SCezary Rojewski return 0;
221215e67b2SCezary Rojewski err:
222215e67b2SCezary Rojewski dev_err(adev->dev, "put core %d failed: %d\n", core_id, ret);
223215e67b2SCezary Rojewski return ret;
224215e67b2SCezary Rojewski }
225215e67b2SCezary Rojewski
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,u8 * instance_id)226215e67b2SCezary Rojewski int avs_dsp_init_module(struct avs_dev *adev, u16 module_id, u8 ppl_instance_id,
227215e67b2SCezary Rojewski u8 core_id, u8 domain, void *param, u32 param_size,
228*320f4d86SAmadeusz Sławiński u8 *instance_id)
229215e67b2SCezary Rojewski {
230215e67b2SCezary Rojewski struct avs_module_entry mentry;
231b27f4523SCezary Rojewski bool was_loaded = false;
232215e67b2SCezary Rojewski int ret, id;
233215e67b2SCezary Rojewski
234215e67b2SCezary Rojewski id = avs_module_id_alloc(adev, module_id);
235215e67b2SCezary Rojewski if (id < 0)
236215e67b2SCezary Rojewski return id;
237215e67b2SCezary Rojewski
238215e67b2SCezary Rojewski ret = avs_get_module_id_entry(adev, module_id, &mentry);
239215e67b2SCezary Rojewski if (ret)
240215e67b2SCezary Rojewski goto err_mod_entry;
241215e67b2SCezary Rojewski
242215e67b2SCezary Rojewski ret = avs_dsp_get_core(adev, core_id);
243215e67b2SCezary Rojewski if (ret)
244215e67b2SCezary Rojewski goto err_mod_entry;
245215e67b2SCezary Rojewski
246b27f4523SCezary Rojewski /* Load code into memory if this is the first instance. */
247b27f4523SCezary Rojewski if (!id && !avs_module_entry_is_loaded(&mentry)) {
248b27f4523SCezary Rojewski ret = avs_dsp_op(adev, transfer_mods, true, &mentry, 1);
249b27f4523SCezary Rojewski if (ret) {
250b27f4523SCezary Rojewski dev_err(adev->dev, "load modules failed: %d\n", ret);
251b27f4523SCezary Rojewski goto err_mod_entry;
252b27f4523SCezary Rojewski }
253b27f4523SCezary Rojewski was_loaded = true;
254b27f4523SCezary Rojewski }
255b27f4523SCezary Rojewski
256215e67b2SCezary Rojewski ret = avs_ipc_init_instance(adev, module_id, id, ppl_instance_id,
257215e67b2SCezary Rojewski core_id, domain, param, param_size);
258215e67b2SCezary Rojewski if (ret) {
259215e67b2SCezary Rojewski ret = AVS_IPC_RET(ret);
260215e67b2SCezary Rojewski goto err_ipc;
261215e67b2SCezary Rojewski }
262215e67b2SCezary Rojewski
263215e67b2SCezary Rojewski *instance_id = id;
264215e67b2SCezary Rojewski return 0;
265215e67b2SCezary Rojewski
266215e67b2SCezary Rojewski err_ipc:
267b27f4523SCezary Rojewski if (was_loaded)
268b27f4523SCezary Rojewski avs_dsp_op(adev, transfer_mods, false, &mentry, 1);
269215e67b2SCezary Rojewski avs_dsp_put_core(adev, core_id);
270215e67b2SCezary Rojewski err_mod_entry:
271215e67b2SCezary Rojewski avs_module_id_free(adev, module_id, id);
272215e67b2SCezary Rojewski return ret;
273215e67b2SCezary Rojewski }
274215e67b2SCezary Rojewski
avs_dsp_delete_module(struct avs_dev * adev,u16 module_id,u8 instance_id,u8 ppl_instance_id,u8 core_id)275*320f4d86SAmadeusz Sławiński void avs_dsp_delete_module(struct avs_dev *adev, u16 module_id, u8 instance_id,
276215e67b2SCezary Rojewski u8 ppl_instance_id, u8 core_id)
277215e67b2SCezary Rojewski {
278b27f4523SCezary Rojewski struct avs_module_entry mentry;
279b27f4523SCezary Rojewski int ret;
280b27f4523SCezary Rojewski
281215e67b2SCezary Rojewski /* Modules not owned by any pipeline need to be freed explicitly. */
282215e67b2SCezary Rojewski if (ppl_instance_id == INVALID_PIPELINE_ID)
283215e67b2SCezary Rojewski avs_ipc_delete_instance(adev, module_id, instance_id);
284215e67b2SCezary Rojewski
285215e67b2SCezary Rojewski avs_module_id_free(adev, module_id, instance_id);
286215e67b2SCezary Rojewski
287b27f4523SCezary Rojewski ret = avs_get_module_id_entry(adev, module_id, &mentry);
288b27f4523SCezary Rojewski /* Unload occupied memory if this was the last instance. */
289b27f4523SCezary Rojewski if (!ret && mentry.type.load_type == AVS_MODULE_LOAD_TYPE_LOADABLE) {
290b27f4523SCezary Rojewski if (avs_is_module_ida_empty(adev, module_id)) {
291b27f4523SCezary Rojewski ret = avs_dsp_op(adev, transfer_mods, false, &mentry, 1);
292b27f4523SCezary Rojewski if (ret)
293b27f4523SCezary Rojewski dev_err(adev->dev, "unload modules failed: %d\n", ret);
294b27f4523SCezary Rojewski }
295b27f4523SCezary Rojewski }
296b27f4523SCezary Rojewski
297215e67b2SCezary Rojewski avs_dsp_put_core(adev, core_id);
298215e67b2SCezary Rojewski }
299215e67b2SCezary Rojewski
avs_dsp_create_pipeline(struct avs_dev * adev,u16 req_size,u8 priority,bool lp,u16 attributes,u8 * instance_id)300215e67b2SCezary Rojewski int avs_dsp_create_pipeline(struct avs_dev *adev, u16 req_size, u8 priority,
301215e67b2SCezary Rojewski bool lp, u16 attributes, u8 *instance_id)
302215e67b2SCezary Rojewski {
303215e67b2SCezary Rojewski struct avs_fw_cfg *fw_cfg = &adev->fw_cfg;
304215e67b2SCezary Rojewski int ret, id;
305215e67b2SCezary Rojewski
306215e67b2SCezary Rojewski id = ida_alloc_max(&adev->ppl_ida, fw_cfg->max_ppl_count - 1, GFP_KERNEL);
307215e67b2SCezary Rojewski if (id < 0)
308215e67b2SCezary Rojewski return id;
309215e67b2SCezary Rojewski
310215e67b2SCezary Rojewski ret = avs_ipc_create_pipeline(adev, req_size, priority, id, lp, attributes);
311215e67b2SCezary Rojewski if (ret) {
312215e67b2SCezary Rojewski ida_free(&adev->ppl_ida, id);
313215e67b2SCezary Rojewski return AVS_IPC_RET(ret);
314215e67b2SCezary Rojewski }
315215e67b2SCezary Rojewski
316215e67b2SCezary Rojewski *instance_id = id;
317215e67b2SCezary Rojewski return 0;
318215e67b2SCezary Rojewski }
319215e67b2SCezary Rojewski
avs_dsp_delete_pipeline(struct avs_dev * adev,u8 instance_id)320215e67b2SCezary Rojewski int avs_dsp_delete_pipeline(struct avs_dev *adev, u8 instance_id)
321215e67b2SCezary Rojewski {
322215e67b2SCezary Rojewski int ret;
323215e67b2SCezary Rojewski
324215e67b2SCezary Rojewski ret = avs_ipc_delete_pipeline(adev, instance_id);
325215e67b2SCezary Rojewski if (ret)
326215e67b2SCezary Rojewski ret = AVS_IPC_RET(ret);
327215e67b2SCezary Rojewski
328215e67b2SCezary Rojewski ida_free(&adev->ppl_ida, instance_id);
329215e67b2SCezary Rojewski return ret;
330215e67b2SCezary Rojewski }
331