brintos

brintos / linux-shallow public Read only

0
0
Text · 6.6 KiB · 81f9b67 Raw
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