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