GNU Linux-libre 5.19-rc6-gnu
[releases.git] / sound / soc / intel / avs / dsp.c
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }