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