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/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 */ 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 */ diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 98812d51b31c8e..42759767a050ae 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; @@ -479,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); @@ -525,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) { @@ -542,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/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/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 */ 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 */ 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 de062118ae8cc8..a77ae08c58322d 100644 --- a/sound/soc/sof/ipc4-loader.c +++ b/sound/soc/sof/ipc4-loader.c @@ -14,18 +14,21 @@ #include "sof-priv.h" #include "ops.h" -static size_t sof_ipc4_fw_parse_ext_man(struct snd_sof_dev *sdev) +#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, 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) { @@ -60,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; @@ -88,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) * @@ -101,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; @@ -112,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; } @@ -149,6 +185,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 +230,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; } @@ -203,11 +260,131 @@ static int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev) out: kfree(msg.data_ptr); + 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); +} + +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; } +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/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h index e3b8484a2f1fa8..dc33319cd40e51 100644 --- a/sound/soc/sof/ipc4-priv.h +++ b/sound/soc/sof/ipc4-priv.h @@ -32,6 +32,10 @@ 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 + * @module_uuids: List of module UUIDs */ struct sof_ipc4_fw_data { u32 manifest_fw_hdr_offset; @@ -40,6 +44,9 @@ 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; + guid_t *module_uuids; }; /** diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index 5f51d936b30671..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; @@ -165,6 +170,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 +180,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); 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 8bbc94907c624b..c2cc72fdbfb1a4 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 @@ -189,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, @@ -394,6 +411,10 @@ 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 snd_sof_widget; + /** * struct sof_ipc_fw_loader_ops - IPC/FW-specific loader ops * @validate: Function pointer for validating the firmware image @@ -405,12 +426,17 @@ 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 + * @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); - 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); + 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; 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);