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