summaryrefslogtreecommitdiffstats
path: root/sound/soc/sof/ipc4-loader.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/sof/ipc4-loader.c')
-rw-r--r--sound/soc/sof/ipc4-loader.c244
1 files changed, 226 insertions, 18 deletions
diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c
index e635ae515fa9..1321acc402fd 100644
--- a/sound/soc/sof/ipc4-loader.c
+++ b/sound/soc/sof/ipc4-loader.c
@@ -14,12 +14,15 @@
#include "sof-priv.h"
#include "ops.h"
-static size_t sof_ipc4_fw_parse_ext_man(struct snd_sof_dev *sdev)
+/* The module ID includes the id of the library it is part of at offset 12 */
+#define SOF_IPC4_MOD_LIB_ID_SHIFT 12
+
+static ssize_t sof_ipc4_fw_parse_ext_man(struct snd_sof_dev *sdev,
+ struct sof_ipc4_fw_library *fw_lib)
{
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
- struct snd_sof_pdata *plat_data = sdev->pdata;
+ const struct firmware *fw = fw_lib->sof_fw.fw;
struct sof_man4_fw_binary_header *fw_header;
- const struct firmware *fw = plat_data->fw;
struct sof_ext_manifest4_hdr *ext_man_hdr;
struct sof_man4_module_config *fm_config;
struct sof_ipc4_fw_module *fw_module;
@@ -71,20 +74,20 @@ static size_t sof_ipc4_fw_parse_ext_man(struct snd_sof_dev *sdev)
return -EINVAL;
}
- dev_info(sdev->dev, "Loaded firmware version: %u.%u.%u.%u\n",
- fw_header->major_version, fw_header->minor_version,
+ dev_info(sdev->dev, "Loaded firmware library: %s, version: %u.%u.%u.%u\n",
+ fw_header->name, fw_header->major_version, fw_header->minor_version,
fw_header->hotfix_version, fw_header->build_version);
- dev_dbg(sdev->dev, "Firmware name: %s, header length: %u, module count: %u\n",
- fw_header->name, fw_header->len, fw_header->num_module_entries);
+ dev_dbg(sdev->dev, "Header length: %u, module count: %u\n",
+ fw_header->len, fw_header->num_module_entries);
- ipc4_data->fw_modules = devm_kmalloc_array(sdev->dev,
- fw_header->num_module_entries,
- sizeof(*fw_module), GFP_KERNEL);
- if (!ipc4_data->fw_modules)
+ fw_lib->modules = devm_kmalloc_array(sdev->dev, fw_header->num_module_entries,
+ sizeof(*fw_module), GFP_KERNEL);
+ if (!fw_lib->modules)
return -ENOMEM;
- ipc4_data->num_fw_modules = fw_header->num_module_entries;
- fw_module = ipc4_data->fw_modules;
+ fw_lib->name = fw_header->name;
+ fw_lib->num_modules = fw_header->num_module_entries;
+ fw_module = fw_lib->modules;
fm_entry = (struct sof_man4_module *)((u8 *)fw_header + fw_header->len);
remaining -= fw_header->len;
@@ -134,13 +137,182 @@ static size_t sof_ipc4_fw_parse_ext_man(struct snd_sof_dev *sdev)
return ext_man_hdr->len;
}
+static size_t sof_ipc4_fw_parse_basefw_ext_man(struct snd_sof_dev *sdev)
+{
+ struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+ struct sof_ipc4_fw_library *fw_lib;
+ ssize_t payload_offset;
+ int ret;
+
+ fw_lib = devm_kzalloc(sdev->dev, sizeof(*fw_lib), GFP_KERNEL);
+ if (!fw_lib)
+ return -ENOMEM;
+
+ fw_lib->sof_fw.fw = sdev->basefw.fw;
+
+ payload_offset = sof_ipc4_fw_parse_ext_man(sdev, fw_lib);
+ if (payload_offset > 0) {
+ fw_lib->sof_fw.payload_offset = payload_offset;
+
+ /* basefw ID is 0 */
+ fw_lib->id = 0;
+ ret = xa_insert(&ipc4_data->fw_lib_xa, 0, fw_lib, GFP_KERNEL);
+ if (ret)
+ return ret;
+ }
+
+ return payload_offset;
+}
+
+static int sof_ipc4_load_library_by_uuid(struct snd_sof_dev *sdev,
+ unsigned long lib_id, const guid_t *uuid)
+{
+ struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+ struct sof_ipc4_fw_library *fw_lib;
+ const char *fw_filename;
+ ssize_t payload_offset;
+ int ret, i, err;
+
+ if (!sdev->pdata->fw_lib_prefix) {
+ dev_err(sdev->dev,
+ "Library loading is not supported due to not set library path\n");
+ return -EINVAL;
+ }
+
+ if (!ipc4_data->load_library) {
+ dev_err(sdev->dev, "Library loading is not supported on this platform\n");
+ return -EOPNOTSUPP;
+ }
+
+ fw_lib = devm_kzalloc(sdev->dev, sizeof(*fw_lib), GFP_KERNEL);
+ if (!fw_lib)
+ return -ENOMEM;
+
+ fw_filename = kasprintf(GFP_KERNEL, "%s/%pUL.bin",
+ sdev->pdata->fw_lib_prefix, uuid);
+ if (!fw_filename) {
+ ret = -ENOMEM;
+ goto free_fw_lib;
+ }
+
+ ret = request_firmware(&fw_lib->sof_fw.fw, fw_filename, sdev->dev);
+ if (ret < 0) {
+ dev_err(sdev->dev, "Library file '%s' is missing\n", fw_filename);
+ goto free_filename;
+ } else {
+ dev_dbg(sdev->dev, "Library file '%s' loaded\n", fw_filename);
+ }
+
+ payload_offset = sof_ipc4_fw_parse_ext_man(sdev, fw_lib);
+ if (payload_offset <= 0) {
+ if (!payload_offset)
+ ret = -EINVAL;
+ else
+ ret = payload_offset;
+
+ goto release;
+ }
+
+ fw_lib->sof_fw.payload_offset = payload_offset;
+ fw_lib->id = lib_id;
+
+ /* Fix up the module ID numbers within the library */
+ for (i = 0; i < fw_lib->num_modules; i++)
+ fw_lib->modules[i].man4_module_entry.id |= (lib_id << SOF_IPC4_MOD_LIB_ID_SHIFT);
+
+ /*
+ * Make sure that the DSP is booted and stays up while attempting the
+ * loading the library for the first time
+ */
+ ret = pm_runtime_resume_and_get(sdev->dev);
+ if (ret < 0 && ret != -EACCES) {
+ dev_err_ratelimited(sdev->dev, "%s: pm_runtime resume failed: %d\n",
+ __func__, ret);
+ goto release;
+ }
+
+ ret = ipc4_data->load_library(sdev, fw_lib, false);
+
+ pm_runtime_mark_last_busy(sdev->dev);
+ err = pm_runtime_put_autosuspend(sdev->dev);
+ if (err < 0)
+ dev_err_ratelimited(sdev->dev, "%s: pm_runtime idle failed: %d\n",
+ __func__, err);
+
+ if (ret)
+ goto release;
+
+ ret = xa_insert(&ipc4_data->fw_lib_xa, lib_id, fw_lib, GFP_KERNEL);
+ if (unlikely(ret))
+ goto release;
+
+ kfree(fw_filename);
+
+ return 0;
+
+release:
+ release_firmware(fw_lib->sof_fw.fw);
+ /* Allocated within sof_ipc4_fw_parse_ext_man() */
+ devm_kfree(sdev->dev, fw_lib->modules);
+free_filename:
+ kfree(fw_filename);
+free_fw_lib:
+ devm_kfree(sdev->dev, fw_lib);
+
+ return ret;
+}
+
+struct sof_ipc4_fw_module *sof_ipc4_find_module_by_uuid(struct snd_sof_dev *sdev,
+ const guid_t *uuid)
+{
+ struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+ struct sof_ipc4_fw_library *fw_lib;
+ unsigned long lib_id;
+ int i, ret;
+
+ if (guid_is_null(uuid))
+ return NULL;
+
+ xa_for_each(&ipc4_data->fw_lib_xa, lib_id, fw_lib) {
+ for (i = 0; i < fw_lib->num_modules; i++) {
+ if (guid_equal(uuid, &fw_lib->modules[i].man4_module_entry.uuid))
+ return &fw_lib->modules[i];
+ }
+ }
+
+ /*
+ * Do not attempt to load external library in case the maximum number of
+ * firmware libraries have been already loaded
+ */
+ if ((lib_id + 1) == ipc4_data->max_libs_count) {
+ dev_err(sdev->dev,
+ "%s: Maximum allowed number of libraries reached (%u)\n",
+ __func__, ipc4_data->max_libs_count);
+ return NULL;
+ }
+
+ /* The module cannot be found, try to load it as a library */
+ ret = sof_ipc4_load_library_by_uuid(sdev, lib_id + 1, uuid);
+ if (ret)
+ return NULL;
+
+ /* Look for the module in the newly loaded library, it should be available now */
+ xa_for_each_start(&ipc4_data->fw_lib_xa, lib_id, fw_lib, lib_id) {
+ for (i = 0; i < fw_lib->num_modules; i++) {
+ if (guid_equal(uuid, &fw_lib->modules[i].man4_module_entry.uuid))
+ return &fw_lib->modules[i];
+ }
+ }
+
+ return NULL;
+}
+
static int sof_ipc4_validate_firmware(struct snd_sof_dev *sdev)
{
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
u32 fw_hdr_offset = ipc4_data->manifest_fw_hdr_offset;
- struct snd_sof_pdata *plat_data = sdev->pdata;
struct sof_man4_fw_binary_header *fw_header;
- const struct firmware *fw = plat_data->fw;
+ const struct firmware *fw = sdev->basefw.fw;
struct sof_ext_manifest4_hdr *ext_man_hdr;
ext_man_hdr = (struct sof_ext_manifest4_hdr *)fw->data;
@@ -156,7 +328,7 @@ static int sof_ipc4_validate_firmware(struct snd_sof_dev *sdev)
return 0;
}
-static int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev)
+int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev)
{
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
const struct sof_ipc_ops *iops = sdev->ipc->ops;
@@ -204,6 +376,24 @@ static int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev)
trace_sof_ipc4_fw_config(sdev, "Trace log size", *tuple->value);
ipc4_data->mtrace_log_bytes = *tuple->value;
break;
+ case SOF_IPC4_FW_CFG_MAX_LIBS_COUNT:
+ trace_sof_ipc4_fw_config(sdev, "maximum number of libraries",
+ *tuple->value);
+ ipc4_data->max_libs_count = *tuple->value;
+ if (!ipc4_data->max_libs_count)
+ ipc4_data->max_libs_count = 1;
+ break;
+ case SOF_IPC4_FW_CFG_MAX_PPL_COUNT:
+ ipc4_data->max_num_pipelines = *tuple->value;
+ trace_sof_ipc4_fw_config(sdev, "Max PPL count %d",
+ ipc4_data->max_num_pipelines);
+ if (ipc4_data->max_num_pipelines <= 0) {
+ dev_err(sdev->dev, "Invalid max_num_pipelines %d",
+ ipc4_data->max_num_pipelines);
+ ret = -EINVAL;
+ goto out;
+ }
+ break;
default:
break;
}
@@ -217,8 +407,26 @@ out:
return ret;
}
+int sof_ipc4_reload_fw_libraries(struct snd_sof_dev *sdev)
+{
+ struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+ struct sof_ipc4_fw_library *fw_lib;
+ unsigned long lib_id;
+ int ret = 0;
+
+ xa_for_each_start(&ipc4_data->fw_lib_xa, lib_id, fw_lib, 1) {
+ ret = ipc4_data->load_library(sdev, fw_lib, true);
+ if (ret) {
+ dev_err(sdev->dev, "%s: Failed to reload library: %s, %d\n",
+ __func__, fw_lib->name, ret);
+ break;
+ }
+ }
+
+ return ret;
+}
+
const struct sof_ipc_fw_loader_ops ipc4_loader_ops = {
.validate = sof_ipc4_validate_firmware,
- .parse_ext_manifest = sof_ipc4_fw_parse_ext_man,
- .query_fw_configuration = sof_ipc4_query_fw_configuration,
+ .parse_ext_manifest = sof_ipc4_fw_parse_basefw_ext_man,
};