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