303 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2//3// Copyright(c) 2021-2022 Intel Corporation4//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. */16static 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. */32static 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 47int 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 61int 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 75int 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 84bool 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. */100static 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. */112static int113avs_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 else151 avs_module_ida_destroy(adev);152 153 adev->mod_idas = ida_ptrs;154 return 0;155}156 157int 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 178exit:179 mutex_unlock(&adev->modres_mutex);180 return ret;181}182 183void 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 194int 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);208exit:209 mutex_unlock(&adev->modres_mutex);210 return ret;211}212 213void 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);226exit:227 mutex_unlock(&adev->modres_mutex);228}229 230/*231 * Once driver loads FW it should keep it in memory, so we are not affected232 * by FW removal from filesystem or even worse by loading different FW at233 * runtime suspend/resume.234 */235int 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_const(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_const(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 calling275 * avs_request_firmware()276 */277void 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_const(entry->name);286 kfree(entry);287}288 289/*290 * Release all FW entries, used on driver removal291 */292void 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_const(entry->name);300 kfree(entry);301 }302}303