From 88f33a79d933255478238f421ede39aa6fa94ad0 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sat, 25 Jun 2022 10:02:19 -0700 Subject: [PATCH 01/10] ASoC: SOF: loader: Set complete state before post_fw_run op Set the FW state to complete right after boot is complete. This enables sending IPC's in the post_fw_run op. This will be needed to support reloading 3rd party module libraries after firmware boot. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/loader.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index 5f51d936b30671..59e6be59258e4a 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -165,6 +165,9 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev) if (sdev->fw_state == SOF_FW_BOOT_READY_FAILED) return -EIO; /* FW boots but fw_ready op failed */ + dev_dbg(sdev->dev, "firmware boot complete\n"); + sof_set_fw_state(sdev, SOF_FW_BOOT_COMPLETE); + /* perform post fw run operations */ ret = snd_sof_dsp_post_fw_run(sdev); if (ret < 0) { @@ -172,9 +175,6 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev) return ret; } - dev_dbg(sdev->dev, "firmware boot complete\n"); - sof_set_fw_state(sdev, SOF_FW_BOOT_COMPLETE); - if (sdev->first_boot && sdev->ipc->ops->fw_loader->query_fw_configuration) return sdev->ipc->ops->fw_loader->query_fw_configuration(sdev); From 0d166a2c1cf6d27948215c81c4f46cdd5cf7a151 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sat, 25 Jun 2022 10:19:49 -0700 Subject: [PATCH 02/10] ASoC: SOF: IPC4: Introduce 2 new fields in struct sof_ipc4_fw_data Introduce a new structure snd_sof_module_library_info to save 3rd party module library info. Add two new fields to struct sof_ipc4_fw_data, max_fw_libs and module_lib_info. max_fw_libs is populated when querying the FW configuration and we allocate memory for module_lib_info based on max_fw_libs. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/ipc4-loader.c | 23 +++++++++++++++++++++++ sound/soc/sof/ipc4-priv.h | 5 +++++ sound/soc/sof/sof-priv.h | 15 +++++++++++++++ 3 files changed, 43 insertions(+) diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c index de062118ae8cc8..760f4817743276 100644 --- a/sound/soc/sof/ipc4-loader.c +++ b/sound/soc/sof/ipc4-loader.c @@ -14,6 +14,8 @@ #include "sof-priv.h" #include "ops.h" +#define SOF_IPC4_MAX_FIRMWARE_LIBS 32 + static size_t sof_ipc4_fw_parse_ext_man(struct snd_sof_dev *sdev) { struct sof_ipc4_fw_data *ipc4_data = sdev->private; @@ -149,6 +151,7 @@ static 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; + struct snd_sof_pdata *plat_data = sdev->pdata; struct sof_ipc4_fw_version *fw_ver; struct sof_ipc4_tuple *tuple; struct sof_ipc4_msg msg; @@ -193,6 +196,26 @@ 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: + { + size_t size = sizeof(struct snd_sof_module_library_info); + + ipc4_data->max_fw_libs = *tuple->value; + + /* limit maximum number of libraries supported */ + if (ipc4_data->max_fw_libs > SOF_IPC4_MAX_FIRMWARE_LIBS) + ipc4_data->max_fw_libs = SOF_IPC4_MAX_FIRMWARE_LIBS; + + if (!ipc4_data->max_fw_libs) + break; + + ipc4_data->module_lib_info = devm_kcalloc(sdev->dev, + ipc4_data->max_fw_libs, + size, GFP_KERNEL); + if (!ipc4_data->module_lib_info) + return -ENOMEM; + break; + } default: break; } diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h index e3b8484a2f1fa8..c18c869e898be2 100644 --- a/sound/soc/sof/ipc4-priv.h +++ b/sound/soc/sof/ipc4-priv.h @@ -32,6 +32,9 @@ enum sof_ipc4_mtrace_type { * @nhlt: NHLT table either from the BIOS or the topology manifest * @mtrace_type: mtrace type supported on the booted platform * @mtrace_log_bytes: log bytes as reported by the firmware via fw_config reply + * @max_fw_libs: Maximum number of FW libraries support by the FW including the base firmware + * @module_lib_info: Array of struct snd_sof_module_library_info containing information + * about all 3rd party modules */ struct sof_ipc4_fw_data { u32 manifest_fw_hdr_offset; @@ -40,6 +43,8 @@ struct sof_ipc4_fw_data { void *nhlt; enum sof_ipc4_mtrace_type mtrace_type; u32 mtrace_log_bytes; + u32 max_fw_libs; + struct snd_sof_module_library_info *module_lib_info; }; /** diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 8bbc94907c624b..e306595319f562 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -12,6 +12,7 @@ #define __SOUND_SOC_SOF_PRIV_H #include +#include #include #include #include @@ -128,6 +129,20 @@ struct snd_sof_platform_stream_params { bool no_ipc_position; }; +/** + * struct snd_sof_module_library_info: 3rd party module library information + * @fw: Pointer to the 3rd party module firmware + * @fw_offset: Offset of the firmware within the firmware file + * @name: 3rd party module firmware name + * @id: ID of the module library + */ +struct snd_sof_module_library_info { + const struct firmware *fw; + size_t fw_offset; + char *name; + u32 id; +}; + /* * SOF DSP HW abstraction operations. * Used to abstract DSP HW architecture and any IO busses between host CPU From 49b52be5dfe0a36af0bcd3193a918a5a387ed9bd Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sat, 25 Jun 2022 11:22:52 -0700 Subject: [PATCH 03/10] ASoC: SOF: loader: Modify the signature of parse_ext_manifest op Modify the signature of the parse_ext_manifest op in struct sof_ipc_fw_loader_ops to pass the pointer to the firmware along with the firmware library index. This is needed to reuse the same op for parsing the extended manifest for module firmware libraries. Along with the above change, also split the parse_ext_manifest for IPC4 into 2 parts. During firmware boot, we only need the firmware offset. So the first call to parse_ext_manifest returns without parsing the module information. Once we've queried the maximum number of libraries supported by the FW after successful boot, we use that information to allocate memory for as many modules as those in the base FW and the max 3rd party modules supported by the firmware. As the manifest is parsed, the module UUID's are saved to the new field, module_uuids in struct sof_ipc4_fw_data. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/ipc3-loader.c | 5 ++- sound/soc/sof/ipc4-loader.c | 71 +++++++++++++++++++++++++++++-------- sound/soc/sof/ipc4-priv.h | 2 ++ sound/soc/sof/loader.c | 9 +++-- sound/soc/sof/sof-priv.h | 6 +++- 5 files changed, 72 insertions(+), 21 deletions(-) diff --git a/sound/soc/sof/ipc3-loader.c b/sound/soc/sof/ipc3-loader.c index bf423ca4e97bb4..ea941ef7850153 100644 --- a/sound/soc/sof/ipc3-loader.c +++ b/sound/soc/sof/ipc3-loader.c @@ -136,10 +136,9 @@ static ssize_t ipc3_fw_ext_man_size(struct snd_sof_dev *sdev, const struct firmw return 0; } -static size_t sof_ipc3_fw_parse_ext_man(struct snd_sof_dev *sdev) +static size_t sof_ipc3_fw_parse_ext_man(struct snd_sof_dev *sdev, const struct firmware *fw, + u32 lib_index, u32 flags) { - struct snd_sof_pdata *plat_data = sdev->pdata; - const struct firmware *fw = plat_data->fw; const struct sof_ext_man_elem_header *elem_hdr; const struct sof_ext_man_header *head; ssize_t ext_man_size; diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c index 760f4817743276..2078e76dc4ee18 100644 --- a/sound/soc/sof/ipc4-loader.c +++ b/sound/soc/sof/ipc4-loader.c @@ -15,19 +15,20 @@ #include "ops.h" #define SOF_IPC4_MAX_FIRMWARE_LIBS 32 +#define SOF_IPC4_MODULE_LIBARY_OFFSET 0x1000 -static size_t sof_ipc4_fw_parse_ext_man(struct snd_sof_dev *sdev) +static size_t sof_ipc4_fw_parse_ext_man(struct snd_sof_dev *sdev, const struct firmware *fw, + u32 lib_index, u32 flags) { struct sof_ipc4_fw_data *ipc4_data = sdev->private; - struct snd_sof_pdata *plat_data = sdev->pdata; 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; struct sof_man4_module *fm_entry; ssize_t remaining; u32 fw_hdr_offset; + int num_modules = 0; int i; if (!ipc4_data) { @@ -62,20 +63,40 @@ static size_t sof_ipc4_fw_parse_ext_man(struct snd_sof_dev *sdev) return -EINVAL; } + /* return the extended manifest header length if SOF_FW_PARSE_MANIFEST_PRE_BOOT is set */ + if (flags & SOF_FW_PARSE_MANIFEST_PRE_BOOT) + return ext_man_hdr->len; + dev_info(sdev->dev, "Loaded firmware version: %u.%u.%u.%u\n", 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); - ipc4_data->fw_modules = devm_kmalloc_array(sdev->dev, - fw_header->num_module_entries, - sizeof(*fw_module), GFP_KERNEL); - if (!ipc4_data->fw_modules) - return -ENOMEM; + /* + * allocate memory for the number of modules in the base FW + the max libraries + * supported by the firmware. This assumes that there is a 1:1 mapping between libraries + * and module UUID's for modules that are not already part of the base firmware. + */ + if (!lib_index) { + /* max_fw_libs includes the base firmware */ + int num_total_modules = fw_header->num_module_entries + ipc4_data->max_fw_libs - 1; + + ipc4_data->fw_modules = devm_kmalloc_array(sdev->dev, num_total_modules, + sizeof(*fw_module), GFP_KERNEL); + if (!ipc4_data->fw_modules) + return -ENOMEM; + + ipc4_data->module_uuids = devm_kcalloc(sdev->dev, num_total_modules, + sizeof(guid_t), GFP_KERNEL); + if (!ipc4_data->module_uuids) + return -ENOMEM; + } else { + /* set the firmware name */ + ipc4_data->module_lib_info[lib_index].name = fw_header->name; + } - ipc4_data->num_fw_modules = fw_header->num_module_entries; - fw_module = ipc4_data->fw_modules; + fw_module = ipc4_data->fw_modules + ipc4_data->num_fw_modules; fm_entry = (struct sof_man4_module *)((u8 *)fw_header + fw_header->len); remaining -= fw_header->len; @@ -90,7 +111,11 @@ static size_t sof_ipc4_fw_parse_ext_man(struct snd_sof_dev *sdev) (fm_entry + fw_header->num_module_entries); remaining -= (fw_header->num_module_entries * sizeof(*fm_entry)); for (i = 0; i < fw_header->num_module_entries; i++) { - memcpy(&fw_module->man4_module_entry, fm_entry, sizeof(*fm_entry)); + /* ignore modules with null UUID */ + if (guid_is_null(&fm_entry->uuid)) { + fm_entry++; + continue; + } if (fm_entry->cfg_count) { if (remaining < (fm_entry->cfg_offset + fm_entry->cfg_count) * @@ -103,10 +128,14 @@ static size_t sof_ipc4_fw_parse_ext_man(struct snd_sof_dev *sdev) /* a module's config is always the same size */ fw_module->bss_size = fm_config[fm_entry->cfg_offset].is_bytes; + guid_copy(&ipc4_data->module_uuids[ipc4_data->num_fw_modules + i], + &fm_entry->uuid); + dev_dbg(sdev->dev, "module %s: UUID %pUL cfg_count: %u, bss_size: %#x\n", - fm_entry->name, &fm_entry->uuid, fm_entry->cfg_count, - fw_module->bss_size); + fm_entry->name, + &ipc4_data->module_uuids[i + ipc4_data->num_fw_modules], + fm_entry->cfg_count, fw_module->bss_size); } else { fw_module->bss_size = 0; @@ -114,14 +143,19 @@ static size_t sof_ipc4_fw_parse_ext_man(struct snd_sof_dev *sdev) &fm_entry->uuid); } - fw_module->man4_module_entry.id = i; + memcpy(&fw_module->man4_module_entry, fm_entry, sizeof(*fm_entry)); + + fw_module->man4_module_entry.id = SOF_IPC4_MODULE_LIBARY_OFFSET * lib_index + i; ida_init(&fw_module->m_ida); fw_module->private = NULL; fw_module++; fm_entry++; + num_modules++; } + ipc4_data->num_fw_modules += num_modules; + return ext_man_hdr->len; } @@ -226,7 +260,14 @@ static int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev) out: kfree(msg.data_ptr); - return ret; + if (ret) + return ret; + + /* + * Unset the SOF_FW_PARSE_MANIFEST_PRE_BOOT flag to allow parsing the extended manifest in + * the base firmware (id: 0) + */ + return sof_ipc4_fw_parse_ext_man(sdev, plat_data->fw, 0, 0); } const struct sof_ipc_fw_loader_ops ipc4_loader_ops = { diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h index c18c869e898be2..dc33319cd40e51 100644 --- a/sound/soc/sof/ipc4-priv.h +++ b/sound/soc/sof/ipc4-priv.h @@ -35,6 +35,7 @@ enum sof_ipc4_mtrace_type { * @max_fw_libs: Maximum number of FW libraries support by the FW including the base firmware * @module_lib_info: Array of struct snd_sof_module_library_info containing information * about all 3rd party modules + * @module_uuids: List of module UUIDs */ struct sof_ipc4_fw_data { u32 manifest_fw_hdr_offset; @@ -45,6 +46,7 @@ struct sof_ipc4_fw_data { u32 mtrace_log_bytes; u32 max_fw_libs; struct snd_sof_module_library_info *module_lib_info; + guid_t *module_uuids; }; /** diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index 59e6be59258e4a..e707a2c503581b 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -17,6 +17,7 @@ int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev) { struct snd_sof_pdata *plat_data = sdev->pdata; + const struct sof_ipc_ops *ipc_ops = sdev->ipc->ops; const char *fw_filename; ssize_t ext_man_size; int ret; @@ -44,8 +45,12 @@ int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev) fw_filename); } - /* check for extended manifest */ - ext_man_size = sdev->ipc->ops->fw_loader->parse_ext_manifest(sdev); + /* + * check for extended manifest. Set the SOF_FW_PARSE_MANIFEST_PRE_BOOT flag and pass 0 + * for the library index of the base firmware. + */ + ext_man_size = ipc_ops->fw_loader->parse_ext_manifest(sdev, plat_data->fw, 0, + SOF_FW_PARSE_MANIFEST_PRE_BOOT); if (ext_man_size > 0) { /* when no error occurred, drop extended manifest */ plat_data->fw_offset = ext_man_size; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index e306595319f562..e4c1ac6ad881b5 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -409,6 +409,9 @@ struct sof_ipc_pm_ops { int (*set_core_state)(struct snd_sof_dev *sdev, int core_idx, bool on); }; +/* Flag used when parsing the firmware extended manifest before firmware boot is complete */ +#define SOF_FW_PARSE_MANIFEST_PRE_BOOT BIT(1) + /** * struct sof_ipc_fw_loader_ops - IPC/FW-specific loader ops * @validate: Function pointer for validating the firmware image @@ -423,7 +426,8 @@ struct sof_ipc_pm_ops { */ struct sof_ipc_fw_loader_ops { int (*validate)(struct snd_sof_dev *sdev); - size_t (*parse_ext_manifest)(struct snd_sof_dev *sdev); + size_t (*parse_ext_manifest)(struct snd_sof_dev *sdev, const struct firmware *fw, + u32 lib_index, u32 flags); int (*load_fw_to_dsp)(struct snd_sof_dev *sdev); int (*query_fw_configuration)(struct snd_sof_dev *sdev); }; From 775cb46bb7ea8d88a8bac8738bcc68c800e58362 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sat, 25 Jun 2022 11:37:47 -0700 Subject: [PATCH 04/10] ASoC: SOF: Add new DSP op for module library loading Add a new op in struct snd_sof_dsp_ops to load module libraries and define the op for IPC4. Signed-off-by: Ranjani Sridharan --- include/sound/sof/ipc4/header.h | 4 +++ sound/soc/sof/intel/hda-loader.c | 59 ++++++++++++++++++++++++++++++++ sound/soc/sof/intel/hda.h | 2 ++ sound/soc/sof/sof-priv.h | 2 ++ 4 files changed, 67 insertions(+) diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h index 99efe0ef178481..622193be7ac43d 100644 --- a/include/sound/sof/ipc4/header.h +++ b/include/sound/sof/ipc4/header.h @@ -185,6 +185,10 @@ enum sof_ipc4_pipeline_state { #define SOF_IPC4_GLB_PIPE_STATE_MASK GENMASK(15, 0) #define SOF_IPC4_GLB_PIPE_STATE(x) ((x) << SOF_IPC4_GLB_PIPE_STATE_SHIFT) +/* load library ipc msg */ +#define SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID_SHIFT 16 +#define SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID(x) ((x) << SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID_SHIFT) + enum sof_ipc4_channel_config { /* one channel only. */ SOF_IPC4_CHANNEL_CONFIG_MONO, diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 98812d51b31c8e..88f20b8970bda1 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "ext_manifest.h" #include "../ops.h" #include "../sof-priv.h" @@ -382,6 +383,64 @@ static int hda_dsp_boot_imr(struct snd_sof_dev *sdev) return ret; } +int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev, + struct snd_sof_module_library_info *lib_info) +{ + struct hdac_ext_stream *hext_stream; + struct firmware stripped_firmware; + struct snd_dma_buffer dmab; + struct sof_ipc4_msg msg = {}; + int ret, ret1; + + stripped_firmware.data = lib_info->fw->data + lib_info->fw_offset; + stripped_firmware.size = lib_info->fw->size - lib_info->fw_offset; + + /* prepare DMA for code loader stream */ + hext_stream = hda_cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, + stripped_firmware.size, + &dmab, SNDRV_PCM_STREAM_PLAYBACK); + if (IS_ERR(hext_stream)) { + dev_err(sdev->dev, "DMA prepare failed\n"); + return PTR_ERR(hext_stream); + } + + memcpy(dmab.area, stripped_firmware.data, stripped_firmware.size); + + msg.primary = hext_stream->hstream.stream_tag - 1; + msg.primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_LOAD_LIBRARY); + msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); + msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG); + msg.primary |= SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID(lib_info->id); + + ret = cl_trigger(sdev, hext_stream, SNDRV_PCM_TRIGGER_START); + if (ret < 0) { + dev_err(sdev->dev, "DMA trigger start failed\n"); + goto cleanup; + } + + ret = sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0); + + ret1 = cl_trigger(sdev, hext_stream, SNDRV_PCM_TRIGGER_STOP); + if (ret1 < 0) { + dev_err(sdev->dev, "DMA trigger stop failed\n"); + if (!ret) + ret = ret1; + } + +cleanup: + /* clean up even in case of error and return the first error */ + ret1 = hda_cl_cleanup(sdev, &dmab, hext_stream); + if (ret1 < 0) { + dev_err(sdev->dev, "Code loader DSP cleanup failed\n"); + + /* set return value to indicate cleanup failure */ + if (!ret) + ret = ret1; + } + + return ret; +} + int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 603f17b60edeb0..ecc0a07dc6859d 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -851,5 +851,7 @@ int cnl_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg); irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context); int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg); extern struct sdw_intel_ops sdw_callback; +int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev, + struct snd_sof_module_library_info *lib_info); #endif diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index e4c1ac6ad881b5..fd210972dec30c 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -204,6 +204,8 @@ struct snd_sof_dsp_ops { int (*load_firmware)(struct snd_sof_dev *sof_dev); /* mandatory */ int (*load_module)(struct snd_sof_dev *sof_dev, struct snd_sof_mod_hdr *hdr); /* optional */ + int (*load_library)(struct snd_sof_dev *sdev, + struct snd_sof_module_library_info *lib_info); /* optional */ /* connect pcm substream to a host stream */ int (*pcm_open)(struct snd_sof_dev *sdev, From 9ff992b03cbfe48b2c05213ff3a6a60fb8076f19 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 20 Jul 2022 14:55:49 -0700 Subject: [PATCH 05/10] ASoC: SOF: Intel: MTL: set the load_library op Set the load_library op for MTL. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/intel/mtl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c index 406d58804b5000..19e8d8e35323d8 100644 --- a/sound/soc/sof/intel/mtl.c +++ b/sound/soc/sof/intel/mtl.c @@ -749,6 +749,9 @@ int sof_mtl_ops_init(struct snd_sof_dev *sdev) /* parse platform specific extended manifest */ sof_mtl_ops.parse_platform_ext_manifest = NULL; + /* module library load */ + sof_mtl_ops.load_library = hda_dsp_ipc4_load_library; + /* dsp core get/put */ /* TODO: add core_get and core_put */ From 4bbbe9af63bad6285cd9011617eaaecbcc72f957 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 20 Jul 2022 14:56:22 -0700 Subject: [PATCH 06/10] ASoC: SOF: Intel: APL: set the load_library op Set the load_library DSP op for APL. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/intel/apl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index 295df44be27138..0613a743b8150b 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -64,6 +64,9 @@ int sof_apl_ops_init(struct snd_sof_dev *sdev) /* ipc */ sof_apl_ops.send_msg = hda_dsp_ipc4_send_msg; + + /* module library load */ + sof_apl_ops.load_library = hda_dsp_ipc4_load_library; } /* set DAI driver ops */ From 9d62fff2314cfa54c2d45fea7cb6efe082c82530 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 20 Jul 2022 14:56:48 -0700 Subject: [PATCH 07/10] ASoC: SOF: Intel: TGL: set the load_library op Set the load_library DSP op for TGL and IPC4. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/intel/tgl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c index 839f600c83d0e8..bc78be7381f0bf 100644 --- a/sound/soc/sof/intel/tgl.c +++ b/sound/soc/sof/intel/tgl.c @@ -87,6 +87,9 @@ int sof_tgl_ops_init(struct snd_sof_dev *sdev) /* ipc */ sof_tgl_ops.send_msg = cnl_ipc4_send_msg; + + /* module library load */ + sof_tgl_ops.load_library = hda_dsp_ipc4_load_library; } /* set DAI driver ops */ From 8348e347698a34f62959bfae1cc7935c1ebd7731 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 20 Jul 2022 14:57:16 -0700 Subject: [PATCH 08/10] ASoC: SOF: Intel: CNL: set the load_library op Set the load_library op for CNL and IPC4. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/intel/cnl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 180001d0a38aeb..78c45ada9b5951 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -370,6 +370,9 @@ int sof_cnl_ops_init(struct snd_sof_dev *sdev) /* ipc */ sof_cnl_ops.send_msg = cnl_ipc4_send_msg; + + /* module library load */ + sof_cnl_ops.load_library = hda_dsp_ipc4_load_library; } /* set DAI driver ops */ From b0aae2bf6b44a2f81cb3e9fda36bd0b3052b7bdb Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sat, 25 Jun 2022 11:53:17 -0700 Subject: [PATCH 09/10] ASoC: SOF: Add load_library op to struct sof_ipc_fw_loader_ops Add a new load_library op to struct sof_ipc_fw_loader_ops to support loading 3rd party module libraries. This op is called during widget parsing to load the module firmware library. Also define and set the load_library op for IPC4 fw_loader ops. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/ipc4-loader.c | 83 +++++++++++++++++++++++++++++++++++++ sound/soc/sof/sof-audio.h | 2 + sound/soc/sof/sof-priv.h | 3 ++ sound/soc/sof/topology.c | 13 ++++++ 4 files changed, 101 insertions(+) diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c index 2078e76dc4ee18..e08622d2119a03 100644 --- a/sound/soc/sof/ipc4-loader.c +++ b/sound/soc/sof/ipc4-loader.c @@ -270,8 +270,91 @@ static int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev) return sof_ipc4_fw_parse_ext_man(sdev, plat_data->fw, 0, 0); } +static int sof_ipc4_load_library(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) +{ + struct sof_ipc4_fw_data *ipc4_data = sdev->private; + struct snd_sof_pdata *plat_data = sdev->pdata; + const guid_t *widget_uuid = &swidget->uuid; + const char *fw_filename; + size_t fw_offset; + int ret, i; + + /* nothing to do if widget UUID is not set */ + if (!guid_is_null(&swidget->uuid)) + return 0; + + /* check if the platform supports load_library */ + if (sof_ops(sdev)->load_library) { + dev_err(sdev->dev, "Loading module libraries not supported\n"); + return -EINVAL; + } + + /* nothing to do if module library is already loaded */ + for (i = 0; i < ipc4_data->num_fw_modules; i++) + if (guid_equal(&ipc4_data->module_uuids[i], widget_uuid)) { + dev_dbg(sdev->dev, "Module library already loaded for %s\n", + swidget->widget->name); + return 0; + } + + /* + * The required 3rd party module libraries are dictated by the topology. So they should be + * bundled in the same folder along with the topology binary + */ + fw_filename = kasprintf(GFP_KERNEL, "%s/%pUL.bin", + plat_data->tplg_filename_prefix, widget_uuid); + if (!fw_filename) + return -ENOMEM; + + /* Index 0 is reserved for base firmware */ + for (i = 1; i < ipc4_data->max_fw_libs; i++) + if (!ipc4_data->module_lib_info[i].id) + break; + + if (i >= ipc4_data->max_fw_libs) { + dev_err(sdev->dev, "Library ID: %u exceeds max value %u\n", + i, ipc4_data->max_fw_libs); + ret = -EBUSY; + goto err; + } + + ipc4_data->module_lib_info[i].id = i; + swidget->lib_info = &ipc4_data->module_lib_info[i]; + + ret = request_firmware(&ipc4_data->module_lib_info[i].fw, fw_filename, sdev->dev); + if (ret < 0) { + dev_err(sdev->dev, "Library file '%s' not found\n", fw_filename); + goto err; + } + + fw_offset = sof_ipc4_fw_parse_ext_man(sdev, ipc4_data->module_lib_info[i].fw, i, 0); + if (fw_offset < 0) + goto release; + + ipc4_data->module_lib_info[i].fw_offset = fw_offset; + + ret = sof_ops(sdev)->load_library(sdev, &ipc4_data->module_lib_info[i]); + if (ret < 0) { + ipc4_data->module_lib_info[i].fw_offset = 0; + goto release; + } + + dev_dbg(sdev->dev, "Loaded module library %s\n", fw_filename); + + kfree(fw_filename); + return 0; + +release: + release_firmware(ipc4_data->module_lib_info[i].fw); + ipc4_data->module_lib_info[i].id = 0; +err: + kfree(fw_filename); + 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, + .load_library = sof_ipc4_load_library, }; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 4284ea2f3a1ff2..5ad77b001c365e 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -387,6 +387,8 @@ struct snd_sof_widget { int num_tuples; struct snd_sof_tuple *tuples; + struct snd_sof_module_library_info *lib_info; + void *private; /* core does not touch this */ }; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index fd210972dec30c..a1cde14d5e6dcf 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -413,6 +413,7 @@ struct sof_ipc_pm_ops { /* Flag used when parsing the firmware extended manifest before firmware boot is complete */ #define SOF_FW_PARSE_MANIFEST_PRE_BOOT BIT(1) +struct snd_sof_widget; /** * struct sof_ipc_fw_loader_ops - IPC/FW-specific loader ops @@ -425,6 +426,7 @@ struct sof_ipc_pm_ops { * @query_fw_configuration: Optional function pointer to query information and * configuration from the booted firmware. * Executed after the first successful firmware boot. + * @load_library: Optional function pointer to load a 3rd party module library */ struct sof_ipc_fw_loader_ops { int (*validate)(struct snd_sof_dev *sdev); @@ -432,6 +434,7 @@ struct sof_ipc_fw_loader_ops { u32 lib_index, u32 flags); int (*load_fw_to_dsp)(struct snd_sof_dev *sdev); int (*query_fw_configuration)(struct snd_sof_dev *sdev); + int (*load_library)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); }; struct sof_ipc_tplg_ops; diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 6087483deb48e0..0b9abb792f8adc 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1382,6 +1382,11 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, w->dobj.private = swidget; list_add(&swidget->list, &sdev->widget_list); + + /* load module library if needed */ + if (sdev->ipc->ops->fw_loader->load_library) + return sdev->ipc->ops->fw_loader->load_library(sdev, swidget); + return ret; } @@ -1408,6 +1413,7 @@ static int sof_widget_unload(struct snd_soc_component *scomp, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget; + struct snd_sof_module_library_info *lib_info; const struct snd_kcontrol_new *kc; struct snd_soc_dapm_widget *widget; struct snd_sof_control *scontrol; @@ -1422,6 +1428,7 @@ static int sof_widget_unload(struct snd_soc_component *scomp, if (!swidget) return 0; + lib_info = swidget->lib_info; widget = swidget->widget; switch (swidget->id) { @@ -1472,6 +1479,12 @@ static int sof_widget_unload(struct snd_soc_component *scomp, kfree(swidget->tuples); + /* release module firmware */ + release_firmware(lib_info->fw); + lib_info->fw = NULL; + lib_info->id = 0; + lib_info->fw_offset = 0; + /* remove and free swidget object */ list_del(&swidget->list); kfree(swidget); From fc391aa80b5d7fb712e7165b70a5eb5ea68d58d3 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sat, 25 Jun 2022 09:57:46 -0700 Subject: [PATCH 10/10] ASoC: SOF: Add a new fw loader op to reload_module libraries Add a new op to struct sof_ipc_fw_loader_ops, reload_libraries, to support reloading the libraries. For HDA platforms, this is called during the post_fw_run op after the base FW is cold booted with the exception of the first boot. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/intel/hda-loader.c | 11 ++++++++++- sound/soc/sof/ipc4-loader.c | 30 ++++++++++++++++++++++++++++++ sound/soc/sof/sof-priv.h | 2 ++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 88f20b8970bda1..42759767a050ae 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -538,7 +538,6 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) ret = hda_cl_copy_fw(sdev, hext_stream); if (!ret) { dev_dbg(sdev->dev, "Firmware download successful, booting...\n"); - hda->skip_imr_boot = false; } else { snd_sof_dsp_dbg_dump(sdev, "Firmware download failed", SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX); @@ -584,6 +583,7 @@ int hda_dsp_pre_fw_run(struct snd_sof_dev *sdev) /* post fw run operations */ int hda_dsp_post_fw_run(struct snd_sof_dev *sdev) { + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; int ret; if (sdev->first_boot) { @@ -601,8 +601,17 @@ int hda_dsp_post_fw_run(struct snd_sof_dev *sdev) (sdev->fw_ready.flags & SOF_IPC_INFO_D3_PERSISTENT || sdev->pdata->ipc_type == SOF_INTEL_IPC4)) hdev->imrboot_supported = true; + } else { + /* reload all 3rd party module libraries during cold boot */ + if (!hda->imrboot_supported || hda->skip_imr_boot) { + ret = sdev->ipc->ops->fw_loader->reload_libraries(sdev); + if (ret < 0) + return ret; + } } + hda->skip_imr_boot = false; + hda_sdw_int_enable(sdev, true); /* re-enable clock gating and power gating */ diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c index e08622d2119a03..a77ae08c58322d 100644 --- a/sound/soc/sof/ipc4-loader.c +++ b/sound/soc/sof/ipc4-loader.c @@ -352,9 +352,39 @@ static int sof_ipc4_load_library(struct snd_sof_dev *sdev, struct snd_sof_widget return ret; } +static int sof_ipc4_reload_libraries(struct snd_sof_dev *sdev) +{ + struct sof_ipc4_fw_data *ipc4_data = sdev->private; + int i, ret; + + if (sof_ops(sdev)->load_library) { + dev_err(sdev->dev, "Loading module libraries not supported\n"); + return -EINVAL; + } + + /* Index 0 is reserved for base firmware */ + for (i = 1; i < ipc4_data->max_fw_libs; i++) { + if (!ipc4_data->module_lib_info[i].id) + break; + + ret = sof_ops(sdev)->load_library(sdev, &ipc4_data->module_lib_info[i]); + if (ret < 0) { + dev_err(sdev->dev, "Failed loading module FW library: %s\n", + ipc4_data->module_lib_info[i].name); + return ret; + } + + dev_dbg(sdev->dev, "Loaded FW library for module: %s\n", + ipc4_data->module_lib_info[i].name); + } + + return 0; +} + 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, .load_library = sof_ipc4_load_library, + .reload_libraries = sof_ipc4_reload_libraries, }; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index a1cde14d5e6dcf..c2cc72fdbfb1a4 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -427,6 +427,7 @@ struct snd_sof_widget; * configuration from the booted firmware. * Executed after the first successful firmware boot. * @load_library: Optional function pointer to load a 3rd party module library + * @reload_libraries: Optional function pointer to reload all needed 3rd party module libraries */ struct sof_ipc_fw_loader_ops { int (*validate)(struct snd_sof_dev *sdev); @@ -435,6 +436,7 @@ struct sof_ipc_fw_loader_ops { int (*load_fw_to_dsp)(struct snd_sof_dev *sdev); int (*query_fw_configuration)(struct snd_sof_dev *sdev); int (*load_library)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); + int (*reload_libraries)(struct snd_sof_dev *sdev); }; struct sof_ipc_tplg_ops;