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