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