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