[PATCH v2 00/19] ASoC: SOF: Intel/IPC4: Support for external firmware libraries
Hi,
Changes since v1: - rebased on "[PATCH v2] ASoC: SOF: Intel: pci-tgl: fix ADL-N descriptor"
Cover letter:
In IPC4 all DSP loadable executable is a 'library' containing modules. The main or basefw is also a library which contains multiple modules. IPC4 allows to use loadable libraries to extend the functionality of the booted basefw.
This series adds support for loading external libraries in case they are needed by the loaded topology file.
The libraries must be placed to a specific firmware directory (fw_lib_prefix), which is: intel/avs-lib|sof-ipc4-lib/ followed by the platform name and in case of community key use a 'community' directory.
For example for upx-i11 (community key): intel/avs-lib/tgl/community is the default path.
The name of the library should be the UUID of the module it contains since the library loading is going to look for the file as <module_UUID>.bin In case there is a need to bundle multiple modules into single library, symlinks can be used to point to the file:
module_boundle.bin <UUID1>.bin -> module_boundle.bin <UUID2>.bin -> module_boundle.bin <UUID3>.bin -> module_boundle.bin
But note that in this case all modules will be loaded to the DSP since only the whole library can be loaded, not individual modules.
Regards, Peter --- Peter Ujfalusi (18): ASoC: SOF: Introduce container struct for SOF firmware ASoC: SOF: amd: Use the basefw firmware container directly ASoC: SOF: Intel: hda-loader: Use the basefw firmware container directly ASoC: SOF: Intel: hda-loader-skl: Use the basefw firmware container directly ASoC: SOF: Drop the firmware and fw_offset from snd_sof_pdata ASoC: SOF: ipc: ops: Add support for optional init and exit callbacks ASoC: SOF: ipc4-loader: Save the maximum number of libraries supported ASoC: SOF: ipc4: Convert the firmware handling (loader) to library convention ASoC: SOF: IPC4: Add helper for looking up module by UUID ASoC: SOF: Add path definition for external firmware libraries ASoC: SOF: Intel: Set the default firmware library path for IPC4 ASoC: SOF: ipc4: Define platform dependent library loading callback ASoC: SOF: Intel: hda: Add flag to indicate that the firmware is IMR booted ASoC: SOF: Intel: Add ipc4 library loading implementation ASoC: SOF: loader: Add support for IPC dependent post firmware boot ops ASoC: SOF: ipc4: Stop using the query_fw_configuration fw_loader ops ASoC: SOF: loader: Remove the query_fw_configuration ops ASoC: SOF: ipc4-loader: Support for loading external libraries
Ranjani Sridharan (1): ASoC: SOF: loader: Set complete state before post_fw_run op
include/sound/sof.h | 10 +- include/sound/sof/ipc4/header.h | 4 + sound/soc/sof/amd/acp-loader.c | 6 +- sound/soc/sof/intel/apl.c | 3 + sound/soc/sof/intel/cnl.c | 3 + sound/soc/sof/intel/hda-loader-skl.c | 7 +- sound/soc/sof/intel/hda-loader.c | 83 +++++++++- sound/soc/sof/intel/hda.h | 4 + sound/soc/sof/intel/icl.c | 3 + sound/soc/sof/intel/mtl.c | 3 + sound/soc/sof/intel/pci-apl.c | 6 + sound/soc/sof/intel/pci-cnl.c | 9 ++ sound/soc/sof/intel/pci-icl.c | 6 + sound/soc/sof/intel/pci-mtl.c | 3 + sound/soc/sof/intel/pci-tgl.c | 24 +++ sound/soc/sof/intel/tgl.c | 3 + sound/soc/sof/ipc.c | 6 + sound/soc/sof/ipc3-loader.c | 26 ++- sound/soc/sof/ipc4-loader.c | 233 ++++++++++++++++++++++++--- sound/soc/sof/ipc4-priv.h | 65 ++++++-- sound/soc/sof/ipc4-topology.c | 17 +- sound/soc/sof/ipc4.c | 41 +++++ sound/soc/sof/loader.c | 25 ++- sound/soc/sof/sof-pci-dev.c | 26 +++ sound/soc/sof/sof-priv.h | 27 +++- 25 files changed, 543 insertions(+), 100 deletions(-)
From: Ranjani Sridharan ranjani.sridharan@linux.intel.com
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 ranjani.sridharan@linux.intel.com Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Chao Song chao.song@intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- 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 5f51d936b306..59e6be59258e 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);
Move the firmware related information under a new struct (sof_firmware) and add it to the high level snd_sof_dev struct.
Convert the generic code to use this new container when working with the basefw and for compatibility reasons set the old plat_data members used by the platforms.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Chao Song chao.song@intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/soc/sof/ipc3-loader.c | 26 ++++++++++++-------------- sound/soc/sof/ipc4-loader.c | 6 ++---- sound/soc/sof/loader.c | 18 +++++++++++++----- sound/soc/sof/sof-priv.h | 14 ++++++++++++++ 4 files changed, 41 insertions(+), 23 deletions(-)
diff --git a/sound/soc/sof/ipc3-loader.c b/sound/soc/sof/ipc3-loader.c index bf423ca4e97b..28218766d211 100644 --- a/sound/soc/sof/ipc3-loader.c +++ b/sound/soc/sof/ipc3-loader.c @@ -138,8 +138,7 @@ static ssize_t ipc3_fw_ext_man_size(struct snd_sof_dev *sdev, const struct firmw
static size_t sof_ipc3_fw_parse_ext_man(struct snd_sof_dev *sdev) { - struct snd_sof_pdata *plat_data = sdev->pdata; - const struct firmware *fw = plat_data->fw; + const struct firmware *fw = sdev->basefw.fw; const struct sof_ext_man_elem_header *elem_hdr; const struct sof_ext_man_header *head; ssize_t ext_man_size; @@ -310,18 +309,18 @@ static int sof_ipc3_parse_module_memcpy(struct snd_sof_dev *sdev,
static int sof_ipc3_load_fw_to_dsp(struct snd_sof_dev *sdev) { - struct snd_sof_pdata *plat_data = sdev->pdata; - const struct firmware *fw = plat_data->fw; + u32 payload_offset = sdev->basefw.payload_offset; + const struct firmware *fw = sdev->basefw.fw; struct snd_sof_fw_header *header; struct snd_sof_mod_hdr *module; int (*load_module)(struct snd_sof_dev *sof_dev, struct snd_sof_mod_hdr *hdr); size_t remaining; int ret, count;
- if (!plat_data->fw) + if (!fw) return -EINVAL;
- header = (struct snd_sof_fw_header *)(fw->data + plat_data->fw_offset); + header = (struct snd_sof_fw_header *)(fw->data + payload_offset); load_module = sof_ops(sdev)->load_module; if (!load_module) { dev_dbg(sdev->dev, "Using generic module loading\n"); @@ -331,9 +330,8 @@ static int sof_ipc3_load_fw_to_dsp(struct snd_sof_dev *sdev) }
/* parse each module */ - module = (struct snd_sof_mod_hdr *)(fw->data + plat_data->fw_offset + - sizeof(*header)); - remaining = fw->size - sizeof(*header) - plat_data->fw_offset; + module = (struct snd_sof_mod_hdr *)(fw->data + payload_offset + sizeof(*header)); + remaining = fw->size - sizeof(*header) - payload_offset; /* check for wrap */ if (remaining > fw->size) { dev_err(sdev->dev, "%s: fw size smaller than header size\n", __func__); @@ -374,19 +372,19 @@ static int sof_ipc3_load_fw_to_dsp(struct snd_sof_dev *sdev)
static int sof_ipc3_validate_firmware(struct snd_sof_dev *sdev) { - struct snd_sof_pdata *plat_data = sdev->pdata; - const struct firmware *fw = plat_data->fw; + u32 payload_offset = sdev->basefw.payload_offset; + const struct firmware *fw = sdev->basefw.fw; struct snd_sof_fw_header *header; - size_t fw_size = fw->size - plat_data->fw_offset; + size_t fw_size = fw->size - payload_offset;
- if (fw->size <= plat_data->fw_offset) { + if (fw->size <= payload_offset) { dev_err(sdev->dev, "firmware size must be greater than firmware offset\n"); return -EINVAL; }
/* Read the header information from the data pointer */ - header = (struct snd_sof_fw_header *)(fw->data + plat_data->fw_offset); + header = (struct snd_sof_fw_header *)(fw->data + payload_offset);
/* verify FW sig */ if (strncmp(header->sig, SND_SOF_FW_SIG, SND_SOF_FW_SIG_SIZE) != 0) { diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c index e635ae515fa9..9f433e9b4cd3 100644 --- a/sound/soc/sof/ipc4-loader.c +++ b/sound/soc/sof/ipc4-loader.c @@ -17,9 +17,8 @@ static size_t sof_ipc4_fw_parse_ext_man(struct snd_sof_dev *sdev) { 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; + const struct firmware *fw = sdev->basefw.fw; struct sof_ext_manifest4_hdr *ext_man_hdr; struct sof_man4_module_config *fm_config; struct sof_ipc4_fw_module *fw_module; @@ -138,9 +137,8 @@ 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; diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index 59e6be59258e..1e31b7c296e7 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -22,7 +22,7 @@ int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev) int ret;
/* Don't request firmware again if firmware is already requested */ - if (plat_data->fw) + if (sdev->basefw.fw) return 0;
fw_filename = kasprintf(GFP_KERNEL, "%s/%s", @@ -31,7 +31,7 @@ int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev) if (!fw_filename) return -ENOMEM;
- ret = request_firmware(&plat_data->fw, fw_filename, sdev->dev); + ret = request_firmware(&sdev->basefw.fw, fw_filename, sdev->dev);
if (ret < 0) { dev_err(sdev->dev, @@ -48,7 +48,7 @@ int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev) ext_man_size = sdev->ipc->ops->fw_loader->parse_ext_manifest(sdev); if (ext_man_size > 0) { /* when no error occurred, drop extended manifest */ - plat_data->fw_offset = ext_man_size; + sdev->basefw.payload_offset = ext_man_size; } else if (!ext_man_size) { /* No extended manifest, so nothing to skip during FW load */ dev_dbg(sdev->dev, "firmware doesn't contain extended manifest\n"); @@ -58,6 +58,12 @@ int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev) fw_filename, ret); }
+ /* + * Until the platform code is switched to use the new container the fw + * and payload offset must be set in plat_data + */ + plat_data->fw = sdev->basefw.fw; + plat_data->fw_offset = sdev->basefw.payload_offset; err: kfree(fw_filename);
@@ -100,7 +106,8 @@ int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev) return 0;
error: - release_firmware(plat_data->fw); + release_firmware(sdev->basefw.fw); + sdev->basefw.fw = NULL; plat_data->fw = NULL; return ret;
@@ -185,7 +192,8 @@ EXPORT_SYMBOL(snd_sof_run_firmware); void snd_sof_fw_unload(struct snd_sof_dev *sdev) { /* TODO: support module unloading at runtime */ - release_firmware(sdev->pdata->fw); + release_firmware(sdev->basefw.fw); + sdev->basefw.fw = NULL; sdev->pdata->fw = NULL; } EXPORT_SYMBOL(snd_sof_fw_unload); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index de08825915b3..3d70b57e4864 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -136,6 +136,17 @@ struct snd_sof_platform_stream_params { bool cont_update_posn; };
+/** + * struct sof_firmware - Container struct for SOF firmware + * @fw: Pointer to the firmware + * @payload_offset: Offset of the data within the loaded firmware image to be + * loaded to the DSP (skipping for example ext_manifest section) + */ +struct sof_firmware { + const struct firmware *fw; + u32 payload_offset; +}; + /* * SOF DSP HW abstraction operations. * Used to abstract DSP HW architecture and any IO busses between host CPU @@ -487,6 +498,9 @@ struct snd_sof_dev { spinlock_t ipc_lock; /* lock for IPC users */ spinlock_t hw_lock; /* lock for HW IO access */
+ /* Main, Base firmware image */ + struct sof_firmware basefw; + /* * ASoC components. plat_drv fields are set dynamically so * can't use const
Switch to access to the firmware struct via sdev->basefw container to unblock the removal of the firmware information from plat_data.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Chao Song chao.song@intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/soc/sof/amd/acp-loader.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/sound/soc/sof/amd/acp-loader.c b/sound/soc/sof/amd/acp-loader.c index d1e74baf5d8b..090c8b18c83c 100644 --- a/sound/soc/sof/amd/acp-loader.c +++ b/sound/soc/sof/amd/acp-loader.c @@ -48,7 +48,6 @@ EXPORT_SYMBOL_NS(acp_dsp_block_read, SND_SOC_SOF_AMD_COMMON); int acp_dsp_block_write(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, u32 offset, void *src, size_t size) { - struct snd_sof_pdata *plat_data = sdev->pdata; struct pci_dev *pci = to_pci_dev(sdev->dev); const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); struct acp_dev_data *adata; @@ -61,7 +60,7 @@ int acp_dsp_block_write(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_t switch (blk_type) { case SOF_FW_BLK_TYPE_IRAM: if (!adata->bin_buf) { - size_fw = plat_data->fw->size; + size_fw = sdev->basefw.fw->size; page_count = PAGE_ALIGN(size_fw) >> PAGE_SHIFT; dma_size = page_count * ACP_PAGE_SIZE; adata->bin_buf = dma_alloc_coherent(&pci->dev, dma_size, @@ -152,7 +151,6 @@ static void configure_pte_for_fw_loading(int type, int num_pages, struct acp_dev int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev) { struct pci_dev *pci = to_pci_dev(sdev->dev); - struct snd_sof_pdata *plat_data = sdev->pdata; struct acp_dev_data *adata; unsigned int src_addr, size_fw; u32 page_count, dma_size; @@ -186,7 +184,7 @@ int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev) dev_err(sdev->dev, "acp dma transfer status: %d\n", ret);
/* Free memory once DMA is complete */ - dma_size = (PAGE_ALIGN(plat_data->fw->size) >> PAGE_SHIFT) * ACP_PAGE_SIZE; + dma_size = (PAGE_ALIGN(sdev->basefw.fw->size) >> PAGE_SHIFT) * ACP_PAGE_SIZE; dma_free_coherent(&pci->dev, dma_size, adata->bin_buf, adata->sha_dma_addr); dma_free_coherent(&pci->dev, ACP_DEFAULT_DRAM_LENGTH, adata->data_buf, adata->dma_addr); adata->bin_buf = NULL;
Switch to access to the firmware struct via sdev->basefw container to unblock the removal of the firmware information from plat_data.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Chao Song chao.song@intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/soc/sof/intel/hda-loader.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 98812d51b31c..147ddc5ee932 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -318,7 +318,6 @@ int hda_cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream
int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev) { - struct snd_sof_pdata *plat_data = sdev->pdata; struct hdac_ext_stream *iccmax_stream; struct hdac_bus *bus = sof_to_bus(sdev); struct firmware stripped_firmware; @@ -329,12 +328,12 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev) /* save the original LTRP guardband value */ original_gb = snd_hdac_chip_readb(bus, VS_LTRP) & HDA_VS_INTEL_LTRP_GB_MASK;
- if (plat_data->fw->size <= plat_data->fw_offset) { + if (sdev->basefw.fw->size <= sdev->basefw.payload_offset) { dev_err(sdev->dev, "error: firmware size must be greater than firmware offset\n"); return -EINVAL; }
- stripped_firmware.size = plat_data->fw->size - plat_data->fw_offset; + stripped_firmware.size = sdev->basefw.fw->size - sdev->basefw.payload_offset;
/* prepare capture stream for ICCMAX */ iccmax_stream = hda_cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, stripped_firmware.size, @@ -405,13 +404,13 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev)
chip_info = desc->chip_info;
- if (plat_data->fw->size <= plat_data->fw_offset) { + if (sdev->basefw.fw->size <= sdev->basefw.payload_offset) { dev_err(sdev->dev, "error: firmware size must be greater than firmware offset\n"); return -EINVAL; }
- stripped_firmware.data = plat_data->fw->data + plat_data->fw_offset; - stripped_firmware.size = plat_data->fw->size - plat_data->fw_offset; + stripped_firmware.data = sdev->basefw.fw->data + sdev->basefw.payload_offset; + stripped_firmware.size = sdev->basefw.fw->size - sdev->basefw.payload_offset;
/* init for booting wait */ init_waitqueue_head(&sdev->boot_wait);
Switch to access to the firmware struct via sdev->basefw container to unblock the removal of the firmware information from plat_data.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Chao Song chao.song@intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/soc/sof/intel/hda-loader-skl.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/sound/soc/sof/intel/hda-loader-skl.c b/sound/soc/sof/intel/hda-loader-skl.c index 0193fb3964a0..3211f561db29 100644 --- a/sound/soc/sof/intel/hda-loader-skl.c +++ b/sound/soc/sof/intel/hda-loader-skl.c @@ -494,14 +494,13 @@ static int cl_copy_fw_skl(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab)
{ - struct snd_sof_pdata *plat_data = sdev->pdata; - const struct firmware *fw = plat_data->fw; + const struct firmware *fw = sdev->basefw.fw; struct firmware stripped_firmware; unsigned int bufsize = HDA_SKL_CLDMA_MAX_BUFFER_SIZE; int ret;
- stripped_firmware.data = plat_data->fw->data + plat_data->fw_offset; - stripped_firmware.size = plat_data->fw->size - plat_data->fw_offset; + stripped_firmware.data = fw->data + sdev->basefw.payload_offset; + stripped_firmware.size = fw->size - sdev->basefw.payload_offset;
dev_dbg(sdev->dev, "firmware size: %#zx buffer size %#x\n", fw->size, bufsize);
The SOF stack now uses the sdev->basefw to work with the SOF firmware, the information from plat_data can be dropped.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Chao Song chao.song@intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- include/sound/sof.h | 4 ---- sound/soc/sof/loader.c | 9 --------- 2 files changed, 13 deletions(-)
diff --git a/include/sound/sof.h b/include/sound/sof.h index 341fef19e612..e1f2f02666a7 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -59,15 +59,11 @@ enum sof_ipc_type { * SOF Platform data. */ struct snd_sof_pdata { - const struct firmware *fw; const char *name; const char *platform;
struct device *dev;
- /* indicate how many first bytes shouldn't be loaded into DSP memory. */ - size_t fw_offset; - /* * notification callback used if the hardware initialization * can take time or is handled in a workqueue. This callback diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index 1e31b7c296e7..723bd8267a3d 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -58,12 +58,6 @@ int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev) fw_filename, ret); }
- /* - * Until the platform code is switched to use the new container the fw - * and payload offset must be set in plat_data - */ - plat_data->fw = sdev->basefw.fw; - plat_data->fw_offset = sdev->basefw.payload_offset; err: kfree(fw_filename);
@@ -73,7 +67,6 @@ EXPORT_SYMBOL(snd_sof_load_firmware_raw);
int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev) { - struct snd_sof_pdata *plat_data = sdev->pdata; int ret;
ret = snd_sof_load_firmware_raw(sdev); @@ -108,7 +101,6 @@ int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev) error: release_firmware(sdev->basefw.fw); sdev->basefw.fw = NULL; - plat_data->fw = NULL; return ret;
} @@ -194,6 +186,5 @@ void snd_sof_fw_unload(struct snd_sof_dev *sdev) /* TODO: support module unloading at runtime */ release_firmware(sdev->basefw.fw); sdev->basefw.fw = NULL; - sdev->pdata->fw = NULL; } EXPORT_SYMBOL(snd_sof_fw_unload);
Add support for IPC specific initialization (init) and cleanup (exit) callback.
These callbacks can be used by IPC implementation to do basic initialization and cleanup.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Chao Song chao.song@intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/soc/sof/ipc.c | 6 ++++++ sound/soc/sof/sof-priv.h | 6 ++++++ 2 files changed, 12 insertions(+)
diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 6ed3f9b6a0c4..30781e808a02 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -200,6 +200,9 @@ struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev) return NULL; }
+ if (ops->init && ops->init(sdev)) + return NULL; + ipc->ops = ops;
return ipc; @@ -217,5 +220,8 @@ void snd_sof_ipc_free(struct snd_sof_dev *sdev) mutex_lock(&ipc->tx_mutex); ipc->disable_ipc_tx = true; mutex_unlock(&ipc->tx_mutex); + + if (ipc->ops->exit) + ipc->ops->exit(sdev); } EXPORT_SYMBOL(snd_sof_ipc_free); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 3d70b57e4864..ea6013ab1d4a 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -443,6 +443,9 @@ struct sof_ipc_pcm_ops; * @fw_loader: Pointer to Firmware Loader ops * @fw_tracing: Pointer to Firmware tracing ops * + * @init: Optional pointer for IPC related initialization + * @exit: Optional pointer for IPC related cleanup + * * @tx_msg: Function pointer for sending a 'short' IPC message * @set_get_data: Function pointer for set/get data ('large' IPC message). This * function may split up the 'large' message and use the @tx_msg @@ -464,6 +467,9 @@ struct sof_ipc_ops { const struct sof_ipc_fw_loader_ops *fw_loader; const struct sof_ipc_fw_tracing_ops *fw_tracing;
+ int (*init)(struct snd_sof_dev *sdev); + void (*exit)(struct snd_sof_dev *sdev); + int (*tx_msg)(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes, void *reply_data, size_t reply_bytes, bool no_pm); int (*set_get_data)(struct snd_sof_dev *sdev, void *data, size_t data_bytes,
The firmware supports external libraries (containing modules) to be loaded runtime. The firmware configuration contains the maximum number of libraries supported, including the base firmware (which is library 0).
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Chao Song chao.song@intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/soc/sof/ipc4-loader.c | 7 +++++++ sound/soc/sof/ipc4-priv.h | 3 +++ 2 files changed, 10 insertions(+)
diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c index 9f433e9b4cd3..2495a205ef78 100644 --- a/sound/soc/sof/ipc4-loader.c +++ b/sound/soc/sof/ipc4-loader.c @@ -202,6 +202,13 @@ 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; default: break; } diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h index e3b8484a2f1f..7f5c7a47b3a7 100644 --- a/sound/soc/sof/ipc4-priv.h +++ b/sound/soc/sof/ipc4-priv.h @@ -32,6 +32,8 @@ 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_libs_count: Maximum number of libraries support by the FW including the + * base firmware */ struct sof_ipc4_fw_data { u32 manifest_fw_hdr_offset; @@ -40,6 +42,7 @@ struct sof_ipc4_fw_data { void *nhlt; enum sof_ipc4_mtrace_type mtrace_type; u32 mtrace_log_bytes; + u32 max_libs_count; };
/**
With IPC4 each DSP loadable binary is a library, which contains ext_manifest section and loadable modules. The basefw is no exception, it is always library 0 and it can contain several modules, depending on the firmware build.
The current code assumes only one binary, which is the basefw and has no concept of libraries. This patch introduces the library+modules abstraction and represents the basefw as library for the IPC4 loader codebase. The basefw loading and handling is not changing, it is still done by the generic code, but it's information is cloned under the library representation.
The libraries are managed via XArray to offload the list and ID management.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Chao Song chao.song@intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/soc/sof/ipc4-loader.c | 45 +++++++++++++++++++++++------ sound/soc/sof/ipc4-priv.h | 53 +++++++++++++++++++++++------------ sound/soc/sof/ipc4-topology.c | 20 ++++++------- sound/soc/sof/ipc4.c | 32 +++++++++++++++++++++ 4 files changed, 113 insertions(+), 37 deletions(-)
diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c index 2495a205ef78..5506ec997328 100644 --- a/sound/soc/sof/ipc4-loader.c +++ b/sound/soc/sof/ipc4-loader.c @@ -14,11 +14,12 @@ #include "sof-priv.h" #include "ops.h"
-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, + struct sof_ipc4_fw_library *fw_lib) { struct sof_ipc4_fw_data *ipc4_data = sdev->private; + const struct firmware *fw = fw_lib->sof_fw.fw; struct sof_man4_fw_binary_header *fw_header; - const struct firmware *fw = sdev->basefw.fw; struct sof_ext_manifest4_hdr *ext_man_hdr; struct sof_man4_module_config *fm_config; struct sof_ipc4_fw_module *fw_module; @@ -76,14 +77,13 @@ static size_t sof_ipc4_fw_parse_ext_man(struct snd_sof_dev *sdev) 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) + 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->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; @@ -133,6 +133,33 @@ 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; + size_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_validate_firmware(struct snd_sof_dev *sdev) { struct sof_ipc4_fw_data *ipc4_data = sdev->private; @@ -224,6 +251,6 @@ static int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev)
const struct sof_ipc_fw_loader_ops ipc4_loader_ops = { .validate = sof_ipc4_validate_firmware, - .parse_ext_manifest = sof_ipc4_fw_parse_ext_man, + .parse_ext_manifest = sof_ipc4_fw_parse_basefw_ext_man, .query_fw_configuration = sof_ipc4_query_fw_configuration, }; diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h index 7f5c7a47b3a7..bce168083f09 100644 --- a/sound/soc/sof/ipc4-priv.h +++ b/sound/soc/sof/ipc4-priv.h @@ -24,11 +24,43 @@ enum sof_ipc4_mtrace_type { SOF_IPC4_MTRACE_INTEL_CAVS_2, };
+/** + * struct sof_ipc4_fw_module - IPC4 module info + * @sof_man4_module: Module info + * @m_ida: Module instance identifier + * @bss_size: Module object size + * @private: Module private data + */ +struct sof_ipc4_fw_module { + struct sof_man4_module man4_module_entry; + struct ida m_ida; + u32 bss_size; + void *private; +}; + +/** + * struct sof_ipc4_fw_library - IPC4 library information + * @sof_fw: SOF Firmware of the library + * @id: Library ID. 0 is reserved for basefw, external libraries must have unique + * ID number between 1 and (sof_ipc4_fw_data.max_libs_count - 1) + * Note: sof_ipc4_fw_data.max_libs_count == 1 implies that external libraries + * are not supported + * @num_modules : Number of FW modules in the library + * @modules: Array of FW modules + */ +struct sof_ipc4_fw_library { + struct sof_firmware sof_fw; + u32 id; + int num_modules; + struct sof_ipc4_fw_module *modules; +}; + /** * struct sof_ipc4_fw_data - IPC4-specific data * @manifest_fw_hdr_offset: FW header offset in the manifest - * @num_fw_modules : Number of modules in base FW - * @fw_modules: Array of base FW modules + * @fw_lib_xa: XArray for firmware libraries, including basefw (ID = 0) + * Used to store the FW libraries and to manage the unique IDs of the + * libraries. * @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 @@ -37,28 +69,13 @@ enum sof_ipc4_mtrace_type { */ struct sof_ipc4_fw_data { u32 manifest_fw_hdr_offset; - int num_fw_modules; - void *fw_modules; + struct xarray fw_lib_xa; void *nhlt; enum sof_ipc4_mtrace_type mtrace_type; u32 mtrace_log_bytes; u32 max_libs_count; };
-/** - * struct sof_ipc4_fw_module - IPC4 module info - * @sof_man4_module : Module info - * @m_ida: Module instance identifier - * @bss_size: Module object size - * @private: Module private data - */ -struct sof_ipc4_fw_module { - struct sof_man4_module man4_module_entry; - struct ida m_ida; - u32 bss_size; - void *private; -}; - extern const struct sof_ipc_fw_loader_ops ipc4_loader_ops; extern const struct sof_ipc_tplg_ops ipc4_tplg_ops; extern const struct sof_ipc_tplg_control_ops tplg_ipc4_control_ops; diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index a81af5f73a4b..98f7f5421ba5 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -290,19 +290,19 @@ static int sof_ipc4_widget_set_module_info(struct snd_sof_widget *swidget) struct snd_soc_component *scomp = swidget->scomp; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_ipc4_fw_data *ipc4_data = sdev->private; - struct sof_ipc4_fw_module *fw_modules = ipc4_data->fw_modules; + struct sof_ipc4_fw_library *fw_lib; + unsigned long lib_id; int i;
- if (!fw_modules) { - dev_err(sdev->dev, "no fw_module information\n"); - return -EINVAL; - } + xa_for_each(&ipc4_data->fw_lib_xa, lib_id, fw_lib) { + /* set module info */ + for (i = 0; i < fw_lib->num_modules; i++) { + struct sof_ipc4_fw_module *module = &fw_lib->modules[i];
- /* set module info */ - for (i = 0; i < ipc4_data->num_fw_modules; i++) { - if (guid_equal(&swidget->uuid, &fw_modules[i].man4_module_entry.uuid)) { - swidget->module_info = &fw_modules[i]; - return 0; + if (guid_equal(&swidget->uuid, &module->man4_module_entry.uuid)) { + swidget->module_info = module; + return 0; + } } }
diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c index 3c9b8692984a..cafbd75d8762 100644 --- a/sound/soc/sof/ipc4.c +++ b/sound/soc/sof/ipc4.c @@ -8,6 +8,7 @@ // Authors: Rander Wang rander.wang@linux.intel.com // Peter Ujfalusi peter.ujfalusi@linux.intel.com // +#include <linux/firmware.h> #include <sound/sof/header.h> #include <sound/sof/ipc4/header.h> #include "sof-priv.h" @@ -657,7 +658,38 @@ static const struct sof_ipc_pm_ops ipc4_pm_ops = { .set_core_state = sof_ipc4_set_core_state, };
+static int sof_ipc4_init(struct snd_sof_dev *sdev) +{ + struct sof_ipc4_fw_data *ipc4_data = sdev->private; + + xa_init_flags(&ipc4_data->fw_lib_xa, XA_FLAGS_ALLOC); + + return 0; +} + +static void sof_ipc4_exit(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; + + xa_for_each(&ipc4_data->fw_lib_xa, lib_id, fw_lib) { + /* + * The basefw (ID == 0) is handled by generic code, it is not + * loaded by IPC4 code. + */ + if (lib_id != 0) + release_firmware(fw_lib->sof_fw.fw); + + fw_lib->sof_fw.fw = NULL; + } + + xa_destroy(&ipc4_data->fw_lib_xa); +} + const struct sof_ipc_ops ipc4_ops = { + .init = sof_ipc4_init, + .exit = sof_ipc4_exit, .tx_msg = sof_ipc4_tx_msg, .rx_msg = sof_ipc4_rx_msg, .set_get_data = sof_ipc4_set_get_data,
Add a simple helper to walk the loaded libraries and their modules to make the ipc4-topology not aware of the underlying infrastructure and simplify the code.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Chao Song chao.song@intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/soc/sof/ipc4-loader.c | 21 +++++++++++++++++++++ sound/soc/sof/ipc4-priv.h | 3 +++ sound/soc/sof/ipc4-topology.c | 17 +++-------------- 3 files changed, 27 insertions(+), 14 deletions(-)
diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c index 5506ec997328..b7e8b3f3d4f0 100644 --- a/sound/soc/sof/ipc4-loader.c +++ b/sound/soc/sof/ipc4-loader.c @@ -160,6 +160,27 @@ static size_t sof_ipc4_fw_parse_basefw_ext_man(struct snd_sof_dev *sdev) return payload_offset; }
+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; + + 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]; + } + } + + return NULL; +} + static int sof_ipc4_validate_firmware(struct snd_sof_dev *sdev) { struct sof_ipc4_fw_data *ipc4_data = sdev->private; diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h index bce168083f09..ecfa9f701ef1 100644 --- a/sound/soc/sof/ipc4-priv.h +++ b/sound/soc/sof/ipc4-priv.h @@ -84,4 +84,7 @@ extern const struct sof_ipc_fw_tracing_ops ipc4_mtrace_ops;
int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 id, u32 state); int sof_ipc4_mtrace_update_pos(struct snd_sof_dev *sdev, int core); + +struct sof_ipc4_fw_module *sof_ipc4_find_module_by_uuid(struct snd_sof_dev *sdev, + const guid_t *uuid); #endif diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 98f7f5421ba5..ab85dde4303b 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -289,22 +289,11 @@ static int sof_ipc4_widget_set_module_info(struct snd_sof_widget *swidget) { struct snd_soc_component *scomp = swidget->scomp; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct sof_ipc4_fw_data *ipc4_data = sdev->private; - struct sof_ipc4_fw_library *fw_lib; - unsigned long lib_id; - int i;
- xa_for_each(&ipc4_data->fw_lib_xa, lib_id, fw_lib) { - /* set module info */ - for (i = 0; i < fw_lib->num_modules; i++) { - struct sof_ipc4_fw_module *module = &fw_lib->modules[i]; + swidget->module_info = sof_ipc4_find_module_by_uuid(sdev, &swidget->uuid);
- if (guid_equal(&swidget->uuid, &module->man4_module_entry.uuid)) { - swidget->module_info = module; - return 0; - } - } - } + if (swidget->module_info) + return 0;
dev_err(sdev->dev, "failed to find module info for widget %s with UUID %pUL\n", swidget->widget->name, &swidget->uuid);
IPC4 based firmware supports dynamically loaded external libraries. The libraries will be not stored alongside of the firmware or tplg files.
For intel platforms the default path will be: intel/avs-lib|sof-ipc4-lib/<platform>/ if a community key is used on the given machine then the libraries will be under 'community' directory, like it is done for the firmware itself.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Chao Song chao.song@intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- include/sound/sof.h | 6 +++++- sound/soc/sof/intel/pci-tgl.c | 3 +++ sound/soc/sof/sof-pci-dev.c | 26 ++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/include/sound/sof.h b/include/sound/sof.h index e1f2f02666a7..266e66318f9c 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -82,6 +82,9 @@ struct snd_sof_pdata { const char *tplg_filename_prefix; const char *tplg_filename;
+ /* loadable external libraries available under this directory */ + const char *fw_lib_prefix; + /* machine */ struct platform_device *pdev_mach; const struct snd_soc_acpi_mach *machine; @@ -127,8 +130,9 @@ struct sof_dev_desc { unsigned int ipc_supported_mask; enum sof_ipc_type ipc_default;
- /* defaults paths for firmware and topology files */ + /* defaults paths for firmware, library and topology files */ const char *default_fw_path[SOF_IPC_TYPE_COUNT]; + const char *default_lib_path[SOF_IPC_TYPE_COUNT]; const char *default_tplg_path[SOF_IPC_TYPE_COUNT];
/* default firmware name */ diff --git a/sound/soc/sof/intel/pci-tgl.c b/sound/soc/sof/intel/pci-tgl.c index 4cfe4f242fc5..757a7c6bb770 100644 --- a/sound/soc/sof/intel/pci-tgl.c +++ b/sound/soc/sof/intel/pci-tgl.c @@ -174,6 +174,9 @@ static const struct sof_dev_desc adl_n_desc = { [SOF_IPC] = "intel/sof", [SOF_INTEL_IPC4] = "intel/avs/adl-n", }, + .default_lib_path = { + [SOF_INTEL_IPC4] = "intel/avs-lib/adl-n", + }, .default_tplg_path = { [SOF_IPC] = "intel/sof-tplg", [SOF_INTEL_IPC4] = "intel/avs-tplg", diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index 643fd1036d60..f5ece43d0ec2 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -28,6 +28,10 @@ static char *fw_filename; module_param(fw_filename, charp, 0444); MODULE_PARM_DESC(fw_filename, "alternate filename for SOF firmware.");
+static char *lib_path; +module_param(lib_path, charp, 0444); +MODULE_PARM_DESC(lib_path, "alternate path for SOF firmware libraries."); + static char *tplg_path; module_param(tplg_path, charp, 0444); MODULE_PARM_DESC(tplg_path, "alternate path for SOF topology."); @@ -272,6 +276,28 @@ int sof_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) sof_pdata->desc->default_fw_path[sof_pdata->ipc_type]; }
+ if (lib_path) { + sof_pdata->fw_lib_prefix = lib_path; + + dev_dbg(dev, "Module parameter used, changed fw_lib path to %s\n", + sof_pdata->fw_lib_prefix); + + } else if (sof_pdata->desc->default_lib_path[sof_pdata->ipc_type]) { + if (dmi_check_system(community_key_platforms) && sof_dmi_use_community_key) { + sof_pdata->fw_lib_prefix = + devm_kasprintf(dev, GFP_KERNEL, "%s/%s", + sof_pdata->desc->default_lib_path[sof_pdata->ipc_type], + "community"); + + dev_dbg(dev, + "Platform uses community key, changed fw_lib path to %s\n", + sof_pdata->fw_lib_prefix); + } else { + sof_pdata->fw_lib_prefix = + sof_pdata->desc->default_lib_path[sof_pdata->ipc_type]; + } + } + if (tplg_path) sof_pdata->tplg_filename_prefix = tplg_path; else
The default path for the external firmware libraries are: intel/avs-lib/<platform> or intel/sof-ipc4-lib/<platform>
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Chao Song chao.song@intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/soc/sof/intel/pci-apl.c | 6 ++++++ sound/soc/sof/intel/pci-cnl.c | 9 +++++++++ sound/soc/sof/intel/pci-icl.c | 6 ++++++ sound/soc/sof/intel/pci-mtl.c | 3 +++ sound/soc/sof/intel/pci-tgl.c | 21 +++++++++++++++++++++ 5 files changed, 45 insertions(+)
diff --git a/sound/soc/sof/intel/pci-apl.c b/sound/soc/sof/intel/pci-apl.c index 998e219011f0..69279dcc92dc 100644 --- a/sound/soc/sof/intel/pci-apl.c +++ b/sound/soc/sof/intel/pci-apl.c @@ -33,6 +33,9 @@ static const struct sof_dev_desc bxt_desc = { [SOF_IPC] = "intel/sof", [SOF_INTEL_IPC4] = "intel/avs/apl", }, + .default_lib_path = { + [SOF_INTEL_IPC4] = "intel/avs-lib/apl", + }, .default_tplg_path = { [SOF_IPC] = "intel/sof-tplg", [SOF_INTEL_IPC4] = "intel/avs-tplg", @@ -61,6 +64,9 @@ static const struct sof_dev_desc glk_desc = { [SOF_IPC] = "intel/sof", [SOF_INTEL_IPC4] = "intel/avs/glk", }, + .default_lib_path = { + [SOF_INTEL_IPC4] = "intel/avs-lib/glk", + }, .default_tplg_path = { [SOF_IPC] = "intel/sof-tplg", [SOF_INTEL_IPC4] = "intel/avs-tplg", diff --git a/sound/soc/sof/intel/pci-cnl.c b/sound/soc/sof/intel/pci-cnl.c index c797356f7028..8db3f8d15b55 100644 --- a/sound/soc/sof/intel/pci-cnl.c +++ b/sound/soc/sof/intel/pci-cnl.c @@ -34,6 +34,9 @@ static const struct sof_dev_desc cnl_desc = { [SOF_IPC] = "intel/sof", [SOF_INTEL_IPC4] = "intel/avs/cnl", }, + .default_lib_path = { + [SOF_INTEL_IPC4] = "intel/avs-lib/cnl", + }, .default_tplg_path = { [SOF_IPC] = "intel/sof-tplg", [SOF_INTEL_IPC4] = "intel/avs-tplg", @@ -62,6 +65,9 @@ static const struct sof_dev_desc cfl_desc = { [SOF_IPC] = "intel/sof", [SOF_INTEL_IPC4] = "intel/avs/cnl", }, + .default_lib_path = { + [SOF_INTEL_IPC4] = "intel/avs-lib/cnl", + }, .default_tplg_path = { [SOF_IPC] = "intel/sof-tplg", [SOF_INTEL_IPC4] = "intel/avs-tplg", @@ -91,6 +97,9 @@ static const struct sof_dev_desc cml_desc = { [SOF_IPC] = "intel/sof", [SOF_INTEL_IPC4] = "intel/avs/cnl", }, + .default_lib_path = { + [SOF_INTEL_IPC4] = "intel/avs-lib/cnl", + }, .default_tplg_path = { [SOF_IPC] = "intel/sof-tplg", [SOF_INTEL_IPC4] = "intel/avs-tplg", diff --git a/sound/soc/sof/intel/pci-icl.c b/sound/soc/sof/intel/pci-icl.c index 48f24f8ace26..d6cf75e357db 100644 --- a/sound/soc/sof/intel/pci-icl.c +++ b/sound/soc/sof/intel/pci-icl.c @@ -34,6 +34,9 @@ static const struct sof_dev_desc icl_desc = { [SOF_IPC] = "intel/sof", [SOF_INTEL_IPC4] = "intel/avs/icl", }, + .default_lib_path = { + [SOF_INTEL_IPC4] = "intel/avs-lib/icl", + }, .default_tplg_path = { [SOF_IPC] = "intel/sof-tplg", [SOF_INTEL_IPC4] = "intel/avs-tplg", @@ -62,6 +65,9 @@ static const struct sof_dev_desc jsl_desc = { [SOF_IPC] = "intel/sof", [SOF_INTEL_IPC4] = "intel/avs/jsl", }, + .default_lib_path = { + [SOF_INTEL_IPC4] = "intel/avs-lib/jsl", + }, .default_tplg_path = { [SOF_IPC] = "intel/sof-tplg", [SOF_INTEL_IPC4] = "intel/avs-tplg", diff --git a/sound/soc/sof/intel/pci-mtl.c b/sound/soc/sof/intel/pci-mtl.c index 9f39da984e9f..6e4e6d4ef5a5 100644 --- a/sound/soc/sof/intel/pci-mtl.c +++ b/sound/soc/sof/intel/pci-mtl.c @@ -34,6 +34,9 @@ static const struct sof_dev_desc mtl_desc = { .default_fw_path = { [SOF_INTEL_IPC4] = "intel/sof-ipc4/mtl", }, + .default_lib_path = { + [SOF_INTEL_IPC4] = "intel/sof-ipc4-lib/mtl", + }, .default_tplg_path = { [SOF_INTEL_IPC4] = "intel/sof-ace-tplg", }, diff --git a/sound/soc/sof/intel/pci-tgl.c b/sound/soc/sof/intel/pci-tgl.c index 757a7c6bb770..e80c4dfef85a 100644 --- a/sound/soc/sof/intel/pci-tgl.c +++ b/sound/soc/sof/intel/pci-tgl.c @@ -34,6 +34,9 @@ static const struct sof_dev_desc tgl_desc = { [SOF_IPC] = "intel/sof", [SOF_INTEL_IPC4] = "intel/avs/tgl", }, + .default_lib_path = { + [SOF_INTEL_IPC4] = "intel/avs-lib/tgl", + }, .default_tplg_path = { [SOF_IPC] = "intel/sof-tplg", [SOF_INTEL_IPC4] = "intel/avs-tplg", @@ -62,6 +65,9 @@ static const struct sof_dev_desc tglh_desc = { [SOF_IPC] = "intel/sof", [SOF_INTEL_IPC4] = "intel/avs/tgl-h", }, + .default_lib_path = { + [SOF_INTEL_IPC4] = "intel/avs-lib/tgl-h", + }, .default_tplg_path = { [SOF_IPC] = "intel/sof-tplg", [SOF_INTEL_IPC4] = "intel/avs-tplg", @@ -90,6 +96,9 @@ static const struct sof_dev_desc ehl_desc = { [SOF_IPC] = "intel/sof", [SOF_INTEL_IPC4] = "intel/avs/ehl", }, + .default_lib_path = { + [SOF_INTEL_IPC4] = "intel/avs-lib/ehl", + }, .default_tplg_path = { [SOF_IPC] = "intel/sof-tplg", [SOF_INTEL_IPC4] = "intel/avs-tplg", @@ -118,6 +127,9 @@ static const struct sof_dev_desc adls_desc = { [SOF_IPC] = "intel/sof", [SOF_INTEL_IPC4] = "intel/avs/adl-s", }, + .default_lib_path = { + [SOF_INTEL_IPC4] = "intel/avs-lib/adl-s", + }, .default_tplg_path = { [SOF_IPC] = "intel/sof-tplg", [SOF_INTEL_IPC4] = "intel/avs-tplg", @@ -146,6 +158,9 @@ static const struct sof_dev_desc adl_desc = { [SOF_IPC] = "intel/sof", [SOF_INTEL_IPC4] = "intel/avs/adl", }, + .default_lib_path = { + [SOF_INTEL_IPC4] = "intel/avs-lib/adl", + }, .default_tplg_path = { [SOF_IPC] = "intel/sof-tplg", [SOF_INTEL_IPC4] = "intel/avs-tplg", @@ -205,6 +220,9 @@ static const struct sof_dev_desc rpls_desc = { [SOF_IPC] = "intel/sof", [SOF_INTEL_IPC4] = "intel/avs/rpl-s", }, + .default_lib_path = { + [SOF_INTEL_IPC4] = "intel/avs-lib/rpl-s", + }, .default_tplg_path = { [SOF_IPC] = "intel/sof-tplg", [SOF_INTEL_IPC4] = "intel/avs-tplg", @@ -233,6 +251,9 @@ static const struct sof_dev_desc rpl_desc = { [SOF_IPC] = "intel/sof", [SOF_INTEL_IPC4] = "intel/avs/rpl", }, + .default_lib_path = { + [SOF_INTEL_IPC4] = "intel/avs-lib/rpl", + }, .default_tplg_path = { [SOF_IPC] = "intel/sof-tplg", [SOF_INTEL_IPC4] = "intel/avs-tplg",
Platforms where external libraries can be supported should set the load_library callback to implement this functionality.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Chao Song chao.song@intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/soc/sof/ipc4-priv.h | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h index ecfa9f701ef1..7e7115ada2a2 100644 --- a/sound/soc/sof/ipc4-priv.h +++ b/sound/soc/sof/ipc4-priv.h @@ -66,6 +66,8 @@ struct sof_ipc4_fw_library { * @mtrace_log_bytes: log bytes as reported by the firmware via fw_config reply * @max_libs_count: Maximum number of libraries support by the FW including the * base firmware + * + * @load_library: Callback function for platform dependent library loading */ struct sof_ipc4_fw_data { u32 manifest_fw_hdr_offset; @@ -74,6 +76,9 @@ struct sof_ipc4_fw_data { enum sof_ipc4_mtrace_type mtrace_type; u32 mtrace_log_bytes; u32 max_libs_count; + + int (*load_library)(struct snd_sof_dev *sdev, + struct sof_ipc4_fw_library *fw_lib, bool reload); };
extern const struct sof_ipc_fw_loader_ops ipc4_loader_ops;
Dynamic loading of external libraries should not be done if the firmware was booted from IMR since in that case the libraries will be restored along with the basefw.
The booted_from_imr flag is introduced and set to true if the IMR boot was successful and to false if cold booting is executed.
The reason for the new flag is that guessing from existing flags, used to decide if we should try booting from IMR or not is not going to be robust as the IMR boot itself can fail and in that case a full, cold boot is executed.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Chao Song chao.song@intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/soc/sof/intel/hda-loader.c | 6 +++++- sound/soc/sof/intel/hda.h | 1 + 2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 147ddc5ee932..5ed524e166d2 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -396,12 +396,16 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) dev_dbg(sdev->dev, "IMR restore supported, booting from IMR directly\n"); hda->boot_iteration = 0; ret = hda_dsp_boot_imr(sdev); - if (!ret) + if (!ret) { + hda->booted_from_imr = true; return 0; + }
dev_warn(sdev->dev, "IMR restore failed, trying to cold boot\n"); }
+ hda->booted_from_imr = false; + chip_info = desc->chip_info;
if (sdev->basefw.fw->size <= sdev->basefw.payload_offset) { diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 65657d145dc2..4d9eb321a046 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -481,6 +481,7 @@ enum sof_hda_D0_substate { struct sof_intel_hda_dev { bool imrboot_supported; bool skip_imr_boot; + bool booted_from_imr;
int boot_iteration;
On Intel HDA platforms the library loading is done via DMA and an IPC message is also need to be sent to initiate the downloading of the new library.
Co-developed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Chao Song chao.song@intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- include/sound/sof/ipc4/header.h | 4 ++ sound/soc/sof/intel/apl.c | 3 ++ sound/soc/sof/intel/cnl.c | 3 ++ sound/soc/sof/intel/hda-loader.c | 66 ++++++++++++++++++++++++++++++++ sound/soc/sof/intel/hda.h | 3 ++ sound/soc/sof/intel/icl.c | 3 ++ sound/soc/sof/intel/mtl.c | 3 ++ sound/soc/sof/intel/tgl.c | 3 ++ 8 files changed, 88 insertions(+)
diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h index 99efe0ef1784..622193be7ac4 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 1549ca7587a4..d93b4ead3c37 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -62,6 +62,9 @@ int sof_apl_ops_init(struct snd_sof_dev *sdev)
ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_1_5;
+ /* External library loading support */ + ipc4_data->load_library = hda_dsp_ipc4_load_library; + /* doorbell */ sof_apl_ops.irq_thread = hda_dsp_ipc4_irq_thread;
diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index da26f0ce9abc..2553afe6f27d 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -407,6 +407,9 @@ int sof_cnl_ops_init(struct snd_sof_dev *sdev)
ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_1_8;
+ /* External library loading support */ + ipc4_data->load_library = hda_dsp_ipc4_load_library; + /* doorbell */ sof_cnl_ops.irq_thread = cnl_ipc4_irq_thread;
diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 5ed524e166d2..38204541fc5d 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -19,7 +19,9 @@ #include <sound/hdaudio_ext.h> #include <sound/hda_register.h> #include <sound/sof.h> +#include <sound/sof/ipc4/header.h> #include "ext_manifest.h" +#include "../ipc4-priv.h" #include "../ops.h" #include "../sof-priv.h" #include "hda.h" @@ -518,6 +520,70 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) return ret; }
+int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev, + struct sof_ipc4_fw_library *fw_lib, bool reload) +{ + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + struct hdac_ext_stream *hext_stream; + struct firmware stripped_firmware; + struct sof_ipc4_msg msg = {}; + struct snd_dma_buffer dmab; + int ret, ret1; + + /* IMR booting will restore the libraries as well, skip the loading */ + if (reload && hda->booted_from_imr) + return 0; + + /* the fw_lib has been verified during loading, we can trust the validity here */ + stripped_firmware.data = fw_lib->sof_fw.fw->data + fw_lib->sof_fw.payload_offset; + stripped_firmware.size = fw_lib->sof_fw.fw->size - fw_lib->sof_fw.payload_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, "%s: DMA prepare failed\n", __func__); + 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(fw_lib->id); + + ret = cl_trigger(sdev, hext_stream, SNDRV_PCM_TRIGGER_START); + if (ret < 0) { + dev_err(sdev->dev, "%s: DMA trigger start failed\n", __func__); + 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, "%s: DMA trigger stop failed\n", __func__); + 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, "%s: Code loader DSP cleanup failed\n", __func__); + + /* set return value to indicate cleanup failure */ + if (!ret) + ret = ret1; + } + + return ret; +} + /* pre fw run operations */ int hda_dsp_pre_fw_run(struct snd_sof_dev *sdev) { diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 4d9eb321a046..c91fc3637823 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -866,4 +866,7 @@ int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) void hda_ipc4_dump(struct snd_sof_dev *sdev); extern struct sdw_intel_ops sdw_callback;
+struct sof_ipc4_fw_library; +int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev, + struct sof_ipc4_fw_library *fw_lib, bool reload); #endif diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c index 6d5877108a3d..f95b2ec57077 100644 --- a/sound/soc/sof/intel/icl.c +++ b/sound/soc/sof/intel/icl.c @@ -130,6 +130,9 @@ int sof_icl_ops_init(struct snd_sof_dev *sdev)
ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_2;
+ /* External library loading support */ + ipc4_data->load_library = hda_dsp_ipc4_load_library; + /* doorbell */ sof_icl_ops.irq_thread = cnl_ipc4_irq_thread;
diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c index 054b9ab721ff..7e8b298786df 100644 --- a/sound/soc/sof/intel/mtl.c +++ b/sound/soc/sof/intel/mtl.c @@ -659,6 +659,9 @@ int sof_mtl_ops_init(struct snd_sof_dev *sdev)
ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_2;
+ /* External library loading support */ + ipc4_data->load_library = hda_dsp_ipc4_load_library; + /* set DAI ops */ hda_set_dai_drv_ops(sdev, &sof_mtl_ops);
diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c index 9ae2890e9dac..143447f7c1ac 100644 --- a/sound/soc/sof/intel/tgl.c +++ b/sound/soc/sof/intel/tgl.c @@ -85,6 +85,9 @@ int sof_tgl_ops_init(struct snd_sof_dev *sdev)
ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_2;
+ /* External library loading support */ + ipc4_data->load_library = hda_dsp_ipc4_load_library; + /* doorbell */ sof_tgl_ops.irq_thread = cnl_ipc4_irq_thread;
Add support for executing IPC dependent tasks after a successful firmware boot.
The new post_fw_boot ops can make the fw_loader query_fw_configuration callback redundant as IPC code can handle the first boot internally.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Chao Song chao.song@intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/soc/sof/loader.c | 6 ++++++ sound/soc/sof/sof-priv.h | 3 +++ 2 files changed, 9 insertions(+)
diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index 723bd8267a3d..a1c4a51636c9 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -174,6 +174,12 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev) return ret; }
+ if (sdev->ipc->ops->post_fw_boot) { + ret = sdev->ipc->ops->post_fw_boot(sdev); + if (ret) + return ret; + } + 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-priv.h b/sound/soc/sof/sof-priv.h index ea6013ab1d4a..c7ab78b042aa 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -445,6 +445,8 @@ struct sof_ipc_pcm_ops; * * @init: Optional pointer for IPC related initialization * @exit: Optional pointer for IPC related cleanup + * @post_fw_boot: Optional pointer to execute IPC related tasks after firmware + * boot. * * @tx_msg: Function pointer for sending a 'short' IPC message * @set_get_data: Function pointer for set/get data ('large' IPC message). This @@ -469,6 +471,7 @@ struct sof_ipc_ops {
int (*init)(struct snd_sof_dev *sdev); void (*exit)(struct snd_sof_dev *sdev); + int (*post_fw_boot)(struct snd_sof_dev *sdev);
int (*tx_msg)(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes, void *reply_data, size_t reply_bytes, bool no_pm);
Execute the configuration query from the generic post_fw_boot callback and do not set the query_fw_configuration ops to allow it's removal.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Chao Song chao.song@intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/soc/sof/ipc4-loader.c | 3 +-- sound/soc/sof/ipc4-priv.h | 1 + sound/soc/sof/ipc4.c | 9 +++++++++ 3 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c index b7e8b3f3d4f0..dbe3ee4ef08c 100644 --- a/sound/soc/sof/ipc4-loader.c +++ b/sound/soc/sof/ipc4-loader.c @@ -202,7 +202,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; @@ -273,5 +273,4 @@ static int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev) const struct sof_ipc_fw_loader_ops ipc4_loader_ops = { .validate = sof_ipc4_validate_firmware, .parse_ext_manifest = sof_ipc4_fw_parse_basefw_ext_man, - .query_fw_configuration = sof_ipc4_query_fw_configuration, }; diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h index 7e7115ada2a2..e4bd6d93fb0f 100644 --- a/sound/soc/sof/ipc4-priv.h +++ b/sound/soc/sof/ipc4-priv.h @@ -90,6 +90,7 @@ extern const struct sof_ipc_fw_tracing_ops ipc4_mtrace_ops; int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 id, u32 state); int sof_ipc4_mtrace_update_pos(struct snd_sof_dev *sdev, int core);
+int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev); struct sof_ipc4_fw_module *sof_ipc4_find_module_by_uuid(struct snd_sof_dev *sdev, const guid_t *uuid); #endif diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c index cafbd75d8762..91c4ea72b4b6 100644 --- a/sound/soc/sof/ipc4.c +++ b/sound/soc/sof/ipc4.c @@ -687,9 +687,18 @@ static void sof_ipc4_exit(struct snd_sof_dev *sdev) xa_destroy(&ipc4_data->fw_lib_xa); }
+static int sof_ipc4_post_boot(struct snd_sof_dev *sdev) +{ + if (sdev->first_boot) + return sof_ipc4_query_fw_configuration(sdev); + + return 0; +} + const struct sof_ipc_ops ipc4_ops = { .init = sof_ipc4_init, .exit = sof_ipc4_exit, + .post_fw_boot = sof_ipc4_post_boot, .tx_msg = sof_ipc4_tx_msg, .rx_msg = sof_ipc4_rx_msg, .set_get_data = sof_ipc4_set_get_data,
The query_fw_configuration callback is redundant and the only user of it was converted to use the generic post_fw_boot ops.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Chao Song chao.song@intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/soc/sof/loader.c | 10 ++-------- sound/soc/sof/sof-priv.h | 4 ---- 2 files changed, 2 insertions(+), 12 deletions(-)
diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index a1c4a51636c9..81d202e5ce53 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -174,14 +174,8 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev) return ret; }
- if (sdev->ipc->ops->post_fw_boot) { - ret = sdev->ipc->ops->post_fw_boot(sdev); - if (ret) - return ret; - } - - if (sdev->first_boot && sdev->ipc->ops->fw_loader->query_fw_configuration) - return sdev->ipc->ops->fw_loader->query_fw_configuration(sdev); + if (sdev->ipc->ops->post_fw_boot) + return sdev->ipc->ops->post_fw_boot(sdev);
return 0; } diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index c7ab78b042aa..403e81220244 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -421,15 +421,11 @@ struct sof_ipc_pm_ops { * DSP. * The function implements generic, hardware independent way * of loading the initial firmware and its modules (if any). - * @query_fw_configuration: Optional function pointer to query information and - * configuration from the booted firmware. - * Executed after the first successful firmware boot. */ struct sof_ipc_fw_loader_ops { int (*validate)(struct snd_sof_dev *sdev); size_t (*parse_ext_manifest)(struct snd_sof_dev *sdev); int (*load_fw_to_dsp)(struct snd_sof_dev *sdev); - int (*query_fw_configuration)(struct snd_sof_dev *sdev); };
struct sof_ipc_tplg_ops;
In case the requested module is not available among the loaded libraries, try to load it as external library.
The kernel will try to load the file from <fw_lib_prefix>/<module_uuid>.bin
If the file found, then the ext manifest of it is parsed, placed it under XArray and the pointer to the module is returned to the caller.
Releasing the firmware will be done on ipc cleanup time.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Chao Song chao.song@intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/soc/sof/ipc4-loader.c | 155 ++++++++++++++++++++++++++++++++++-- sound/soc/sof/ipc4-priv.h | 2 + sound/soc/sof/ipc4.c | 2 +- 3 files changed, 153 insertions(+), 6 deletions(-)
diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c index dbe3ee4ef08c..af0018b38cf0 100644 --- a/sound/soc/sof/ipc4-loader.c +++ b/sound/soc/sof/ipc4-loader.c @@ -14,6 +14,9 @@ #include "sof-priv.h" #include "ops.h"
+/* 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 size_t sof_ipc4_fw_parse_ext_man(struct snd_sof_dev *sdev, struct sof_ipc4_fw_library *fw_lib) { @@ -71,17 +74,18 @@ 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);
fw_lib->modules = devm_kmalloc_array(sdev->dev, fw_header->num_module_entries, sizeof(*fw_module), GFP_KERNEL); if (!fw_lib->modules) return -ENOMEM;
+ fw_lib->name = fw_header->name; fw_lib->num_modules = fw_header->num_module_entries; fw_module = fw_lib->modules;
@@ -160,13 +164,111 @@ static size_t sof_ipc4_fw_parse_basefw_ext_man(struct snd_sof_dev *sdev) 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; + size_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; + int i, ret;
if (guid_is_null(uuid)) return NULL; @@ -178,6 +280,30 @@ struct sof_ipc4_fw_module *sof_ipc4_find_module_by_uuid(struct snd_sof_dev *sdev } }
+ /* + * 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; }
@@ -270,6 +396,25 @@ int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev) 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_basefw_ext_man, diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h index e4bd6d93fb0f..d6f35004c4b7 100644 --- a/sound/soc/sof/ipc4-priv.h +++ b/sound/soc/sof/ipc4-priv.h @@ -50,6 +50,7 @@ struct sof_ipc4_fw_module { */ struct sof_ipc4_fw_library { struct sof_firmware sof_fw; + const char *name; u32 id; int num_modules; struct sof_ipc4_fw_module *modules; @@ -91,6 +92,7 @@ int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 id, u32 state); int sof_ipc4_mtrace_update_pos(struct snd_sof_dev *sdev, int core);
int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev); +int sof_ipc4_reload_fw_libraries(struct snd_sof_dev *sdev); struct sof_ipc4_fw_module *sof_ipc4_find_module_by_uuid(struct snd_sof_dev *sdev, const guid_t *uuid); #endif diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c index 91c4ea72b4b6..74cd7e956019 100644 --- a/sound/soc/sof/ipc4.c +++ b/sound/soc/sof/ipc4.c @@ -692,7 +692,7 @@ static int sof_ipc4_post_boot(struct snd_sof_dev *sdev) if (sdev->first_boot) return sof_ipc4_query_fw_configuration(sdev);
- return 0; + return sof_ipc4_reload_fw_libraries(sdev); }
const struct sof_ipc_ops ipc4_ops = {
On 10/20/2022 2:12 PM, Peter Ujfalusi wrote:
Hi,
Changes since v1:
- rebased on "[PATCH v2] ASoC: SOF: Intel: pci-tgl: fix ADL-N descriptor"
Cover letter:
In IPC4 all DSP loadable executable is a 'library' containing modules. The main or basefw is also a library which contains multiple modules. IPC4 allows to use loadable libraries to extend the functionality of the booted basefw.
This series adds support for loading external libraries in case they are needed by the loaded topology file.
The libraries must be placed to a specific firmware directory (fw_lib_prefix), which is: intel/avs-lib|sof-ipc4-lib/ followed by the platform name and in case of community key use a 'community' directory.
For example for upx-i11 (community key): intel/avs-lib/tgl/community is the default path.
The name of the library should be the UUID of the module it contains since the library loading is going to look for the file as <module_UUID>.bin In case there is a need to bundle multiple modules into single library, symlinks can be used to point to the file:
module_boundle.bin <UUID1>.bin -> module_boundle.bin <UUID2>.bin -> module_boundle.bin <UUID3>.bin -> module_boundle.bin
But note that in this case all modules will be loaded to the DSP since only the whole library can be loaded, not individual modules.
Regards, Peter
I've synced with Czarek and read your mails again, and while I still have minor doubts to paths that are chosen I think there is nothing blocking it from being merged from our side at this point. Thanks for explaining!
On Thu, 20 Oct 2022 15:12:19 +0300, Peter Ujfalusi wrote:
Changes since v1:
- rebased on "[PATCH v2] ASoC: SOF: Intel: pci-tgl: fix ADL-N descriptor"
Cover letter:
In IPC4 all DSP loadable executable is a 'library' containing modules. The main or basefw is also a library which contains multiple modules. IPC4 allows to use loadable libraries to extend the functionality of the booted basefw.
[...]
Applied to
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
Thanks!
[01/19] ASoC: SOF: loader: Set complete state before post_fw_run op commit: 9b9db0d69bc16072a1d549ed4f343fd55ddfc48c [02/19] ASoC: SOF: Introduce container struct for SOF firmware commit: 4f373ccf226e37a20fdc15a3df8034517a6045fd [03/19] ASoC: SOF: amd: Use the basefw firmware container directly commit: b9bed09aa97f90a40695eb472b1baba81242e3ed [04/19] ASoC: SOF: Intel: hda-loader: Use the basefw firmware container directly commit: 410a321c9721bb3e839f64ffe60b45492d0f1f65 [05/19] ASoC: SOF: Intel: hda-loader-skl: Use the basefw firmware container directly commit: 4fd0f664bdcdd63ce95542227f65459447426aa4 [06/19] ASoC: SOF: Drop the firmware and fw_offset from snd_sof_pdata commit: e3775fda57d49984eaa2cfd86665a152806bfd81 [07/19] ASoC: SOF: ipc: ops: Add support for optional init and exit callbacks commit: aa23b375363f6aba208761ff9985231cc69d00b8 [08/19] ASoC: SOF: ipc4-loader: Save the maximum number of libraries supported commit: b0a12fa905fad870bd941df2726953edafb489f3 [09/19] ASoC: SOF: ipc4: Convert the firmware handling (loader) to library convention commit: 5a932cfce4401491c942ddcb7fd3ca669e507b4d [10/19] ASoC: SOF: IPC4: Add helper for looking up module by UUID commit: c73f8b470855d3123ab4b443aa9c255412dc1a13 [11/19] ASoC: SOF: Add path definition for external firmware libraries commit: 25bbc0c59ee15cfc37acaaa831de447f2c2fbcb9 [12/19] ASoC: SOF: Intel: Set the default firmware library path for IPC4 commit: cd6f2a2e6346ea0955c9bed0c60add8c13b3d5f7 [13/19] ASoC: SOF: ipc4: Define platform dependent library loading callback commit: a5ab431e18d48e618166e8507a3555568d570cd8 [14/19] ASoC: SOF: Intel: hda: Add flag to indicate that the firmware is IMR booted commit: 5d5d915bcde228cd78f5d42062fb65babe651363 [15/19] ASoC: SOF: Intel: Add ipc4 library loading implementation commit: 3ab2c21e65188cac151de1fbe6adf841f2ecb082 [16/19] ASoC: SOF: loader: Add support for IPC dependent post firmware boot ops commit: cbb984b68b8d03aa423a3a0bf2946175b9e25345 [17/19] ASoC: SOF: ipc4: Stop using the query_fw_configuration fw_loader ops commit: e68513106eec04eba9da30d761ba0d22a4cf9e93 [18/19] ASoC: SOF: loader: Remove the query_fw_configuration ops commit: ba42b8bac3fd10b90eefbe42d8d0839d71bf7638 [19/19] ASoC: SOF: ipc4-loader: Support for loading external libraries commit: 73c091a2fe96fac2b893ba166fa7cd11eff45947
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
participants (3)
-
Amadeusz Sławiński
-
Mark Brown
-
Peter Ujfalusi