xref: /openbmc/linux/sound/soc/intel/avs/utils.c (revision 8ffd3d5e)
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/firmware.h>
10 #include <linux/kfifo.h>
11 #include <linux/slab.h>
12 #include "avs.h"
13 #include "messages.h"
14 
15 /* Caller responsible for holding adev->modres_mutex. */
16 static int avs_module_entry_index(struct avs_dev *adev, const guid_t *uuid)
17 {
18 	int i;
19 
20 	for (i = 0; i < adev->mods_info->count; i++) {
21 		struct avs_module_entry *module;
22 
23 		module = &adev->mods_info->entries[i];
24 		if (guid_equal(&module->uuid, uuid))
25 			return i;
26 	}
27 
28 	return -ENOENT;
29 }
30 
31 /* Caller responsible for holding adev->modres_mutex. */
32 static int avs_module_id_entry_index(struct avs_dev *adev, u32 module_id)
33 {
34 	int i;
35 
36 	for (i = 0; i < adev->mods_info->count; i++) {
37 		struct avs_module_entry *module;
38 
39 		module = &adev->mods_info->entries[i];
40 		if (module->module_id == module_id)
41 			return i;
42 	}
43 
44 	return -ENOENT;
45 }
46 
47 int avs_get_module_entry(struct avs_dev *adev, const guid_t *uuid, struct avs_module_entry *entry)
48 {
49 	int idx;
50 
51 	mutex_lock(&adev->modres_mutex);
52 
53 	idx = avs_module_entry_index(adev, uuid);
54 	if (idx >= 0)
55 		memcpy(entry, &adev->mods_info->entries[idx], sizeof(*entry));
56 
57 	mutex_unlock(&adev->modres_mutex);
58 	return (idx < 0) ? idx : 0;
59 }
60 
61 int avs_get_module_id_entry(struct avs_dev *adev, u32 module_id, struct avs_module_entry *entry)
62 {
63 	int idx;
64 
65 	mutex_lock(&adev->modres_mutex);
66 
67 	idx = avs_module_id_entry_index(adev, module_id);
68 	if (idx >= 0)
69 		memcpy(entry, &adev->mods_info->entries[idx], sizeof(*entry));
70 
71 	mutex_unlock(&adev->modres_mutex);
72 	return (idx < 0) ? idx : 0;
73 }
74 
75 int avs_get_module_id(struct avs_dev *adev, const guid_t *uuid)
76 {
77 	struct avs_module_entry module;
78 	int ret;
79 
80 	ret = avs_get_module_entry(adev, uuid, &module);
81 	return !ret ? module.module_id : -ENOENT;
82 }
83 
84 bool avs_is_module_ida_empty(struct avs_dev *adev, u32 module_id)
85 {
86 	bool ret = false;
87 	int idx;
88 
89 	mutex_lock(&adev->modres_mutex);
90 
91 	idx = avs_module_id_entry_index(adev, module_id);
92 	if (idx >= 0)
93 		ret = ida_is_empty(adev->mod_idas[idx]);
94 
95 	mutex_unlock(&adev->modres_mutex);
96 	return ret;
97 }
98 
99 /* Caller responsible for holding adev->modres_mutex. */
100 static void avs_module_ida_destroy(struct avs_dev *adev)
101 {
102 	int i = adev->mods_info ? adev->mods_info->count : 0;
103 
104 	while (i--) {
105 		ida_destroy(adev->mod_idas[i]);
106 		kfree(adev->mod_idas[i]);
107 	}
108 	kfree(adev->mod_idas);
109 }
110 
111 /* Caller responsible for holding adev->modres_mutex. */
112 static int
113 avs_module_ida_alloc(struct avs_dev *adev, struct avs_mods_info *newinfo, bool purge)
114 {
115 	struct avs_mods_info *oldinfo = adev->mods_info;
116 	struct ida **ida_ptrs;
117 	u32 tocopy_count = 0;
118 	int i;
119 
120 	if (!purge && oldinfo) {
121 		if (oldinfo->count >= newinfo->count)
122 			dev_warn(adev->dev, "refreshing %d modules info with %d\n",
123 				 oldinfo->count, newinfo->count);
124 		tocopy_count = oldinfo->count;
125 	}
126 
127 	ida_ptrs = kcalloc(newinfo->count, sizeof(*ida_ptrs), GFP_KERNEL);
128 	if (!ida_ptrs)
129 		return -ENOMEM;
130 
131 	if (tocopy_count)
132 		memcpy(ida_ptrs, adev->mod_idas, tocopy_count * sizeof(*ida_ptrs));
133 
134 	for (i = tocopy_count; i < newinfo->count; i++) {
135 		ida_ptrs[i] = kzalloc(sizeof(**ida_ptrs), GFP_KERNEL);
136 		if (!ida_ptrs[i]) {
137 			while (i--)
138 				kfree(ida_ptrs[i]);
139 
140 			kfree(ida_ptrs);
141 			return -ENOMEM;
142 		}
143 
144 		ida_init(ida_ptrs[i]);
145 	}
146 
147 	/* If old elements have been reused, don't wipe them. */
148 	if (tocopy_count)
149 		kfree(adev->mod_idas);
150 	else
151 		avs_module_ida_destroy(adev);
152 
153 	adev->mod_idas = ida_ptrs;
154 	return 0;
155 }
156 
157 int avs_module_info_init(struct avs_dev *adev, bool purge)
158 {
159 	struct avs_mods_info *info;
160 	int ret;
161 
162 	ret = avs_ipc_get_modules_info(adev, &info);
163 	if (ret)
164 		return AVS_IPC_RET(ret);
165 
166 	mutex_lock(&adev->modres_mutex);
167 
168 	ret = avs_module_ida_alloc(adev, info, purge);
169 	if (ret < 0) {
170 		dev_err(adev->dev, "initialize module idas failed: %d\n", ret);
171 		goto exit;
172 	}
173 
174 	/* Refresh current information with newly received table. */
175 	kfree(adev->mods_info);
176 	adev->mods_info = info;
177 
178 exit:
179 	mutex_unlock(&adev->modres_mutex);
180 	return ret;
181 }
182 
183 void avs_module_info_free(struct avs_dev *adev)
184 {
185 	mutex_lock(&adev->modres_mutex);
186 
187 	avs_module_ida_destroy(adev);
188 	kfree(adev->mods_info);
189 	adev->mods_info = NULL;
190 
191 	mutex_unlock(&adev->modres_mutex);
192 }
193 
194 int avs_module_id_alloc(struct avs_dev *adev, u16 module_id)
195 {
196 	int ret, idx, max_id;
197 
198 	mutex_lock(&adev->modres_mutex);
199 
200 	idx = avs_module_id_entry_index(adev, module_id);
201 	if (idx == -ENOENT) {
202 		dev_err(adev->dev, "invalid module id: %d", module_id);
203 		ret = -EINVAL;
204 		goto exit;
205 	}
206 	max_id = adev->mods_info->entries[idx].instance_max_count - 1;
207 	ret = ida_alloc_max(adev->mod_idas[idx], max_id, GFP_KERNEL);
208 exit:
209 	mutex_unlock(&adev->modres_mutex);
210 	return ret;
211 }
212 
213 void avs_module_id_free(struct avs_dev *adev, u16 module_id, u8 instance_id)
214 {
215 	int idx;
216 
217 	mutex_lock(&adev->modres_mutex);
218 
219 	idx = avs_module_id_entry_index(adev, module_id);
220 	if (idx == -ENOENT) {
221 		dev_err(adev->dev, "invalid module id: %d", module_id);
222 		goto exit;
223 	}
224 
225 	ida_free(adev->mod_idas[idx], instance_id);
226 exit:
227 	mutex_unlock(&adev->modres_mutex);
228 }
229 
230 /*
231  * Once driver loads FW it should keep it in memory, so we are not affected
232  * by FW removal from filesystem or even worse by loading different FW at
233  * runtime suspend/resume.
234  */
235 int avs_request_firmware(struct avs_dev *adev, const struct firmware **fw_p, const char *name)
236 {
237 	struct avs_fw_entry *entry;
238 	int ret;
239 
240 	/* first check in list if it is not already loaded */
241 	list_for_each_entry(entry, &adev->fw_list, node) {
242 		if (!strcmp(name, entry->name)) {
243 			*fw_p = entry->fw;
244 			return 0;
245 		}
246 	}
247 
248 	/* FW is not loaded, let's load it now and add to the list */
249 	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
250 	if (!entry)
251 		return -ENOMEM;
252 
253 	entry->name = kstrdup(name, GFP_KERNEL);
254 	if (!entry->name) {
255 		kfree(entry);
256 		return -ENOMEM;
257 	}
258 
259 	ret = request_firmware(&entry->fw, name, adev->dev);
260 	if (ret < 0) {
261 		kfree(entry->name);
262 		kfree(entry);
263 		return ret;
264 	}
265 
266 	*fw_p = entry->fw;
267 
268 	list_add_tail(&entry->node, &adev->fw_list);
269 
270 	return 0;
271 }
272 
273 /*
274  * Release single FW entry, used to handle errors in functions calling
275  * avs_request_firmware()
276  */
277 void avs_release_last_firmware(struct avs_dev *adev)
278 {
279 	struct avs_fw_entry *entry;
280 
281 	entry = list_last_entry(&adev->fw_list, typeof(*entry), node);
282 
283 	list_del(&entry->node);
284 	release_firmware(entry->fw);
285 	kfree(entry->name);
286 	kfree(entry);
287 }
288 
289 /*
290  * Release all FW entries, used on driver removal
291  */
292 void avs_release_firmwares(struct avs_dev *adev)
293 {
294 	struct avs_fw_entry *entry, *tmp;
295 
296 	list_for_each_entry_safe(entry, tmp, &adev->fw_list, node) {
297 		list_del(&entry->node);
298 		release_firmware(entry->fw);
299 		kfree(entry->name);
300 		kfree(entry);
301 	}
302 }
303