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