162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license. When using or 462306a36Sopenharmony_ci// redistributing this file, you may do so under either license. 562306a36Sopenharmony_ci// 662306a36Sopenharmony_ci// Copyright(c) 2022 Intel Corporation. All rights reserved. 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/firmware.h> 962306a36Sopenharmony_ci#include <sound/sof/ext_manifest4.h> 1062306a36Sopenharmony_ci#include <sound/sof/ipc4/header.h> 1162306a36Sopenharmony_ci#include <trace/events/sof.h> 1262306a36Sopenharmony_ci#include "ipc4-priv.h" 1362306a36Sopenharmony_ci#include "sof-audio.h" 1462306a36Sopenharmony_ci#include "sof-priv.h" 1562306a36Sopenharmony_ci#include "ops.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* The module ID includes the id of the library it is part of at offset 12 */ 1862306a36Sopenharmony_ci#define SOF_IPC4_MOD_LIB_ID_SHIFT 12 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic ssize_t sof_ipc4_fw_parse_ext_man(struct snd_sof_dev *sdev, 2162306a36Sopenharmony_ci struct sof_ipc4_fw_library *fw_lib) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci struct sof_ipc4_fw_data *ipc4_data = sdev->private; 2462306a36Sopenharmony_ci const struct firmware *fw = fw_lib->sof_fw.fw; 2562306a36Sopenharmony_ci struct sof_man4_fw_binary_header *fw_header; 2662306a36Sopenharmony_ci struct sof_ext_manifest4_hdr *ext_man_hdr; 2762306a36Sopenharmony_ci struct sof_man4_module_config *fm_config; 2862306a36Sopenharmony_ci struct sof_ipc4_fw_module *fw_module; 2962306a36Sopenharmony_ci struct sof_man4_module *fm_entry; 3062306a36Sopenharmony_ci ssize_t remaining; 3162306a36Sopenharmony_ci u32 fw_hdr_offset; 3262306a36Sopenharmony_ci int i; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci if (!ipc4_data) { 3562306a36Sopenharmony_ci dev_err(sdev->dev, "%s: ipc4_data is not available\n", __func__); 3662306a36Sopenharmony_ci return -EINVAL; 3762306a36Sopenharmony_ci } 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci remaining = fw->size; 4062306a36Sopenharmony_ci if (remaining <= sizeof(*ext_man_hdr)) { 4162306a36Sopenharmony_ci dev_err(sdev->dev, "Firmware size is too small: %zu\n", remaining); 4262306a36Sopenharmony_ci return -EINVAL; 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci ext_man_hdr = (struct sof_ext_manifest4_hdr *)fw->data; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci /* 4862306a36Sopenharmony_ci * At the start of the firmware image we must have an extended manifest. 4962306a36Sopenharmony_ci * Verify that the magic number is correct. 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_ci if (ext_man_hdr->id != SOF_EXT_MAN4_MAGIC_NUMBER) { 5262306a36Sopenharmony_ci dev_err(sdev->dev, 5362306a36Sopenharmony_ci "Unexpected extended manifest magic number: %#x\n", 5462306a36Sopenharmony_ci ext_man_hdr->id); 5562306a36Sopenharmony_ci return -EINVAL; 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci fw_hdr_offset = ipc4_data->manifest_fw_hdr_offset; 5962306a36Sopenharmony_ci if (!fw_hdr_offset) 6062306a36Sopenharmony_ci return -EINVAL; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (remaining <= ext_man_hdr->len + fw_hdr_offset + sizeof(*fw_header)) { 6362306a36Sopenharmony_ci dev_err(sdev->dev, "Invalid firmware size %zu, should be at least %zu\n", 6462306a36Sopenharmony_ci remaining, ext_man_hdr->len + fw_hdr_offset + sizeof(*fw_header)); 6562306a36Sopenharmony_ci return -EINVAL; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci fw_header = (struct sof_man4_fw_binary_header *) 6962306a36Sopenharmony_ci (fw->data + ext_man_hdr->len + fw_hdr_offset); 7062306a36Sopenharmony_ci remaining -= (ext_man_hdr->len + fw_hdr_offset); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (remaining <= fw_header->len) { 7362306a36Sopenharmony_ci dev_err(sdev->dev, "Invalid fw_header->len %u\n", fw_header->len); 7462306a36Sopenharmony_ci return -EINVAL; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci dev_info(sdev->dev, "Loaded firmware library: %s, version: %u.%u.%u.%u\n", 7862306a36Sopenharmony_ci fw_header->name, fw_header->major_version, fw_header->minor_version, 7962306a36Sopenharmony_ci fw_header->hotfix_version, fw_header->build_version); 8062306a36Sopenharmony_ci dev_dbg(sdev->dev, "Header length: %u, module count: %u\n", 8162306a36Sopenharmony_ci fw_header->len, fw_header->num_module_entries); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci fw_lib->modules = devm_kmalloc_array(sdev->dev, fw_header->num_module_entries, 8462306a36Sopenharmony_ci sizeof(*fw_module), GFP_KERNEL); 8562306a36Sopenharmony_ci if (!fw_lib->modules) 8662306a36Sopenharmony_ci return -ENOMEM; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci fw_lib->name = fw_header->name; 8962306a36Sopenharmony_ci fw_lib->num_modules = fw_header->num_module_entries; 9062306a36Sopenharmony_ci fw_module = fw_lib->modules; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci fm_entry = (struct sof_man4_module *)((u8 *)fw_header + fw_header->len); 9362306a36Sopenharmony_ci remaining -= fw_header->len; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (remaining < fw_header->num_module_entries * sizeof(*fm_entry)) { 9662306a36Sopenharmony_ci dev_err(sdev->dev, "Invalid num_module_entries %u\n", 9762306a36Sopenharmony_ci fw_header->num_module_entries); 9862306a36Sopenharmony_ci return -EINVAL; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci fm_config = (struct sof_man4_module_config *) 10262306a36Sopenharmony_ci (fm_entry + fw_header->num_module_entries); 10362306a36Sopenharmony_ci remaining -= (fw_header->num_module_entries * sizeof(*fm_entry)); 10462306a36Sopenharmony_ci for (i = 0; i < fw_header->num_module_entries; i++) { 10562306a36Sopenharmony_ci memcpy(&fw_module->man4_module_entry, fm_entry, sizeof(*fm_entry)); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (fm_entry->cfg_count) { 10862306a36Sopenharmony_ci if (remaining < (fm_entry->cfg_offset + fm_entry->cfg_count) * 10962306a36Sopenharmony_ci sizeof(*fm_config)) { 11062306a36Sopenharmony_ci dev_err(sdev->dev, "Invalid module cfg_offset %u\n", 11162306a36Sopenharmony_ci fm_entry->cfg_offset); 11262306a36Sopenharmony_ci return -EINVAL; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci fw_module->fw_mod_cfg = &fm_config[fm_entry->cfg_offset]; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci dev_dbg(sdev->dev, 11862306a36Sopenharmony_ci "module %s: UUID %pUL cfg_count: %u, bss_size: %#x\n", 11962306a36Sopenharmony_ci fm_entry->name, &fm_entry->uuid, fm_entry->cfg_count, 12062306a36Sopenharmony_ci fm_config[fm_entry->cfg_offset].is_bytes); 12162306a36Sopenharmony_ci } else { 12262306a36Sopenharmony_ci dev_dbg(sdev->dev, "module %s: UUID %pUL\n", fm_entry->name, 12362306a36Sopenharmony_ci &fm_entry->uuid); 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci fw_module->man4_module_entry.id = i; 12762306a36Sopenharmony_ci ida_init(&fw_module->m_ida); 12862306a36Sopenharmony_ci fw_module->private = NULL; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci fw_module++; 13162306a36Sopenharmony_ci fm_entry++; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci return ext_man_hdr->len; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic size_t sof_ipc4_fw_parse_basefw_ext_man(struct snd_sof_dev *sdev) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci struct sof_ipc4_fw_data *ipc4_data = sdev->private; 14062306a36Sopenharmony_ci struct sof_ipc4_fw_library *fw_lib; 14162306a36Sopenharmony_ci ssize_t payload_offset; 14262306a36Sopenharmony_ci int ret; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci fw_lib = devm_kzalloc(sdev->dev, sizeof(*fw_lib), GFP_KERNEL); 14562306a36Sopenharmony_ci if (!fw_lib) 14662306a36Sopenharmony_ci return -ENOMEM; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci fw_lib->sof_fw.fw = sdev->basefw.fw; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci payload_offset = sof_ipc4_fw_parse_ext_man(sdev, fw_lib); 15162306a36Sopenharmony_ci if (payload_offset > 0) { 15262306a36Sopenharmony_ci fw_lib->sof_fw.payload_offset = payload_offset; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci /* basefw ID is 0 */ 15562306a36Sopenharmony_ci fw_lib->id = 0; 15662306a36Sopenharmony_ci ret = xa_insert(&ipc4_data->fw_lib_xa, 0, fw_lib, GFP_KERNEL); 15762306a36Sopenharmony_ci if (ret) 15862306a36Sopenharmony_ci return ret; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci return payload_offset; 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic int sof_ipc4_load_library_by_uuid(struct snd_sof_dev *sdev, 16562306a36Sopenharmony_ci unsigned long lib_id, const guid_t *uuid) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci struct sof_ipc4_fw_data *ipc4_data = sdev->private; 16862306a36Sopenharmony_ci struct sof_ipc4_fw_library *fw_lib; 16962306a36Sopenharmony_ci const char *fw_filename; 17062306a36Sopenharmony_ci ssize_t payload_offset; 17162306a36Sopenharmony_ci int ret, i, err; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (!sdev->pdata->fw_lib_prefix) { 17462306a36Sopenharmony_ci dev_err(sdev->dev, 17562306a36Sopenharmony_ci "Library loading is not supported due to not set library path\n"); 17662306a36Sopenharmony_ci return -EINVAL; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (!ipc4_data->load_library) { 18062306a36Sopenharmony_ci dev_err(sdev->dev, "Library loading is not supported on this platform\n"); 18162306a36Sopenharmony_ci return -EOPNOTSUPP; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci fw_lib = devm_kzalloc(sdev->dev, sizeof(*fw_lib), GFP_KERNEL); 18562306a36Sopenharmony_ci if (!fw_lib) 18662306a36Sopenharmony_ci return -ENOMEM; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci fw_filename = kasprintf(GFP_KERNEL, "%s/%pUL.bin", 18962306a36Sopenharmony_ci sdev->pdata->fw_lib_prefix, uuid); 19062306a36Sopenharmony_ci if (!fw_filename) { 19162306a36Sopenharmony_ci ret = -ENOMEM; 19262306a36Sopenharmony_ci goto free_fw_lib; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci ret = request_firmware(&fw_lib->sof_fw.fw, fw_filename, sdev->dev); 19662306a36Sopenharmony_ci if (ret < 0) { 19762306a36Sopenharmony_ci dev_err(sdev->dev, "Library file '%s' is missing\n", fw_filename); 19862306a36Sopenharmony_ci goto free_filename; 19962306a36Sopenharmony_ci } else { 20062306a36Sopenharmony_ci dev_dbg(sdev->dev, "Library file '%s' loaded\n", fw_filename); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci payload_offset = sof_ipc4_fw_parse_ext_man(sdev, fw_lib); 20462306a36Sopenharmony_ci if (payload_offset <= 0) { 20562306a36Sopenharmony_ci if (!payload_offset) 20662306a36Sopenharmony_ci ret = -EINVAL; 20762306a36Sopenharmony_ci else 20862306a36Sopenharmony_ci ret = payload_offset; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci goto release; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci fw_lib->sof_fw.payload_offset = payload_offset; 21462306a36Sopenharmony_ci fw_lib->id = lib_id; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci /* Fix up the module ID numbers within the library */ 21762306a36Sopenharmony_ci for (i = 0; i < fw_lib->num_modules; i++) 21862306a36Sopenharmony_ci fw_lib->modules[i].man4_module_entry.id |= (lib_id << SOF_IPC4_MOD_LIB_ID_SHIFT); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* 22162306a36Sopenharmony_ci * Make sure that the DSP is booted and stays up while attempting the 22262306a36Sopenharmony_ci * loading the library for the first time 22362306a36Sopenharmony_ci */ 22462306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(sdev->dev); 22562306a36Sopenharmony_ci if (ret < 0 && ret != -EACCES) { 22662306a36Sopenharmony_ci dev_err_ratelimited(sdev->dev, "%s: pm_runtime resume failed: %d\n", 22762306a36Sopenharmony_ci __func__, ret); 22862306a36Sopenharmony_ci goto release; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci ret = ipc4_data->load_library(sdev, fw_lib, false); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci pm_runtime_mark_last_busy(sdev->dev); 23462306a36Sopenharmony_ci err = pm_runtime_put_autosuspend(sdev->dev); 23562306a36Sopenharmony_ci if (err < 0) 23662306a36Sopenharmony_ci dev_err_ratelimited(sdev->dev, "%s: pm_runtime idle failed: %d\n", 23762306a36Sopenharmony_ci __func__, err); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (ret) 24062306a36Sopenharmony_ci goto release; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci ret = xa_insert(&ipc4_data->fw_lib_xa, lib_id, fw_lib, GFP_KERNEL); 24362306a36Sopenharmony_ci if (unlikely(ret)) 24462306a36Sopenharmony_ci goto release; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci kfree(fw_filename); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci return 0; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cirelease: 25162306a36Sopenharmony_ci release_firmware(fw_lib->sof_fw.fw); 25262306a36Sopenharmony_ci /* Allocated within sof_ipc4_fw_parse_ext_man() */ 25362306a36Sopenharmony_ci devm_kfree(sdev->dev, fw_lib->modules); 25462306a36Sopenharmony_cifree_filename: 25562306a36Sopenharmony_ci kfree(fw_filename); 25662306a36Sopenharmony_cifree_fw_lib: 25762306a36Sopenharmony_ci devm_kfree(sdev->dev, fw_lib); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci return ret; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistruct sof_ipc4_fw_module *sof_ipc4_find_module_by_uuid(struct snd_sof_dev *sdev, 26362306a36Sopenharmony_ci const guid_t *uuid) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci struct sof_ipc4_fw_data *ipc4_data = sdev->private; 26662306a36Sopenharmony_ci struct sof_ipc4_fw_library *fw_lib; 26762306a36Sopenharmony_ci unsigned long lib_id; 26862306a36Sopenharmony_ci int i, ret; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (guid_is_null(uuid)) 27162306a36Sopenharmony_ci return NULL; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci xa_for_each(&ipc4_data->fw_lib_xa, lib_id, fw_lib) { 27462306a36Sopenharmony_ci for (i = 0; i < fw_lib->num_modules; i++) { 27562306a36Sopenharmony_ci if (guid_equal(uuid, &fw_lib->modules[i].man4_module_entry.uuid)) 27662306a36Sopenharmony_ci return &fw_lib->modules[i]; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* 28162306a36Sopenharmony_ci * Do not attempt to load external library in case the maximum number of 28262306a36Sopenharmony_ci * firmware libraries have been already loaded 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_ci if ((lib_id + 1) == ipc4_data->max_libs_count) { 28562306a36Sopenharmony_ci dev_err(sdev->dev, 28662306a36Sopenharmony_ci "%s: Maximum allowed number of libraries reached (%u)\n", 28762306a36Sopenharmony_ci __func__, ipc4_data->max_libs_count); 28862306a36Sopenharmony_ci return NULL; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* The module cannot be found, try to load it as a library */ 29262306a36Sopenharmony_ci ret = sof_ipc4_load_library_by_uuid(sdev, lib_id + 1, uuid); 29362306a36Sopenharmony_ci if (ret) 29462306a36Sopenharmony_ci return NULL; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* Look for the module in the newly loaded library, it should be available now */ 29762306a36Sopenharmony_ci xa_for_each_start(&ipc4_data->fw_lib_xa, lib_id, fw_lib, lib_id) { 29862306a36Sopenharmony_ci for (i = 0; i < fw_lib->num_modules; i++) { 29962306a36Sopenharmony_ci if (guid_equal(uuid, &fw_lib->modules[i].man4_module_entry.uuid)) 30062306a36Sopenharmony_ci return &fw_lib->modules[i]; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci return NULL; 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic int sof_ipc4_validate_firmware(struct snd_sof_dev *sdev) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci struct sof_ipc4_fw_data *ipc4_data = sdev->private; 31062306a36Sopenharmony_ci u32 fw_hdr_offset = ipc4_data->manifest_fw_hdr_offset; 31162306a36Sopenharmony_ci struct sof_man4_fw_binary_header *fw_header; 31262306a36Sopenharmony_ci const struct firmware *fw = sdev->basefw.fw; 31362306a36Sopenharmony_ci struct sof_ext_manifest4_hdr *ext_man_hdr; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci ext_man_hdr = (struct sof_ext_manifest4_hdr *)fw->data; 31662306a36Sopenharmony_ci fw_header = (struct sof_man4_fw_binary_header *) 31762306a36Sopenharmony_ci (fw->data + ext_man_hdr->len + fw_hdr_offset); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* TODO: Add firmware verification code here */ 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci dev_dbg(sdev->dev, "Validated firmware version: %u.%u.%u.%u\n", 32262306a36Sopenharmony_ci fw_header->major_version, fw_header->minor_version, 32362306a36Sopenharmony_ci fw_header->hotfix_version, fw_header->build_version); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci return 0; 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ciint sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci struct sof_ipc4_fw_data *ipc4_data = sdev->private; 33162306a36Sopenharmony_ci const struct sof_ipc_ops *iops = sdev->ipc->ops; 33262306a36Sopenharmony_ci struct sof_ipc4_fw_version *fw_ver; 33362306a36Sopenharmony_ci struct sof_ipc4_tuple *tuple; 33462306a36Sopenharmony_ci struct sof_ipc4_msg msg; 33562306a36Sopenharmony_ci size_t offset = 0; 33662306a36Sopenharmony_ci int ret; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci /* Get the firmware configuration */ 33962306a36Sopenharmony_ci msg.primary = SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG); 34062306a36Sopenharmony_ci msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); 34162306a36Sopenharmony_ci msg.primary |= SOF_IPC4_MOD_ID(SOF_IPC4_MOD_INIT_BASEFW_MOD_ID); 34262306a36Sopenharmony_ci msg.primary |= SOF_IPC4_MOD_INSTANCE(SOF_IPC4_MOD_INIT_BASEFW_INSTANCE_ID); 34362306a36Sopenharmony_ci msg.extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_FW_PARAM_FW_CONFIG); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci msg.data_size = sdev->ipc->max_payload_size; 34662306a36Sopenharmony_ci msg.data_ptr = kzalloc(msg.data_size, GFP_KERNEL); 34762306a36Sopenharmony_ci if (!msg.data_ptr) 34862306a36Sopenharmony_ci return -ENOMEM; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci ret = iops->set_get_data(sdev, &msg, msg.data_size, false); 35162306a36Sopenharmony_ci if (ret) 35262306a36Sopenharmony_ci goto out; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci while (offset < msg.data_size) { 35562306a36Sopenharmony_ci tuple = (struct sof_ipc4_tuple *)((u8 *)msg.data_ptr + offset); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci switch (tuple->type) { 35862306a36Sopenharmony_ci case SOF_IPC4_FW_CFG_FW_VERSION: 35962306a36Sopenharmony_ci fw_ver = (struct sof_ipc4_fw_version *)tuple->value; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci dev_info(sdev->dev, 36262306a36Sopenharmony_ci "Booted firmware version: %u.%u.%u.%u\n", 36362306a36Sopenharmony_ci fw_ver->major, fw_ver->minor, fw_ver->hotfix, 36462306a36Sopenharmony_ci fw_ver->build); 36562306a36Sopenharmony_ci break; 36662306a36Sopenharmony_ci case SOF_IPC4_FW_CFG_DL_MAILBOX_BYTES: 36762306a36Sopenharmony_ci trace_sof_ipc4_fw_config(sdev, "DL mailbox size", *tuple->value); 36862306a36Sopenharmony_ci break; 36962306a36Sopenharmony_ci case SOF_IPC4_FW_CFG_UL_MAILBOX_BYTES: 37062306a36Sopenharmony_ci trace_sof_ipc4_fw_config(sdev, "UL mailbox size", *tuple->value); 37162306a36Sopenharmony_ci break; 37262306a36Sopenharmony_ci case SOF_IPC4_FW_CFG_TRACE_LOG_BYTES: 37362306a36Sopenharmony_ci trace_sof_ipc4_fw_config(sdev, "Trace log size", *tuple->value); 37462306a36Sopenharmony_ci ipc4_data->mtrace_log_bytes = *tuple->value; 37562306a36Sopenharmony_ci break; 37662306a36Sopenharmony_ci case SOF_IPC4_FW_CFG_MAX_LIBS_COUNT: 37762306a36Sopenharmony_ci trace_sof_ipc4_fw_config(sdev, "maximum number of libraries", 37862306a36Sopenharmony_ci *tuple->value); 37962306a36Sopenharmony_ci ipc4_data->max_libs_count = *tuple->value; 38062306a36Sopenharmony_ci if (!ipc4_data->max_libs_count) 38162306a36Sopenharmony_ci ipc4_data->max_libs_count = 1; 38262306a36Sopenharmony_ci break; 38362306a36Sopenharmony_ci case SOF_IPC4_FW_CFG_MAX_PPL_COUNT: 38462306a36Sopenharmony_ci ipc4_data->max_num_pipelines = *tuple->value; 38562306a36Sopenharmony_ci trace_sof_ipc4_fw_config(sdev, "Max PPL count %d", 38662306a36Sopenharmony_ci ipc4_data->max_num_pipelines); 38762306a36Sopenharmony_ci if (ipc4_data->max_num_pipelines <= 0) { 38862306a36Sopenharmony_ci dev_err(sdev->dev, "Invalid max_num_pipelines %d", 38962306a36Sopenharmony_ci ipc4_data->max_num_pipelines); 39062306a36Sopenharmony_ci ret = -EINVAL; 39162306a36Sopenharmony_ci goto out; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci break; 39462306a36Sopenharmony_ci default: 39562306a36Sopenharmony_ci break; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci offset += sizeof(*tuple) + tuple->size; 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ciout: 40262306a36Sopenharmony_ci kfree(msg.data_ptr); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci return ret; 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ciint sof_ipc4_reload_fw_libraries(struct snd_sof_dev *sdev) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci struct sof_ipc4_fw_data *ipc4_data = sdev->private; 41062306a36Sopenharmony_ci struct sof_ipc4_fw_library *fw_lib; 41162306a36Sopenharmony_ci unsigned long lib_id; 41262306a36Sopenharmony_ci int ret = 0; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci xa_for_each_start(&ipc4_data->fw_lib_xa, lib_id, fw_lib, 1) { 41562306a36Sopenharmony_ci ret = ipc4_data->load_library(sdev, fw_lib, true); 41662306a36Sopenharmony_ci if (ret) { 41762306a36Sopenharmony_ci dev_err(sdev->dev, "%s: Failed to reload library: %s, %d\n", 41862306a36Sopenharmony_ci __func__, fw_lib->name, ret); 41962306a36Sopenharmony_ci break; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci return ret; 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci/** 42762306a36Sopenharmony_ci * sof_ipc4_update_cpc_from_manifest - Update the cpc in base config from manifest 42862306a36Sopenharmony_ci * @sdev: SOF device 42962306a36Sopenharmony_ci * @fw_module: pointer struct sof_ipc4_fw_module to parse 43062306a36Sopenharmony_ci * @basecfg: Pointer to the base_config to update 43162306a36Sopenharmony_ci */ 43262306a36Sopenharmony_civoid sof_ipc4_update_cpc_from_manifest(struct snd_sof_dev *sdev, 43362306a36Sopenharmony_ci struct sof_ipc4_fw_module *fw_module, 43462306a36Sopenharmony_ci struct sof_ipc4_base_module_cfg *basecfg) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci const struct sof_man4_module_config *fw_mod_cfg; 43762306a36Sopenharmony_ci u32 cpc_pick = 0; 43862306a36Sopenharmony_ci u32 max_cpc = 0; 43962306a36Sopenharmony_ci const char *msg; 44062306a36Sopenharmony_ci int i; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (!fw_module->fw_mod_cfg) { 44362306a36Sopenharmony_ci msg = "No mod_cfg available for CPC lookup in the firmware file's manifest"; 44462306a36Sopenharmony_ci goto no_cpc; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci /* 44862306a36Sopenharmony_ci * Find the best matching (highest) CPC value based on the module's 44962306a36Sopenharmony_ci * IBS/OBS configuration inferred from the audio format selection. 45062306a36Sopenharmony_ci * 45162306a36Sopenharmony_ci * The CPC value in each module config entry has been measured and 45262306a36Sopenharmony_ci * recorded as a IBS/OBS/CPC triplet and stored in the firmware file's 45362306a36Sopenharmony_ci * manifest 45462306a36Sopenharmony_ci */ 45562306a36Sopenharmony_ci fw_mod_cfg = fw_module->fw_mod_cfg; 45662306a36Sopenharmony_ci for (i = 0; i < fw_module->man4_module_entry.cfg_count; i++) { 45762306a36Sopenharmony_ci if (basecfg->obs == fw_mod_cfg[i].obs && 45862306a36Sopenharmony_ci basecfg->ibs == fw_mod_cfg[i].ibs && 45962306a36Sopenharmony_ci cpc_pick < fw_mod_cfg[i].cpc) 46062306a36Sopenharmony_ci cpc_pick = fw_mod_cfg[i].cpc; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci if (max_cpc < fw_mod_cfg[i].cpc) 46362306a36Sopenharmony_ci max_cpc = fw_mod_cfg[i].cpc; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci basecfg->cpc = cpc_pick; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci /* We have a matching configuration for CPC */ 46962306a36Sopenharmony_ci if (basecfg->cpc) 47062306a36Sopenharmony_ci return; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* 47362306a36Sopenharmony_ci * No matching IBS/OBS found, the firmware manifest is missing 47462306a36Sopenharmony_ci * information in the module's module configuration table. 47562306a36Sopenharmony_ci */ 47662306a36Sopenharmony_ci if (!max_cpc) 47762306a36Sopenharmony_ci msg = "No CPC value available in the firmware file's manifest"; 47862306a36Sopenharmony_ci else if (!cpc_pick) 47962306a36Sopenharmony_ci msg = "No CPC match in the firmware file's manifest"; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cino_cpc: 48262306a36Sopenharmony_ci dev_dbg(sdev->dev, "%s (UUID: %pUL): %s (ibs/obs: %u/%u)\n", 48362306a36Sopenharmony_ci fw_module->man4_module_entry.name, 48462306a36Sopenharmony_ci &fw_module->man4_module_entry.uuid, msg, basecfg->ibs, 48562306a36Sopenharmony_ci basecfg->obs); 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ciconst struct sof_ipc_fw_loader_ops ipc4_loader_ops = { 48962306a36Sopenharmony_ci .validate = sof_ipc4_validate_firmware, 49062306a36Sopenharmony_ci .parse_ext_manifest = sof_ipc4_fw_parse_basefw_ext_man, 49162306a36Sopenharmony_ci}; 492