From 014fdeb0d747304111cfecf93df4407c1a0c80db Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 29 Nov 2023 14:53:15 +0200 Subject: [PATCH 01/13] ASoC: SOF: Move sof_of_machine_select() to sof-of-dev.c from sof-audio.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 Reviewed-by: Kai Vehmanen Link: https://lore.kernel.org/r/20231129125327.23708-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- 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 563fe6f7789f..2198bb8e92c3 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -990,28 +990,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); From 3bc3477915587440035f192c89a1bf9a4360abb3 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 29 Nov 2023 14:53:16 +0200 Subject: [PATCH 02/13] ASoC: SOF: Move sof_machine_* functions from sof-audio.c to core.c 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 Reviewed-by: Kai Vehmanen Link: https://lore.kernel.org/r/20231129125327.23708-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- 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 #include #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 2198bb8e92c3..de40a5e227f4 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -11,7 +11,6 @@ #include #include #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, @@ -989,100 +988,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, From 1162d267eabd6392d0d07bc88b75056e88042fc0 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 29 Nov 2023 14:53:17 +0200 Subject: [PATCH 03/13] ASoC: SOF: Add placeholder for platform IPC type and path overrides 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 Reviewed-by: Kai Vehmanen Link: https://lore.kernel.org/r/20231129125327.23708-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- 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; From a07625dcaf994ab3c5a6a456624719df05ed8ad6 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 29 Nov 2023 14:53:18 +0200 Subject: [PATCH 04/13] ASoC: SOF: sof-acpi-dev: Save the default IPC type and path overrides Store the default IPC type and the firmware and topology path overrides to ipc_file_profile_base Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Link: https://lore.kernel.org/r/20231129125327.23708-5-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- 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; From 396016d56da4ad30fe9ff736e44d9e1457484805 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 29 Nov 2023 14:53:19 +0200 Subject: [PATCH 05/13] ASoC: SOF: sof-of-dev: Save the default IPC type and path overrides Store the default IPC type and the firmware and topology path overrides to ipc_file_profile_base Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Link: https://lore.kernel.org/r/20231129125327.23708-6-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- 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; From 59ddeae037b81303063bcf62b70fb33841b3f89e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 29 Nov 2023 14:53:20 +0200 Subject: [PATCH 06/13] ASoC: SOF: sof-pci-dev: Save the default IPC type and path overrides Store the default IPC type and the overrides to ipc_file_profile_base Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Link: https://lore.kernel.org/r/20231129125327.23708-7-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- 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; From b1a4ee9fd5a2dfb0f23abe58377f816915ec14ba Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 29 Nov 2023 14:53:21 +0200 Subject: [PATCH 07/13] ASoC: SOF: core: Implement firmware, topology path setup in core 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 Reviewed-by: Kai Vehmanen Link: https://lore.kernel.org/r/20231129125327.23708-8-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- 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 +#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. */ From b2b0bba36f0a81743e7143a8801ca75d2238bdac Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 29 Nov 2023 14:53:22 +0200 Subject: [PATCH 08/13] ASoC: SOF: sof-acpi-dev: Rely on core to create the file paths 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 Reviewed-by: Kai Vehmanen Link: https://lore.kernel.org/r/20231129125327.23708-9-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- 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; From 8616168928f278723fdc3f4d7cd3d611dcdae8f8 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 29 Nov 2023 14:53:23 +0200 Subject: [PATCH 09/13] ASoC: SOF: sof-of-dev: Rely on core to create the file paths 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 Reviewed-by: Kai Vehmanen Link: https://lore.kernel.org/r/20231129125327.23708-10-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- 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; From 8a83f180abb5b95f524fc9b5eb2291f0e39bed30 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 29 Nov 2023 14:53:24 +0200 Subject: [PATCH 10/13] ASoC: SOF: sof-pci-dev: Rely on core to create the file paths 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 Reviewed-by: Kai Vehmanen Link: https://lore.kernel.org/r/20231129125327.23708-11-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- 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; + 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 { - 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; + dev_err(dev, "Invalid IPC type requested: %d\n", sof_pci_ipc_type); + ret = -EINVAL; + goto out; } - if (fw_filename) { - sof_pdata->fw_filename = fw_filename; + 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; - dev_dbg(dev, "Module parameter used, changed fw filename to %s\n", - sof_pdata->fw_filename); - } else { - sof_pdata->fw_filename = desc->default_fw_filename[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"; } - /* - * 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]; - } - - 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 - 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 */ From a5a65437b02df8b842c4620b0b776bcd91ce200a Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 29 Nov 2023 14:53:25 +0200 Subject: [PATCH 11/13] ASoC: SOF: core: Add helper for initialization of paths, ops Add sof_init_environment() as a helper function to contain path and ops initialization. Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Link: https://lore.kernel.org/r/20231129125327.23708-12-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- 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); From 9b6896538ea71b7c24da1ecb38738a311176f6a8 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 29 Nov 2023 14:53:26 +0200 Subject: [PATCH 12/13] ASoC: SOF: Intel: Do not use resource managed allocation for ipc4_data 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 Reviewed-by: Kai Vehmanen Link: https://lore.kernel.org/r/20231129125327.23708-13-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- 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; From 6c393ebbd74ad341bcfb4e2d0091b2655fad45d0 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 29 Nov 2023 14:53:27 +0200 Subject: [PATCH 13/13] ASoC: SOF: core: Implement IPC version fallback if firmware files are missing 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 Reviewed-by: Kai Vehmanen Link: https://lore.kernel.org/r/20231129125327.23708-14-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- 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 #include +#include #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/\n"); +} + +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); + 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; + + for (i = ipc_fallback_start; i >= 0 ; i--) { + if (i == base_profile->ipc_type || + !(desc->ipc_supported_mask & BIT(i))) + continue; + + ret = sof_file_profile_for_ipc_type(sdev, i, desc, base_profile, + out_profile); + if (!ret) + break; + } + +out: if (ret) - return 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); - sof_print_profile_info(dev, out_profile); - - return 0; + return ret; } EXPORT_SYMBOL(sof_create_ipc_file_profile);