[PATCH 00/13] ASoC: SOF: IPC path handling and fallback support
Hi,
The main aim of the series is to provide a mechanism to fallback to 'older' IPC versions in case the desired one is missing either a firmware or topology file. It is going to make the life of users and distributions if we are going to start transition existing IPC3 platforms to IPC4 (CAVS2.5) and we might have missed some topology file to convert for example. In that case the kernel will fallback to IPC3 without audio regression.
To be able to support this we needed to change the probe sequence to know the topology filename earlier and check if it is present in the filesystem.
No functional changes for now, the default IPC versions have not been changed.
Regards, Peter --- Peter Ujfalusi (13): ASoC: SOF: Move sof_of_machine_select() to sof-of-dev.c from sof-audio.c ASoC: SOF: Move sof_machine_* functions from sof-audio.c to core.c ASoC: SOF: Add placeholder for platform IPC type and path overrides ASoC: SOF: sof-acpi-dev: Save the default IPC type and path overrides ASoC: SOF: sof-of-dev: Save the default IPC type and path overrides ASoC: SOF: sof-pci-dev: Save the default IPC type and path overrides ASoC: SOF: core: Implement firmware, topology path setup in core ASoC: SOF: sof-acpi-dev: Rely on core to create the file paths ASoC: SOF: sof-of-dev: Rely on core to create the file paths ASoC: SOF: sof-pci-dev: Rely on core to create the file paths ASoC: SOF: core: Add helper for initialization of paths, ops ASoC: SOF: Intel: Do not use resource managed allocation for ipc4_data ASoC: SOF: core: Implement IPC version fallback if firmware files are missing
include/sound/sof.h | 15 ++ sound/soc/sof/Kconfig | 11 ++ sound/soc/sof/Makefile | 3 +- sound/soc/sof/core.c | 289 +++++++++++++++++++++++----- sound/soc/sof/fw-file-profile.c | 322 ++++++++++++++++++++++++++++++++ sound/soc/sof/intel/apl.c | 2 +- sound/soc/sof/intel/cnl.c | 2 +- sound/soc/sof/intel/hda-dai.c | 3 + sound/soc/sof/intel/icl.c | 2 +- sound/soc/sof/intel/lnl.c | 2 +- sound/soc/sof/intel/mtl.c | 2 +- sound/soc/sof/intel/skl.c | 2 +- sound/soc/sof/intel/tgl.c | 2 +- sound/soc/sof/sof-acpi-dev.c | 16 +- sound/soc/sof/sof-audio.c | 120 ------------ sound/soc/sof/sof-of-dev.c | 36 +++- sound/soc/sof/sof-of-dev.h | 9 + sound/soc/sof/sof-pci-dev.c | 102 ++-------- sound/soc/sof/sof-priv.h | 9 +- 19 files changed, 671 insertions(+), 278 deletions(-) create mode 100644 sound/soc/sof/fw-file-profile.c
Move the sof_of_machine_select() function to sof-of-dev.c file and provide an inline stub in case of non OF builds.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/soc/sof/sof-audio.c | 22 ---------------------- sound/soc/sof/sof-of-dev.c | 23 +++++++++++++++++++++++ sound/soc/sof/sof-of-dev.h | 9 +++++++++ 3 files changed, 32 insertions(+), 22 deletions(-)
diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 77cc64ac7113..4972a66c6007 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -1007,28 +1007,6 @@ int sof_dai_get_bclk(struct snd_soc_pcm_runtime *rtd) } EXPORT_SYMBOL(sof_dai_get_bclk);
-static struct snd_sof_of_mach *sof_of_machine_select(struct snd_sof_dev *sdev) -{ - struct snd_sof_pdata *sof_pdata = sdev->pdata; - const struct sof_dev_desc *desc = sof_pdata->desc; - struct snd_sof_of_mach *mach = desc->of_machines; - - if (!mach) - return NULL; - - for (; mach->compatible; mach++) { - if (of_machine_is_compatible(mach->compatible)) { - sof_pdata->tplg_filename = mach->sof_tplg_filename; - if (mach->fw_filename) - sof_pdata->fw_filename = mach->fw_filename; - - return mach; - } - } - - return NULL; -} - /* * SOF Driver enumeration. */ diff --git a/sound/soc/sof/sof-of-dev.c b/sound/soc/sof/sof-of-dev.c index c6be8a91e74b..432b511bf8c4 100644 --- a/sound/soc/sof/sof-of-dev.c +++ b/sound/soc/sof/sof-of-dev.c @@ -41,6 +41,29 @@ static void sof_of_probe_complete(struct device *dev) pm_runtime_enable(dev); }
+struct snd_sof_of_mach *sof_of_machine_select(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *sof_pdata = sdev->pdata; + const struct sof_dev_desc *desc = sof_pdata->desc; + struct snd_sof_of_mach *mach = desc->of_machines; + + if (!mach) + return NULL; + + for (; mach->compatible; mach++) { + if (of_machine_is_compatible(mach->compatible)) { + sof_pdata->tplg_filename = mach->sof_tplg_filename; + if (mach->fw_filename) + sof_pdata->fw_filename = mach->fw_filename; + + return mach; + } + } + + return NULL; +} +EXPORT_SYMBOL(sof_of_machine_select); + int sof_of_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; diff --git a/sound/soc/sof/sof-of-dev.h b/sound/soc/sof/sof-of-dev.h index b6cc70595f3b..547e358a37e3 100644 --- a/sound/soc/sof/sof-of-dev.h +++ b/sound/soc/sof/sof-of-dev.h @@ -16,6 +16,15 @@ struct snd_sof_of_mach { const char *sof_tplg_filename; };
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_OF_DEV) +struct snd_sof_of_mach *sof_of_machine_select(struct snd_sof_dev *sdev); +#else +static inline struct snd_sof_of_mach *sof_of_machine_select(struct snd_sof_dev *sdev) +{ + return NULL; +} +#endif + extern const struct dev_pm_ops sof_of_pm;
int sof_of_probe(struct platform_device *pdev);
Relocate the machine handling functions from sof-audio.c to core.c to maintain code separation.
While doing the move, drop the redundant IS_ERR_OR_NULL(plat_data->pdev_mach) check from sof_machine_unregister()
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/soc/sof/core.c | 95 +++++++++++++++++++++++++++++++++++++ sound/soc/sof/sof-audio.c | 98 --------------------------------------- sound/soc/sof/sof-priv.h | 2 - 3 files changed, 95 insertions(+), 100 deletions(-)
diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index d7b090224f1b..a87522ea4301 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -13,6 +13,7 @@ #include <sound/soc.h> #include <sound/sof.h> #include "sof-priv.h" +#include "sof-of-dev.h" #include "ops.h"
#define CREATE_TRACE_POINTS @@ -143,6 +144,66 @@ void sof_set_fw_state(struct snd_sof_dev *sdev, enum sof_fw_state new_state) } EXPORT_SYMBOL(sof_set_fw_state);
+/* SOF Driver enumeration */ +static int sof_machine_check(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *sof_pdata = sdev->pdata; + const struct sof_dev_desc *desc = sof_pdata->desc; + struct snd_soc_acpi_mach *mach; + + if (!IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE)) { + const struct snd_sof_of_mach *of_mach; + + if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) && + sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC)) + goto nocodec; + + /* find machine */ + mach = snd_sof_machine_select(sdev); + if (mach) { + sof_pdata->machine = mach; + + if (sof_pdata->subsystem_id_set) { + mach->mach_params.subsystem_vendor = sof_pdata->subsystem_vendor; + mach->mach_params.subsystem_device = sof_pdata->subsystem_device; + mach->mach_params.subsystem_id_set = true; + } + + snd_sof_set_mach_params(mach, sdev); + return 0; + } + + of_mach = sof_of_machine_select(sdev); + if (of_mach) { + sof_pdata->of_machine = of_mach; + return 0; + } + + if (!IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC)) { + dev_err(sdev->dev, "error: no matching ASoC machine driver found - aborting probe\n"); + return -ENODEV; + } + } else { + dev_warn(sdev->dev, "Force to use nocodec mode\n"); + } + +nocodec: + /* select nocodec mode */ + dev_warn(sdev->dev, "Using nocodec machine driver\n"); + mach = devm_kzalloc(sdev->dev, sizeof(*mach), GFP_KERNEL); + if (!mach) + return -ENOMEM; + + mach->drv_name = "sof-nocodec"; + if (!sof_pdata->tplg_filename) + sof_pdata->tplg_filename = desc->nocodec_tplg_filename; + + sof_pdata->machine = mach; + snd_sof_set_mach_params(mach, sdev); + + return 0; +} + /* * FW Boot State Transition Diagram * @@ -527,6 +588,40 @@ int snd_sof_device_shutdown(struct device *dev) } EXPORT_SYMBOL(snd_sof_device_shutdown);
+/* Machine driver registering and unregistering */ +int sof_machine_register(struct snd_sof_dev *sdev, void *pdata) +{ + struct snd_sof_pdata *plat_data = pdata; + const char *drv_name; + const void *mach; + int size; + + drv_name = plat_data->machine->drv_name; + mach = plat_data->machine; + size = sizeof(*plat_data->machine); + + /* register machine driver, pass machine info as pdata */ + plat_data->pdev_mach = + platform_device_register_data(sdev->dev, drv_name, + PLATFORM_DEVID_NONE, mach, size); + if (IS_ERR(plat_data->pdev_mach)) + return PTR_ERR(plat_data->pdev_mach); + + dev_dbg(sdev->dev, "created machine %s\n", + dev_name(&plat_data->pdev_mach->dev)); + + return 0; +} +EXPORT_SYMBOL(sof_machine_register); + +void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata) +{ + struct snd_sof_pdata *plat_data = pdata; + + platform_device_unregister(plat_data->pdev_mach); +} +EXPORT_SYMBOL(sof_machine_unregister); + MODULE_AUTHOR("Liam Girdwood"); MODULE_DESCRIPTION("Sound Open Firmware (SOF) Core"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 4972a66c6007..9163975c9c3f 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -11,7 +11,6 @@ #include <linux/bitfield.h> #include <trace/events/sof.h> #include "sof-audio.h" -#include "sof-of-dev.h" #include "ops.h"
static bool is_virtual_widget(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget, @@ -1006,100 +1005,3 @@ int sof_dai_get_bclk(struct snd_soc_pcm_runtime *rtd) return sof_dai_get_clk(rtd, SOF_DAI_CLK_INTEL_SSP_BCLK); } EXPORT_SYMBOL(sof_dai_get_bclk); - -/* - * SOF Driver enumeration. - */ -int sof_machine_check(struct snd_sof_dev *sdev) -{ - struct snd_sof_pdata *sof_pdata = sdev->pdata; - const struct sof_dev_desc *desc = sof_pdata->desc; - struct snd_soc_acpi_mach *mach; - - if (!IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE)) { - const struct snd_sof_of_mach *of_mach; - - if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) && - sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC)) - goto nocodec; - - /* find machine */ - mach = snd_sof_machine_select(sdev); - if (mach) { - sof_pdata->machine = mach; - - if (sof_pdata->subsystem_id_set) { - mach->mach_params.subsystem_vendor = sof_pdata->subsystem_vendor; - mach->mach_params.subsystem_device = sof_pdata->subsystem_device; - mach->mach_params.subsystem_id_set = true; - } - - snd_sof_set_mach_params(mach, sdev); - return 0; - } - - of_mach = sof_of_machine_select(sdev); - if (of_mach) { - sof_pdata->of_machine = of_mach; - return 0; - } - - if (!IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC)) { - dev_err(sdev->dev, "error: no matching ASoC machine driver found - aborting probe\n"); - return -ENODEV; - } - } else { - dev_warn(sdev->dev, "Force to use nocodec mode\n"); - } - -nocodec: - /* select nocodec mode */ - dev_warn(sdev->dev, "Using nocodec machine driver\n"); - mach = devm_kzalloc(sdev->dev, sizeof(*mach), GFP_KERNEL); - if (!mach) - return -ENOMEM; - - mach->drv_name = "sof-nocodec"; - if (!sof_pdata->tplg_filename) - sof_pdata->tplg_filename = desc->nocodec_tplg_filename; - - sof_pdata->machine = mach; - snd_sof_set_mach_params(mach, sdev); - - return 0; -} -EXPORT_SYMBOL(sof_machine_check); - -int sof_machine_register(struct snd_sof_dev *sdev, void *pdata) -{ - struct snd_sof_pdata *plat_data = pdata; - const char *drv_name; - const void *mach; - int size; - - drv_name = plat_data->machine->drv_name; - mach = plat_data->machine; - size = sizeof(*plat_data->machine); - - /* register machine driver, pass machine info as pdata */ - plat_data->pdev_mach = - platform_device_register_data(sdev->dev, drv_name, - PLATFORM_DEVID_NONE, mach, size); - if (IS_ERR(plat_data->pdev_mach)) - return PTR_ERR(plat_data->pdev_mach); - - dev_dbg(sdev->dev, "created machine %s\n", - dev_name(&plat_data->pdev_mach->dev)); - - return 0; -} -EXPORT_SYMBOL(sof_machine_register); - -void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata) -{ - struct snd_sof_pdata *plat_data = pdata; - - if (!IS_ERR_OR_NULL(plat_data->pdev_mach)) - platform_device_unregister(plat_data->pdev_mach); -} -EXPORT_SYMBOL(sof_machine_unregister); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index f4185012eb69..faa8a19ed737 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -814,8 +814,6 @@ int sof_stream_pcm_open(struct snd_sof_dev *sdev, int sof_stream_pcm_close(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream);
-int sof_machine_check(struct snd_sof_dev *sdev); - /* SOF client support */ #if IS_ENABLED(CONFIG_SND_SOC_SOF_CLIENT) int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id,
Add a struct sof_loadable_file_profile which can be filled by platforms (sof-acpi-dev.c, sof-of-dev.c and sof-acpi-dev.c) to be able to use common, generic code to handle path customization.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- include/sound/sof.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
diff --git a/include/sound/sof.h b/include/sound/sof.h index 268d0ca0f69f..05213bb515a3 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -57,6 +57,18 @@ enum sof_ipc_type { SOF_IPC_TYPE_COUNT };
+struct sof_loadable_file_profile { + enum sof_ipc_type ipc_type; + + const char *fw_path; + const char *fw_path_postfix; + const char *fw_name; + const char *fw_lib_path; + const char *fw_lib_path_postfix; + const char *tplg_path; + const char *tplg_name; +}; + /* * SOF Platform data. */ @@ -86,6 +98,9 @@ struct snd_sof_pdata { /* descriptor */ const struct sof_dev_desc *desc;
+ /* platform's preferred IPC type and path overrides */ + struct sof_loadable_file_profile ipc_file_profile_base; + /* firmware and topology filenames */ const char *fw_filename_prefix; const char *fw_filename;
Store the default IPC type and the firmware and topology path overrides to ipc_file_profile_base
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/soc/sof/sof-acpi-dev.c | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/sound/soc/sof/sof-acpi-dev.c b/sound/soc/sof/sof-acpi-dev.c index 84a4a0a3318e..87c0c2edc4c9 100644 --- a/sound/soc/sof/sof-acpi-dev.c +++ b/sound/soc/sof/sof-acpi-dev.c @@ -87,6 +87,10 @@ int sof_acpi_probe(struct platform_device *pdev, const struct sof_dev_desc *desc else sof_pdata->tplg_filename_prefix = desc->default_tplg_path[SOF_IPC_TYPE_3];
+ sof_pdata->ipc_file_profile_base.ipc_type = desc->ipc_default; + sof_pdata->ipc_file_profile_base.fw_path = fw_path; + sof_pdata->ipc_file_profile_base.tplg_path = tplg_path; + /* set callback to be called on successful device probe to enable runtime_pm */ sof_pdata->sof_probe_complete = sof_acpi_probe_complete;
Store the default IPC type and the firmware and topology path overrides to ipc_file_profile_base
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/soc/sof/sof-of-dev.c | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/sound/soc/sof/sof-of-dev.c b/sound/soc/sof/sof-of-dev.c index 432b511bf8c4..7b58f45790f7 100644 --- a/sound/soc/sof/sof-of-dev.c +++ b/sound/soc/sof/sof-of-dev.c @@ -99,6 +99,10 @@ int sof_of_probe(struct platform_device *pdev) else sof_pdata->tplg_filename_prefix = desc->default_tplg_path[SOF_IPC_TYPE_3];
+ sof_pdata->ipc_file_profile_base.ipc_type = desc->ipc_default; + sof_pdata->ipc_file_profile_base.fw_path = fw_path; + sof_pdata->ipc_file_profile_base.tplg_path = tplg_path; + /* set callback to be called on successful device probe to enable runtime_pm */ sof_pdata->sof_probe_complete = sof_of_probe_complete;
Store the default IPC type and the overrides to ipc_file_profile_base
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/soc/sof/sof-pci-dev.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index 64b326e3ef85..becc85b27d51 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -190,6 +190,7 @@ static void sof_pci_probe_complete(struct device *dev)
int sof_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { + struct sof_loadable_file_profile *path_override; struct device *dev = &pci->dev; const struct sof_dev_desc *desc = (const struct sof_dev_desc *)pci_id->driver_data; @@ -334,6 +335,20 @@ int sof_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) sof_pdata->tplg_filename = sof_dmi_override_tplg_name; }
+ path_override = &sof_pdata->ipc_file_profile_base; + path_override->ipc_type = sof_pdata->ipc_type; + path_override->fw_path = fw_path; + path_override->fw_name = fw_filename; + path_override->fw_lib_path = lib_path; + path_override->tplg_path = tplg_path; + path_override->tplg_name = sof_pdata->tplg_filename; + + if (dmi_check_system(community_key_platforms) && + sof_dmi_use_community_key) { + path_override->fw_path_postfix = "community"; + path_override->fw_lib_path_postfix = "community"; + } + /* set callback to be called on successful device probe to enable runtime_pm */ sof_pdata->sof_probe_complete = sof_pci_probe_complete;
Use the information stored in ipc_file_profile_base by platforms to construct the paths, filenames that are going to be used to load the firmware and topology files.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/soc/sof/Makefile | 3 +- sound/soc/sof/core.c | 70 +++++++++++++++-- sound/soc/sof/fw-file-profile.c | 130 ++++++++++++++++++++++++++++++++ sound/soc/sof/sof-priv.h | 7 ++ 4 files changed, 203 insertions(+), 7 deletions(-) create mode 100644 sound/soc/sof/fw-file-profile.c
diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index ef6fd43d0b72..3624124575af 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -1,7 +1,8 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\ - control.o trace.o iomem-utils.o sof-audio.o stream-ipc.o + control.o trace.o iomem-utils.o sof-audio.o stream-ipc.o\ + fw-file-profile.o
# IPC implementations ifneq ($(CONFIG_SND_SOC_SOF_IPC3),) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index a87522ea4301..f1a083de9f9e 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -204,6 +204,67 @@ static int sof_machine_check(struct snd_sof_dev *sdev) return 0; }
+static int sof_select_ipc_and_paths(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *plat_data = sdev->pdata; + struct sof_loadable_file_profile *base_profile = &plat_data->ipc_file_profile_base; + struct sof_loadable_file_profile out_profile; + struct device *dev = sdev->dev; + int ret; + + /* check IPC support */ + if (!(BIT(base_profile->ipc_type) & plat_data->desc->ipc_supported_mask)) { + dev_err(dev, + "ipc_type %d is not supported on this platform, mask is %#x\n", + base_profile->ipc_type, plat_data->desc->ipc_supported_mask); + return -EINVAL; + } + + if (base_profile->ipc_type != plat_data->desc->ipc_default) + dev_info(dev, + "Module parameter used, overriding default IPC %d to %d\n", + plat_data->desc->ipc_default, base_profile->ipc_type); + + if (base_profile->fw_path) + dev_dbg(dev, "Module parameter used, changed fw path to %s\n", + base_profile->fw_path); + else if (base_profile->fw_path_postfix) + dev_dbg(dev, "Path postfix appended to default fw path: %s\n", + base_profile->fw_path_postfix); + + if (base_profile->fw_lib_path) + dev_dbg(dev, "Module parameter used, changed fw_lib path to %s\n", + base_profile->fw_lib_path); + else if (base_profile->fw_lib_path_postfix) + dev_dbg(dev, "Path postfix appended to default fw_lib path: %s\n", + base_profile->fw_lib_path_postfix); + + if (base_profile->fw_name) + dev_dbg(dev, "Module parameter used, changed fw filename to %s\n", + base_profile->fw_name); + + if (base_profile->tplg_path) + dev_dbg(dev, "Module parameter used, changed tplg path to %s\n", + base_profile->tplg_path); + + if (base_profile->tplg_name) + dev_dbg(dev, "Module parameter used, changed tplg name to %s\n", + base_profile->tplg_name); + + ret = sof_create_ipc_file_profile(sdev, base_profile, &out_profile); + if (ret) + return ret; + + plat_data->ipc_type = out_profile.ipc_type; + plat_data->fw_filename = out_profile.fw_name; + plat_data->fw_filename_prefix = out_profile.fw_path; + plat_data->fw_lib_prefix = out_profile.fw_lib_path; + plat_data->tplg_filename_prefix = out_profile.tplg_path; + plat_data->tplg_filename = out_profile.tplg_name; + + return 0; +} + /* * FW Boot State Transition Diagram * @@ -442,12 +503,9 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) } }
- /* check IPC support */ - if (!(BIT(plat_data->ipc_type) & plat_data->desc->ipc_supported_mask)) { - dev_err(dev, "ipc_type %d is not supported on this platform, mask is %#x\n", - plat_data->ipc_type, plat_data->desc->ipc_supported_mask); - return -EINVAL; - } + ret = sof_select_ipc_and_paths(sdev); + if (ret) + return ret;
/* init ops, if necessary */ ret = sof_ops_init(sdev); diff --git a/sound/soc/sof/fw-file-profile.c b/sound/soc/sof/fw-file-profile.c new file mode 100644 index 000000000000..58b55516049e --- /dev/null +++ b/sound/soc/sof/fw-file-profile.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2023 Intel Corporation. All rights reserved. +// + +#include <sound/sof.h> +#include "sof-priv.h" + +static int +sof_file_profile_for_ipc_type(struct device *dev, + const struct sof_dev_desc *desc, + struct sof_loadable_file_profile *base_profile, + struct sof_loadable_file_profile *out_profile) +{ + enum sof_ipc_type ipc_type = base_profile->ipc_type; + bool fw_lib_path_allocated = false; + bool fw_path_allocated = false; + int ret = 0; + + /* firmware path */ + if (base_profile->fw_path) { + out_profile->fw_path = base_profile->fw_path; + } else if (base_profile->fw_path_postfix) { + out_profile->fw_path = devm_kasprintf(dev, GFP_KERNEL, "%s/%s", + desc->default_fw_path[ipc_type], + base_profile->fw_path_postfix); + if (!out_profile->fw_path) + return -ENOMEM; + + fw_path_allocated = true; + } else { + out_profile->fw_path = desc->default_fw_path[ipc_type]; + } + + /* firmware filename */ + if (base_profile->fw_name) + out_profile->fw_name = base_profile->fw_name; + else + out_profile->fw_name = desc->default_fw_filename[ipc_type]; + + + /* firmware library path */ + if (base_profile->fw_lib_path) { + out_profile->fw_lib_path = base_profile->fw_lib_path; + } else if (desc->default_lib_path[ipc_type]) { + if (base_profile->fw_lib_path_postfix) { + out_profile->fw_lib_path = devm_kasprintf(dev, + GFP_KERNEL, "%s/%s", + desc->default_lib_path[ipc_type], + base_profile->fw_lib_path_postfix); + if (!out_profile->fw_lib_path) { + ret = -ENOMEM; + goto out; + } + + fw_lib_path_allocated = true; + } else { + out_profile->fw_lib_path = desc->default_lib_path[ipc_type]; + } + } + + if (base_profile->fw_path_postfix) + out_profile->fw_path_postfix = base_profile->fw_path_postfix; + + if (base_profile->fw_lib_path_postfix) + out_profile->fw_lib_path_postfix = base_profile->fw_lib_path_postfix; + + /* topology path */ + if (base_profile->tplg_path) + out_profile->tplg_path = base_profile->tplg_path; + else + out_profile->tplg_path = desc->default_tplg_path[ipc_type]; + + /* topology name */ + if (base_profile->tplg_name) + out_profile->tplg_name = base_profile->tplg_name; + + out_profile->ipc_type = ipc_type; + +out: + if (ret) { + /* Free up path strings created with devm_kasprintf */ + if (fw_path_allocated) + devm_kfree(dev, out_profile->fw_path); + if (fw_lib_path_allocated) + devm_kfree(dev, out_profile->fw_lib_path); + + memset(out_profile, 0, sizeof(*out_profile)); + } + + return ret; +} + +static void sof_print_profile_info(struct device *dev, + struct sof_loadable_file_profile *profile) +{ + dev_info(dev, "Firmware paths/files for ipc type %d:\n", profile->ipc_type); + + dev_info(dev, " Firmware file: %s/%s\n", profile->fw_path, profile->fw_name); + if (profile->fw_lib_path) + dev_info(dev, " Firmware lib path: %s\n", profile->fw_lib_path); + if (profile->tplg_name) + dev_info(dev, " Topology file: %s/%s\n", profile->tplg_path, + profile->tplg_name); + else + dev_info(dev, " Topology path: %s\n", profile->tplg_path); +} + +int sof_create_ipc_file_profile(struct snd_sof_dev *sdev, + struct sof_loadable_file_profile *base_profile, + struct sof_loadable_file_profile *out_profile) +{ + const struct sof_dev_desc *desc = sdev->pdata->desc; + struct device *dev = sdev->dev; + int ret; + + memset(out_profile, 0, sizeof(*out_profile)); + + ret = sof_file_profile_for_ipc_type(dev, desc, base_profile, out_profile); + if (ret) + return ret; + + sof_print_profile_info(dev, out_profile); + + return 0; +} +EXPORT_SYMBOL(sof_create_ipc_file_profile); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index faa8a19ed737..6d7897bf9607 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -695,6 +695,13 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev); */ extern struct snd_compress_ops sof_compressed_ops;
+/* + * Firmware (firmware, libraries, topologies) file location + */ +int sof_create_ipc_file_profile(struct snd_sof_dev *sdev, + struct sof_loadable_file_profile *base_profile, + struct sof_loadable_file_profile *out_profile); + /* * Firmware loading. */
The core is now using the information from ipc_file_profile_base to create the paths for the loadable files, no need to set it in here anymore.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/soc/sof/sof-acpi-dev.c | 12 ------------ 1 file changed, 12 deletions(-)
diff --git a/sound/soc/sof/sof-acpi-dev.c b/sound/soc/sof/sof-acpi-dev.c index 87c0c2edc4c9..2977f0a63fba 100644 --- a/sound/soc/sof/sof-acpi-dev.c +++ b/sound/soc/sof/sof-acpi-dev.c @@ -74,18 +74,6 @@ int sof_acpi_probe(struct platform_device *pdev, const struct sof_dev_desc *desc
sof_pdata->desc = desc; sof_pdata->dev = &pdev->dev; - sof_pdata->fw_filename = desc->default_fw_filename[SOF_IPC_TYPE_3]; - - /* alternate fw and tplg filenames ? */ - if (fw_path) - sof_pdata->fw_filename_prefix = fw_path; - else - sof_pdata->fw_filename_prefix = desc->default_fw_path[SOF_IPC_TYPE_3]; - - if (tplg_path) - sof_pdata->tplg_filename_prefix = tplg_path; - else - sof_pdata->tplg_filename_prefix = desc->default_tplg_path[SOF_IPC_TYPE_3];
sof_pdata->ipc_file_profile_base.ipc_type = desc->ipc_default; sof_pdata->ipc_file_profile_base.fw_path = fw_path;
The core is now using the information from ipc_file_profile_base to create the paths for the loadable files, no need to set it in here anymore.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/soc/sof/sof-of-dev.c | 11 ----------- 1 file changed, 11 deletions(-)
diff --git a/sound/soc/sof/sof-of-dev.c b/sound/soc/sof/sof-of-dev.c index 7b58f45790f7..fa92da5ee9b3 100644 --- a/sound/soc/sof/sof-of-dev.c +++ b/sound/soc/sof/sof-of-dev.c @@ -87,17 +87,6 @@ int sof_of_probe(struct platform_device *pdev)
sof_pdata->desc = desc; sof_pdata->dev = &pdev->dev; - sof_pdata->fw_filename = desc->default_fw_filename[SOF_IPC_TYPE_3]; - - if (fw_path) - sof_pdata->fw_filename_prefix = fw_path; - else - sof_pdata->fw_filename_prefix = desc->default_fw_path[SOF_IPC_TYPE_3]; - - if (tplg_path) - sof_pdata->tplg_filename_prefix = tplg_path; - else - sof_pdata->tplg_filename_prefix = desc->default_tplg_path[SOF_IPC_TYPE_3];
sof_pdata->ipc_file_profile_base.ipc_type = desc->ipc_default; sof_pdata->ipc_file_profile_base.fw_path = fw_path;
The core is now using the information from ipc_file_profile_base to create the paths for the loadable files, no need to set it in here anymore.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/soc/sof/sof-pci-dev.c | 115 ++++++------------------------------ 1 file changed, 17 insertions(+), 98 deletions(-)
diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index becc85b27d51..aab5c900cecf 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -233,120 +233,39 @@ int sof_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) sof_pdata->desc = desc; sof_pdata->dev = dev;
- sof_pdata->ipc_type = desc->ipc_default; + path_override = &sof_pdata->ipc_file_profile_base;
if (sof_pci_ipc_type < 0) { - sof_pdata->ipc_type = desc->ipc_default; - } else { - dev_info(dev, "overriding default IPC %d to requested %d\n", - desc->ipc_default, sof_pci_ipc_type); - if (sof_pci_ipc_type >= SOF_IPC_TYPE_COUNT) { - dev_err(dev, "invalid request value %d\n", sof_pci_ipc_type); - ret = -EINVAL; - goto out; - } - if (!(BIT(sof_pci_ipc_type) & desc->ipc_supported_mask)) { - dev_err(dev, "invalid request value %d, supported mask is %#x\n", - sof_pci_ipc_type, desc->ipc_supported_mask); - ret = -EINVAL; - goto out; - } - sof_pdata->ipc_type = sof_pci_ipc_type; - } - - if (fw_filename) { - sof_pdata->fw_filename = fw_filename; - - dev_dbg(dev, "Module parameter used, changed fw filename to %s\n", - sof_pdata->fw_filename); + path_override->ipc_type = desc->ipc_default; + } else if (sof_pci_ipc_type < SOF_IPC_TYPE_COUNT) { + path_override->ipc_type = sof_pci_ipc_type; } else { - sof_pdata->fw_filename = desc->default_fw_filename[sof_pdata->ipc_type]; + dev_err(dev, "Invalid IPC type requested: %d\n", sof_pci_ipc_type); + ret = -EINVAL; + goto out; }
- /* - * for platforms using the SOF community key, change the - * default path automatically to pick the right files from the - * linux-firmware tree. This can be overridden with the - * fw_path kernel parameter, e.g. for developers. - */ - - /* alternate fw and tplg filenames ? */ - if (fw_path) { - sof_pdata->fw_filename_prefix = fw_path; - - dev_dbg(dev, - "Module parameter used, changed fw path to %s\n", - sof_pdata->fw_filename_prefix); - - } else if (dmi_check_system(community_key_platforms) && sof_dmi_use_community_key) { - sof_pdata->fw_filename_prefix = - devm_kasprintf(dev, GFP_KERNEL, "%s/%s", - sof_pdata->desc->default_fw_path[sof_pdata->ipc_type], - "community"); - - dev_dbg(dev, - "Platform uses community key, changed fw path to %s\n", - sof_pdata->fw_filename_prefix); - } else { - sof_pdata->fw_filename_prefix = - sof_pdata->desc->default_fw_path[sof_pdata->ipc_type]; - } + path_override->fw_path = fw_path; + path_override->fw_name = fw_filename; + path_override->fw_lib_path = lib_path; + path_override->tplg_path = tplg_path;
- 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 (dmi_check_system(community_key_platforms) && + sof_dmi_use_community_key) { + path_override->fw_path_postfix = "community"; + path_override->fw_lib_path_postfix = "community"; }
- if (tplg_path) - sof_pdata->tplg_filename_prefix = tplg_path; - else - sof_pdata->tplg_filename_prefix = - sof_pdata->desc->default_tplg_path[sof_pdata->ipc_type]; - /* * the topology filename will be provided in the machine descriptor, unless * it is overridden by a module parameter or DMI quirk. */ if (tplg_filename) { - sof_pdata->tplg_filename = tplg_filename; - - dev_dbg(dev, "Module parameter used, changed tplg filename to %s\n", - sof_pdata->tplg_filename); + path_override->tplg_name = tplg_filename; } else { dmi_check_system(sof_tplg_table); if (sof_dmi_override_tplg_name) - sof_pdata->tplg_filename = sof_dmi_override_tplg_name; - } - - path_override = &sof_pdata->ipc_file_profile_base; - path_override->ipc_type = sof_pdata->ipc_type; - path_override->fw_path = fw_path; - path_override->fw_name = fw_filename; - path_override->fw_lib_path = lib_path; - path_override->tplg_path = tplg_path; - path_override->tplg_name = sof_pdata->tplg_filename; - - if (dmi_check_system(community_key_platforms) && - sof_dmi_use_community_key) { - path_override->fw_path_postfix = "community"; - path_override->fw_lib_path_postfix = "community"; + path_override->tplg_name = sof_dmi_override_tplg_name; }
/* set callback to be called on successful device probe to enable runtime_pm */
Add sof_init_environment() as a helper function to contain path and ops initialization.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/soc/sof/core.c | 56 +++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 22 deletions(-)
diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index f1a083de9f9e..a2e9506e0f85 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -265,6 +265,38 @@ static int sof_select_ipc_and_paths(struct snd_sof_dev *sdev) return 0; }
+static int sof_init_environment(struct snd_sof_dev *sdev) +{ + int ret; + + ret = sof_select_ipc_and_paths(sdev); + if (ret) + return ret; + + /* init ops, if necessary */ + ret = sof_ops_init(sdev); + if (ret < 0) + return ret; + + /* check all mandatory ops */ + if (!sof_ops(sdev) || !sof_ops(sdev)->probe) { + dev_err(sdev->dev, "missing mandatory ops\n"); + sof_ops_free(sdev); + return -EINVAL; + } + + if (!sdev->dspless_mode_selected && + (!sof_ops(sdev)->run || !sof_ops(sdev)->block_read || + !sof_ops(sdev)->block_write || !sof_ops(sdev)->send_msg || + !sof_ops(sdev)->load_firmware || !sof_ops(sdev)->ipc_msg_data)) { + dev_err(sdev->dev, "missing mandatory DSP ops\n"); + sof_ops_free(sdev); + return -EINVAL; + } + + return 0; +} + /* * FW Boot State Transition Diagram * @@ -503,31 +535,11 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) } }
- ret = sof_select_ipc_and_paths(sdev); + /* Initialize loadable file paths and check the environment validity */ + ret = sof_init_environment(sdev); if (ret) return ret;
- /* init ops, if necessary */ - ret = sof_ops_init(sdev); - if (ret < 0) - return ret; - - /* check all mandatory ops */ - if (!sof_ops(sdev) || !sof_ops(sdev)->probe) { - sof_ops_free(sdev); - dev_err(dev, "missing mandatory ops\n"); - return -EINVAL; - } - - if (!sdev->dspless_mode_selected && - (!sof_ops(sdev)->run || !sof_ops(sdev)->block_read || - !sof_ops(sdev)->block_write || !sof_ops(sdev)->send_msg || - !sof_ops(sdev)->load_firmware || !sof_ops(sdev)->ipc_msg_data)) { - sof_ops_free(sdev); - dev_err(dev, "missing mandatory DSP ops\n"); - return -EINVAL; - } - INIT_LIST_HEAD(&sdev->pcm_list); INIT_LIST_HEAD(&sdev->kcontrol_list); INIT_LIST_HEAD(&sdev->widget_list);
Manage the ipc4_data allocation in code instead of devm since the ops_init might be called more than once due to IPC type fallback.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/soc/sof/intel/apl.c | 2 +- sound/soc/sof/intel/cnl.c | 2 +- sound/soc/sof/intel/hda-dai.c | 3 +++ sound/soc/sof/intel/icl.c | 2 +- sound/soc/sof/intel/lnl.c | 2 +- sound/soc/sof/intel/mtl.c | 2 +- sound/soc/sof/intel/skl.c | 2 +- sound/soc/sof/intel/tgl.c | 2 +- 8 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index 776b66389c34..dee6c7f73e80 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -55,7 +55,7 @@ int sof_apl_ops_init(struct snd_sof_dev *sdev) if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) { struct sof_ipc4_fw_data *ipc4_data;
- sdev->private = devm_kzalloc(sdev->dev, sizeof(*ipc4_data), GFP_KERNEL); + sdev->private = kzalloc(sizeof(*ipc4_data), GFP_KERNEL); if (!sdev->private) return -ENOMEM;
diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 598cf50abadb..85e1e4760d0e 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -402,7 +402,7 @@ int sof_cnl_ops_init(struct snd_sof_dev *sdev) if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) { struct sof_ipc4_fw_data *ipc4_data;
- sdev->private = devm_kzalloc(sdev->dev, sizeof(*ipc4_data), GFP_KERNEL); + sdev->private = kzalloc(sizeof(*ipc4_data), GFP_KERNEL); if (!sdev->private) return -ENOMEM;
diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index a20deaf3b428..f4cbc0ad5de3 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -621,6 +621,9 @@ void hda_ops_free(struct snd_sof_dev *sdev)
if (!hda_use_tplg_nhlt) intel_nhlt_free(ipc4_data->nhlt); + + kfree(sdev->private); + sdev->private = NULL; } } EXPORT_SYMBOL_NS(hda_ops_free, SND_SOC_SOF_INTEL_HDA_COMMON); diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c index 8e29d6bb6fe8..040698591992 100644 --- a/sound/soc/sof/intel/icl.c +++ b/sound/soc/sof/intel/icl.c @@ -123,7 +123,7 @@ int sof_icl_ops_init(struct snd_sof_dev *sdev) if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) { struct sof_ipc4_fw_data *ipc4_data;
- sdev->private = devm_kzalloc(sdev->dev, sizeof(*ipc4_data), GFP_KERNEL); + sdev->private = kzalloc(sizeof(*ipc4_data), GFP_KERNEL); if (!sdev->private) return -ENOMEM;
diff --git a/sound/soc/sof/intel/lnl.c b/sound/soc/sof/intel/lnl.c index db94b45e53af..03308721ebd4 100644 --- a/sound/soc/sof/intel/lnl.c +++ b/sound/soc/sof/intel/lnl.c @@ -120,7 +120,7 @@ int sof_lnl_ops_init(struct snd_sof_dev *sdev)
sof_lnl_ops.get_stream_position = mtl_dsp_get_stream_hda_link_position;
- sdev->private = devm_kzalloc(sdev->dev, sizeof(struct sof_ipc4_fw_data), GFP_KERNEL); + sdev->private = kzalloc(sizeof(struct sof_ipc4_fw_data), GFP_KERNEL); if (!sdev->private) return -ENOMEM;
diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c index 3ef9e5c37028..f941e2c49d78 100644 --- a/sound/soc/sof/intel/mtl.c +++ b/sound/soc/sof/intel/mtl.c @@ -709,7 +709,7 @@ int sof_mtl_ops_init(struct snd_sof_dev *sdev)
sof_mtl_ops.get_stream_position = mtl_dsp_get_stream_hda_link_position;
- sdev->private = devm_kzalloc(sdev->dev, sizeof(struct sof_ipc4_fw_data), GFP_KERNEL); + sdev->private = kzalloc(sizeof(struct sof_ipc4_fw_data), GFP_KERNEL); if (!sdev->private) return -ENOMEM;
diff --git a/sound/soc/sof/intel/skl.c b/sound/soc/sof/intel/skl.c index d24e64e71b58..93824e6ce573 100644 --- a/sound/soc/sof/intel/skl.c +++ b/sound/soc/sof/intel/skl.c @@ -62,7 +62,7 @@ int sof_skl_ops_init(struct snd_sof_dev *sdev) /* probe/remove/shutdown */ sof_skl_ops.shutdown = hda_dsp_shutdown;
- sdev->private = devm_kzalloc(sdev->dev, sizeof(*ipc4_data), GFP_KERNEL); + sdev->private = kzalloc(sizeof(*ipc4_data), GFP_KERNEL); if (!sdev->private) return -ENOMEM;
diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c index f7de1f5ba06d..d890cac6cb01 100644 --- a/sound/soc/sof/intel/tgl.c +++ b/sound/soc/sof/intel/tgl.c @@ -82,7 +82,7 @@ int sof_tgl_ops_init(struct snd_sof_dev *sdev) if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) { struct sof_ipc4_fw_data *ipc4_data;
- sdev->private = devm_kzalloc(sdev->dev, sizeof(*ipc4_data), GFP_KERNEL); + sdev->private = kzalloc(sizeof(*ipc4_data), GFP_KERNEL); if (!sdev->private) return -ENOMEM;
If a firmware file is missing for the selected IPC type then try to switch to other supported IPC type and check if that one can be used instead.
If for example a platform is changed to IPC4 as default version but the given machine does not yet have the needed topology file created then we will fall back to IPC3 which should have all the needed files.
Relocate the sof_init_environment() to be done at a later phase, in sof_probe_continue().
This will only have changes in behavior if CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE is enabled (Intel HDA platforms) by not failing the module probe, but it is not going to be different case compared to for example failed firmware booting or topology loading error.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/soc/sof/Kconfig | 11 ++ sound/soc/sof/core.c | 104 ++++++++++----- sound/soc/sof/fw-file-profile.c | 226 +++++++++++++++++++++++++++++--- 3 files changed, 292 insertions(+), 49 deletions(-)
diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index a741ed96e789..32ffd345e07f 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -126,6 +126,17 @@ config SND_SOC_SOF_STRICT_ABI_CHECKS If you are not involved in SOF releases and CI development, select "N".
+config SND_SOC_SOF_ALLOW_FALLBACK_TO_NEWER_IPC_VERSION + bool "SOF allow fallback to newer IPC version" + help + This option will allow the kernel to try to 'fallback' to a newer IPC + version if there are missing firmware files to satisfy the default IPC + version. + IPC version fallback to older versions is not affected by this option, + it is always available. + Say Y if you are involved in SOF development and need this option. + If not, select N. + config SND_SOC_SOF_DEBUG bool "SOF debugging features" help diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index a2e9506e0f85..a2afec8f5879 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -212,14 +212,6 @@ static int sof_select_ipc_and_paths(struct snd_sof_dev *sdev) struct device *dev = sdev->dev; int ret;
- /* check IPC support */ - if (!(BIT(base_profile->ipc_type) & plat_data->desc->ipc_supported_mask)) { - dev_err(dev, - "ipc_type %d is not supported on this platform, mask is %#x\n", - base_profile->ipc_type, plat_data->desc->ipc_supported_mask); - return -EINVAL; - } - if (base_profile->ipc_type != plat_data->desc->ipc_default) dev_info(dev, "Module parameter used, overriding default IPC %d to %d\n", @@ -260,19 +252,14 @@ static int sof_select_ipc_and_paths(struct snd_sof_dev *sdev) plat_data->fw_filename_prefix = out_profile.fw_path; plat_data->fw_lib_prefix = out_profile.fw_lib_path; plat_data->tplg_filename_prefix = out_profile.tplg_path; - plat_data->tplg_filename = out_profile.tplg_name;
return 0; }
-static int sof_init_environment(struct snd_sof_dev *sdev) +static int validate_sof_ops(struct snd_sof_dev *sdev) { int ret;
- ret = sof_select_ipc_and_paths(sdev); - if (ret) - return ret; - /* init ops, if necessary */ ret = sof_ops_init(sdev); if (ret < 0) @@ -297,6 +284,71 @@ static int sof_init_environment(struct snd_sof_dev *sdev) return 0; }
+static int sof_init_sof_ops(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *plat_data = sdev->pdata; + struct sof_loadable_file_profile *base_profile = &plat_data->ipc_file_profile_base; + + /* check IPC support */ + if (!(BIT(base_profile->ipc_type) & plat_data->desc->ipc_supported_mask)) { + dev_err(sdev->dev, + "ipc_type %d is not supported on this platform, mask is %#x\n", + base_profile->ipc_type, plat_data->desc->ipc_supported_mask); + return -EINVAL; + } + + /* + * Save the selected IPC type and a topology name override before + * selecting ops since platform code might need this information + */ + plat_data->ipc_type = base_profile->ipc_type; + plat_data->tplg_filename = base_profile->tplg_name; + + return validate_sof_ops(sdev); +} + +static int sof_init_environment(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *plat_data = sdev->pdata; + struct sof_loadable_file_profile *base_profile = &plat_data->ipc_file_profile_base; + int ret; + + /* probe the DSP hardware */ + ret = snd_sof_probe(sdev); + if (ret < 0) { + dev_err(sdev->dev, "failed to probe DSP %d\n", ret); + sof_ops_free(sdev); + return ret; + } + + /* check machine info */ + ret = sof_machine_check(sdev); + if (ret < 0) { + dev_err(sdev->dev, "failed to get machine info %d\n", ret); + goto err_machine_check; + } + + ret = sof_select_ipc_and_paths(sdev); + if (!ret && plat_data->ipc_type != base_profile->ipc_type) { + /* IPC type changed, re-initialize the ops */ + sof_ops_free(sdev); + + ret = validate_sof_ops(sdev); + if (ret < 0) { + snd_sof_remove(sdev); + return ret; + } + } + +err_machine_check: + if (ret) { + snd_sof_remove(sdev); + sof_ops_free(sdev); + } + + return ret; +} + /* * FW Boot State Transition Diagram * @@ -342,23 +394,13 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) struct snd_sof_pdata *plat_data = sdev->pdata; int ret;
- /* probe the DSP hardware */ - ret = snd_sof_probe(sdev); - if (ret < 0) { - dev_err(sdev->dev, "error: failed to probe DSP %d\n", ret); - goto probe_err; - } + /* Initialize loadable file paths and check the environment validity */ + ret = sof_init_environment(sdev); + if (ret) + return ret;
sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE);
- /* check machine info */ - ret = sof_machine_check(sdev); - if (ret < 0) { - dev_err(sdev->dev, "error: failed to get machine info %d\n", - ret); - goto dsp_err; - } - /* set up platform component driver */ snd_sof_new_platform_drv(sdev);
@@ -478,9 +520,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) ipc_err: dbg_err: snd_sof_free_debug(sdev); -dsp_err: snd_sof_remove(sdev); -probe_err: snd_sof_remove_late(sdev); sof_ops_free(sdev);
@@ -535,8 +575,8 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) } }
- /* Initialize loadable file paths and check the environment validity */ - ret = sof_init_environment(sdev); + /* Initialize sof_ops based on the initial selected IPC version */ + ret = sof_init_sof_ops(sdev); if (ret) return ret;
diff --git a/sound/soc/sof/fw-file-profile.c b/sound/soc/sof/fw-file-profile.c index 58b55516049e..138a1ca2c4a8 100644 --- a/sound/soc/sof/fw-file-profile.c +++ b/sound/soc/sof/fw-file-profile.c @@ -6,17 +6,99 @@ // Copyright(c) 2023 Intel Corporation. All rights reserved. //
+#include <linux/firmware.h> #include <sound/sof.h> +#include <sound/sof/ext_manifest4.h> #include "sof-priv.h"
+static int sof_test_firmware_file(struct device *dev, + struct sof_loadable_file_profile *profile, + enum sof_ipc_type *ipc_type_to_adjust) +{ + enum sof_ipc_type fw_ipc_type; + const struct firmware *fw; + const char *fw_filename; + const u32 *magic; + int ret; + + fw_filename = kasprintf(GFP_KERNEL, "%s/%s", profile->fw_path, + profile->fw_name); + if (!fw_filename) + return -ENOMEM; + + ret = firmware_request_nowarn(&fw, fw_filename, dev); + if (ret < 0) { + dev_dbg(dev, "Failed to open firmware file: %s\n", fw_filename); + kfree(fw_filename); + return ret; + } + + /* firmware file exists, check the magic number */ + magic = (const u32 *)fw->data; + switch (*magic) { + case SOF_EXT_MAN_MAGIC_NUMBER: + fw_ipc_type = SOF_IPC_TYPE_3; + break; + case SOF_EXT_MAN4_MAGIC_NUMBER: + fw_ipc_type = SOF_IPC_TYPE_4; + break; + default: + dev_err(dev, "Invalid firmware magic: %#x\n", *magic); + ret = -EINVAL; + goto out; + } + + if (ipc_type_to_adjust) { + *ipc_type_to_adjust = fw_ipc_type; + } else if (fw_ipc_type != profile->ipc_type) { + dev_err(dev, + "ipc type mismatch between %s and expected: %d vs %d\n", + fw_filename, fw_ipc_type, profile->ipc_type); + ret = -EINVAL; + } +out: + release_firmware(fw); + kfree(fw_filename); + + return ret; +} + +static int sof_test_topology_file(struct device *dev, + struct sof_loadable_file_profile *profile) +{ + const struct firmware *fw; + const char *tplg_filename; + int ret; + + if (!profile->tplg_path || !profile->tplg_name) + return 0; + + tplg_filename = kasprintf(GFP_KERNEL, "%s/%s", profile->tplg_path, + profile->tplg_name); + if (!tplg_filename) + return -ENOMEM; + + ret = firmware_request_nowarn(&fw, tplg_filename, dev); + if (!ret) + release_firmware(fw); + else + dev_dbg(dev, "Failed to open topology file: %s\n", tplg_filename); + + kfree(tplg_filename); + + return ret; +} + static int -sof_file_profile_for_ipc_type(struct device *dev, +sof_file_profile_for_ipc_type(struct snd_sof_dev *sdev, + enum sof_ipc_type ipc_type, const struct sof_dev_desc *desc, struct sof_loadable_file_profile *base_profile, struct sof_loadable_file_profile *out_profile) { - enum sof_ipc_type ipc_type = base_profile->ipc_type; + struct snd_sof_pdata *plat_data = sdev->pdata; bool fw_lib_path_allocated = false; + struct device *dev = sdev->dev; bool fw_path_allocated = false; int ret = 0;
@@ -41,6 +123,25 @@ sof_file_profile_for_ipc_type(struct device *dev, else out_profile->fw_name = desc->default_fw_filename[ipc_type];
+ /* + * Check the custom firmware path/filename and adjust the ipc_type to + * match with the existing file for the remaining path configuration. + * + * For default path and firmware name do a verification before + * continuing further. + */ + if (base_profile->fw_path || base_profile->fw_name) { + ret = sof_test_firmware_file(dev, out_profile, &ipc_type); + if (ret) + return ret; + + if (!(desc->ipc_supported_mask & BIT(ipc_type))) { + dev_err(dev, "Unsupported IPC type %d needed by %s/%s\n", + ipc_type, out_profile->fw_path, + out_profile->fw_name); + return -EINVAL; + } + }
/* firmware library path */ if (base_profile->fw_lib_path) { @@ -75,11 +176,17 @@ sof_file_profile_for_ipc_type(struct device *dev, out_profile->tplg_path = desc->default_tplg_path[ipc_type];
/* topology name */ - if (base_profile->tplg_name) - out_profile->tplg_name = base_profile->tplg_name; + out_profile->tplg_name = plat_data->tplg_filename;
out_profile->ipc_type = ipc_type;
+ /* Test only default firmware file */ + if (!base_profile->fw_path && !base_profile->fw_name) + ret = sof_test_firmware_file(dev, out_profile, NULL); + + if (!ret) + ret = sof_test_topology_file(dev, out_profile); + out: if (ret) { /* Free up path strings created with devm_kasprintf */ @@ -94,19 +201,76 @@ sof_file_profile_for_ipc_type(struct device *dev, return ret; }
-static void sof_print_profile_info(struct device *dev, +static void +sof_print_missing_firmware_info(struct snd_sof_dev *sdev, + enum sof_ipc_type ipc_type, + struct sof_loadable_file_profile *base_profile) +{ + struct snd_sof_pdata *plat_data = sdev->pdata; + const struct sof_dev_desc *desc = plat_data->desc; + struct device *dev = sdev->dev; + int ipc_type_count, i; + char *marker; + + dev_err(dev, "SOF firmware and/or topology file not found.\n"); + dev_info(dev, "Supported default profiles\n"); + + if (IS_ENABLED(CONFIG_SND_SOC_SOF_ALLOW_FALLBACK_TO_NEWER_IPC_VERSION)) + ipc_type_count = SOF_IPC_TYPE_COUNT - 1; + else + ipc_type_count = base_profile->ipc_type; + + for (i = 0; i <= ipc_type_count; i++) { + if (!(desc->ipc_supported_mask & BIT(i))) + continue; + + if (i == ipc_type) + marker = "Requested"; + else + marker = "Fallback"; + + dev_info(dev, "- ipc type %d (%s):\n", i, marker); + if (base_profile->fw_path_postfix) + dev_info(dev, " Firmware file: %s/%s/%s\n", + desc->default_fw_path[i], + base_profile->fw_path_postfix, + desc->default_fw_filename[i]); + else + dev_info(dev, " Firmware file: %s/%s\n", + desc->default_fw_path[i], + desc->default_fw_filename[i]); + + dev_info(dev, " Topology file: %s/%s\n", + desc->default_tplg_path[i], + plat_data->tplg_filename); + } + + if (base_profile->fw_path || base_profile->fw_name || + base_profile->tplg_path || base_profile->tplg_name) + dev_info(dev, "Verify the path/name override module parameters.\n"); + + dev_info(dev, "Check if you have 'sof-firmware' package installed.\n"); + dev_info(dev, "Optionally it can be manually downloaded from:\n"); + dev_info(dev, " https://github.com/thesofproject/sof-bin/%5Cn"); +} + +static void sof_print_profile_info(struct snd_sof_dev *sdev, + enum sof_ipc_type ipc_type, struct sof_loadable_file_profile *profile) { + struct device *dev = sdev->dev; + + if (ipc_type != profile->ipc_type) + dev_info(dev, + "Using fallback IPC type %d (requested type was %d)\n", + profile->ipc_type, ipc_type); + dev_info(dev, "Firmware paths/files for ipc type %d:\n", profile->ipc_type);
dev_info(dev, " Firmware file: %s/%s\n", profile->fw_path, profile->fw_name); if (profile->fw_lib_path) dev_info(dev, " Firmware lib path: %s\n", profile->fw_lib_path); - if (profile->tplg_name) - dev_info(dev, " Topology file: %s/%s\n", profile->tplg_path, - profile->tplg_name); - else - dev_info(dev, " Topology path: %s\n", profile->tplg_path); + dev_info(dev, " Topology file: %s/%s\n", profile->tplg_path, profile->tplg_name); }
int sof_create_ipc_file_profile(struct snd_sof_dev *sdev, @@ -114,17 +278,45 @@ int sof_create_ipc_file_profile(struct snd_sof_dev *sdev, struct sof_loadable_file_profile *out_profile) { const struct sof_dev_desc *desc = sdev->pdata->desc; - struct device *dev = sdev->dev; - int ret; + int ipc_fallback_start, ret, i;
memset(out_profile, 0, sizeof(*out_profile));
- ret = sof_file_profile_for_ipc_type(dev, desc, base_profile, out_profile); - if (ret) - return ret; + ret = sof_file_profile_for_ipc_type(sdev, base_profile->ipc_type, desc, + base_profile, out_profile); + if (!ret) + goto out; + + /* + * No firmware file was found for the requested IPC type, as fallback + * if SND_SOC_SOF_ALLOW_FALLBACK_TO_NEWER_IPC_VERSION is selected, check + * all IPC versions in a backwards direction (from newer to older) + * if SND_SOC_SOF_ALLOW_FALLBACK_TO_NEWER_IPC_VERSION is not selected, + * check only older IPC versions than the selected/default version + */ + if (IS_ENABLED(CONFIG_SND_SOC_SOF_ALLOW_FALLBACK_TO_NEWER_IPC_VERSION)) + ipc_fallback_start = SOF_IPC_TYPE_COUNT - 1; + else + ipc_fallback_start = (int)base_profile->ipc_type - 1;
- sof_print_profile_info(dev, out_profile); + for (i = ipc_fallback_start; i >= 0 ; i--) { + if (i == base_profile->ipc_type || + !(desc->ipc_supported_mask & BIT(i))) + continue;
- return 0; + ret = sof_file_profile_for_ipc_type(sdev, i, desc, base_profile, + out_profile); + if (!ret) + break; + } + +out: + if (ret) + sof_print_missing_firmware_info(sdev, base_profile->ipc_type, + base_profile); + else + sof_print_profile_info(sdev, base_profile->ipc_type, out_profile); + + return ret; } EXPORT_SYMBOL(sof_create_ipc_file_profile);
On Wed, 29 Nov 2023 14:53:14 +0200, Peter Ujfalusi wrote:
The main aim of the series is to provide a mechanism to fallback to 'older' IPC versions in case the desired one is missing either a firmware or topology file. It is going to make the life of users and distributions if we are going to start transition existing IPC3 platforms to IPC4 (CAVS2.5) and we might have missed some topology file to convert for example. In that case the kernel will fallback to IPC3 without audio regression.
[...]
Applied to
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
Thanks!
[01/13] ASoC: SOF: Move sof_of_machine_select() to sof-of-dev.c from sof-audio.c commit: 014fdeb0d747304111cfecf93df4407c1a0c80db [02/13] ASoC: SOF: Move sof_machine_* functions from sof-audio.c to core.c commit: 3bc3477915587440035f192c89a1bf9a4360abb3 [03/13] ASoC: SOF: Add placeholder for platform IPC type and path overrides commit: 1162d267eabd6392d0d07bc88b75056e88042fc0 [04/13] ASoC: SOF: sof-acpi-dev: Save the default IPC type and path overrides commit: a07625dcaf994ab3c5a6a456624719df05ed8ad6 [05/13] ASoC: SOF: sof-of-dev: Save the default IPC type and path overrides commit: 396016d56da4ad30fe9ff736e44d9e1457484805 [06/13] ASoC: SOF: sof-pci-dev: Save the default IPC type and path overrides commit: 59ddeae037b81303063bcf62b70fb33841b3f89e [07/13] ASoC: SOF: core: Implement firmware, topology path setup in core commit: b1a4ee9fd5a2dfb0f23abe58377f816915ec14ba [08/13] ASoC: SOF: sof-acpi-dev: Rely on core to create the file paths commit: b2b0bba36f0a81743e7143a8801ca75d2238bdac [09/13] ASoC: SOF: sof-of-dev: Rely on core to create the file paths commit: 8616168928f278723fdc3f4d7cd3d611dcdae8f8 [10/13] ASoC: SOF: sof-pci-dev: Rely on core to create the file paths commit: 8a83f180abb5b95f524fc9b5eb2291f0e39bed30 [11/13] ASoC: SOF: core: Add helper for initialization of paths, ops commit: a5a65437b02df8b842c4620b0b776bcd91ce200a [12/13] ASoC: SOF: Intel: Do not use resource managed allocation for ipc4_data commit: 9b6896538ea71b7c24da1ecb38738a311176f6a8 [13/13] ASoC: SOF: core: Implement IPC version fallback if firmware files are missing commit: 6c393ebbd74ad341bcfb4e2d0091b2655fad45d0
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 (2)
-
Mark Brown
-
Peter Ujfalusi