From 2c91f33c72db65a053e1aedfd662c1c984fb4c02 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 14 Apr 2025 14:32:29 +0800 Subject: [PATCH 01/11] ASoC: SOF: topology: allocate string for tuples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation to handle tuples from multiple topologies, duplicate the tuple string value by allocating memory for it. Signed-off-by: Bard Liao Reviewed-by: Liam Girdwood Reviewed-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Link: https://patch.msgid.link/20250414063239.85200-2-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index dc9cb8324067..15e8ca62ab14 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -571,7 +571,11 @@ static int sof_copy_tuples(struct snd_sof_dev *sdev, struct snd_soc_tplg_vendor_ continue; tuples[*num_copied_tuples].token = tokens[j].token; - tuples[*num_copied_tuples].value.s = elem->string; + tuples[*num_copied_tuples].value.s = + devm_kasprintf(sdev->dev, GFP_KERNEL, + "%s", elem->string); + if (!tuples[*num_copied_tuples].value.s) + return -ENOMEM; } else { struct snd_soc_tplg_vendor_value_elem *elem; From 18877fd3799575e0595d98d799b5509be234e50f Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 14 Apr 2025 14:32:30 +0800 Subject: [PATCH 02/11] ASoC: SOF: topology: don't convert error code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No need to convert the return value of snd_soc_tplg_component_load(). Signed-off-by: Bard Liao Reviewed-by: Liam Girdwood Reviewed-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Link: https://patch.msgid.link/20250414063239.85200-3-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 15e8ca62ab14..fc4a9cddd162 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2486,11 +2486,9 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) else ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw); - if (ret < 0) { + if (ret < 0) dev_err(scomp->dev, "error: tplg component load failed %d\n", ret); - ret = -EINVAL; - } release_firmware(fw); From d1e70eed0b30bd2b15fc6c93b5701be564bbe353 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 14 Apr 2025 14:32:31 +0800 Subject: [PATCH 03/11] ASoC: soc-acpi: add get_function_tplg_files ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We always use a single topology that contains all PCM devices belonging to a machine configuration. However, with SDCA, we want to be able to load function topologies based on the supported device functions. This change is in preparation for loading those function topologies. Signed-off-by: Bard Liao Reviewed-by: Liam Girdwood Reviewed-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Link: https://patch.msgid.link/20250414063239.85200-4-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- include/sound/soc-acpi.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h index 72e371a21767..b8af309c2683 100644 --- a/include/sound/soc-acpi.h +++ b/include/sound/soc-acpi.h @@ -10,6 +10,7 @@ #include #include #include +#include struct snd_soc_acpi_package_context { char *name; /* package name */ @@ -193,6 +194,15 @@ struct snd_soc_acpi_link_adr { * is not constant since this field may be updated at run-time * @sof_tplg_filename: Sound Open Firmware topology file name, if enabled * @tplg_quirk_mask: quirks to select different topology files dynamically + * @get_function_tplg_files: This is an optional callback, if specified then instead of + * the single sof_tplg_filename the callback will return the list of function topology + * files to be loaded. + * Return value: The number of the files or negative ERRNO. 0 means that the single topology + * file should be used, no function topology split can be used on the machine. + * @card: the pointer of the card + * @mach: the pointer of the machine driver + * @prefix: the prefix of the topology file name. Typically, it is the path. + * @tplg_files: the pointer of the array of the topology file names. */ /* Descriptor for SST ASoC machine driver */ struct snd_soc_acpi_mach { @@ -212,6 +222,9 @@ struct snd_soc_acpi_mach { struct snd_soc_acpi_mach_params mach_params; const char *sof_tplg_filename; const u32 tplg_quirk_mask; + int (*get_function_tplg_files)(struct snd_soc_card *card, + const struct snd_soc_acpi_mach *mach, + const char *prefix, const char ***tplg_files); }; #define SND_SOC_ACPI_MAX_CODECS 3 From 6d5997c412cc4beb859b37cc4eaa61e54fc076a3 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 14 Apr 2025 14:32:32 +0800 Subject: [PATCH 04/11] ASoC: SOF: topology: load multiple topologies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, we always use single topology file to describe the widgets. However, with SDCA, we want to be able to load sub-topologies based on the supported device functions. Signed-off-by: Bard Liao Reviewed-by: Liam Girdwood Reviewed-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Link: https://patch.msgid.link/20250414063239.85200-5-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 86 +++++++++++++++++++++++++++++++--------- 1 file changed, 68 insertions(+), 18 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index fc4a9cddd162..e19ba94f2c80 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2310,8 +2310,10 @@ static const struct snd_soc_tplg_ops sof_tplg_ops = { .link_load = sof_link_load, .link_unload = sof_link_unload, - /* completion - called at completion of firmware loading */ - .complete = sof_complete, + /* + * No need to set the complete callback. sof_complete will be called explicitly after + * topology loading is complete. + */ /* manifest - optional to inform component of manifest */ .manifest = sof_manifest, @@ -2467,34 +2469,82 @@ static const struct snd_soc_tplg_ops sof_dspless_tplg_ops = { int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_pdata *sof_pdata = sdev->pdata; + const char *tplg_filename_prefix = sof_pdata->tplg_filename_prefix; const struct firmware *fw; + const char **tplg_files; + int tplg_cnt = 0; int ret; + int i; - dev_dbg(scomp->dev, "loading topology:%s\n", file); + tplg_files = kcalloc(scomp->card->num_links, sizeof(char *), GFP_KERNEL); + if (!tplg_files) + return -ENOMEM; - ret = request_firmware(&fw, file, scomp->dev); - if (ret < 0) { - dev_err(scomp->dev, "error: tplg request firmware %s failed err: %d\n", - file, ret); - dev_err(scomp->dev, - "you may need to download the firmware from https://github.com/thesofproject/sof-bin/\n"); - return ret; + if (sof_pdata->machine->get_function_tplg_files) { + tplg_cnt = sof_pdata->machine->get_function_tplg_files(scomp->card, + sof_pdata->machine, + tplg_filename_prefix, + &tplg_files); + if (tplg_cnt < 0) { + kfree(tplg_files); + return tplg_cnt; + } } - if (sdev->dspless_mode_selected) - ret = snd_soc_tplg_component_load(scomp, &sof_dspless_tplg_ops, fw); - else - ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw); + /* + * The monolithic topology will be used if there is no get_function_tplg_files + * callback or the callback returns 0. + */ + if (!tplg_cnt) { + tplg_files[0] = file; + tplg_cnt = 1; + dev_dbg(scomp->dev, "loading topology: %s\n", file); + } else { + dev_info(scomp->dev, "Using function topologies instead %s\n", file); + } - if (ret < 0) - dev_err(scomp->dev, "error: tplg component load failed %d\n", - ret); + for (i = 0; i < tplg_cnt; i++) { + /* Only print the file names if the function topologies are used */ + if (tplg_files[0] != file) + dev_info(scomp->dev, "loading topology %d: %s\n", i, tplg_files[i]); - release_firmware(fw); + ret = request_firmware(&fw, tplg_files[i], scomp->dev); + if (ret < 0) { + /* + * snd_soc_tplg_component_remove(scomp) will be called + * if snd_soc_tplg_component_load(scomp) failed and all + * objects in the scomp will be removed. No need to call + * snd_soc_tplg_component_remove(scomp) here. + */ + dev_err(scomp->dev, "tplg request firmware %s failed err: %d\n", + tplg_files[i], ret); + goto out; + } + if (sdev->dspless_mode_selected) + ret = snd_soc_tplg_component_load(scomp, &sof_dspless_tplg_ops, fw); + else + ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw); + + release_firmware(fw); + + if (ret < 0) { + dev_err(scomp->dev, "tplg %s component load failed %d\n", + tplg_files[i], ret); + goto out; + } + } + + /* call sof_complete when topologies are loaded successfully */ + ret = sof_complete(scomp); + +out: if (ret >= 0 && sdev->led_present) ret = snd_ctl_led_request(); + kfree(tplg_files); + return ret; } EXPORT_SYMBOL(snd_sof_load_topology); From 2fbeff33381cf017facbf5f13d34693baa5a2296 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 14 Apr 2025 14:32:33 +0800 Subject: [PATCH 05/11] ASoC: Intel: add sof_sdw_get_tplg_files ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add sof_sdw_get_tplg_files ops to get sub-topology file names for the sof_sdw card. Signed-off-by: Bard Liao Reviewed-by: Liam Girdwood Reviewed-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Link: https://patch.msgid.link/20250414063239.85200-6-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/Makefile | 2 +- .../intel/common/sof-function-topology-lib.c | 135 ++++++++++++++++++ .../intel/common/sof-function-topology-lib.h | 15 ++ 3 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 sound/soc/intel/common/sof-function-topology-lib.c create mode 100644 sound/soc/intel/common/sof-function-topology-lib.h diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index 0afd114be9e5..7822bcae6c69 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -12,7 +12,7 @@ snd-soc-acpi-intel-match-y := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-matc soc-acpi-intel-lnl-match.o \ soc-acpi-intel-ptl-match.o \ soc-acpi-intel-hda-match.o \ - soc-acpi-intel-sdw-mockup-match.o + soc-acpi-intel-sdw-mockup-match.o sof-function-topology-lib.o snd-soc-acpi-intel-match-y += soc-acpi-intel-ssp-common.o diff --git a/sound/soc/intel/common/sof-function-topology-lib.c b/sound/soc/intel/common/sof-function-topology-lib.c new file mode 100644 index 000000000000..90fe7aa3df1c --- /dev/null +++ b/sound/soc/intel/common/sof-function-topology-lib.c @@ -0,0 +1,135 @@ +// 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) 2025 Intel Corporation. +// + +#include +#include +#include +#include +#include +#include "sof-function-topology-lib.h" + +enum tplg_device_id { + TPLG_DEVICE_SDCA_JACK, + TPLG_DEVICE_SDCA_AMP, + TPLG_DEVICE_SDCA_MIC, + TPLG_DEVICE_INTEL_PCH_DMIC, + TPLG_DEVICE_HDMI, + TPLG_DEVICE_MAX +}; + +#define SDCA_DEVICE_MASK (BIT(TPLG_DEVICE_SDCA_JACK) | BIT(TPLG_DEVICE_SDCA_AMP) | \ + BIT(TPLG_DEVICE_SDCA_MIC)) + +#define SOF_INTEL_PLATFORM_NAME_MAX 4 + +int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_mach *mach, + const char *prefix, const char ***tplg_files) +{ + struct snd_soc_acpi_mach_params mach_params = mach->mach_params; + struct snd_soc_dai_link *dai_link; + const struct firmware *fw; + char platform[SOF_INTEL_PLATFORM_NAME_MAX]; + unsigned long tplg_mask = 0; + int tplg_num = 0; + int tplg_dev; + int ret; + int i; + + ret = sscanf(mach->sof_tplg_filename, "sof-%3s-*.tplg", platform); + if (ret != 1) { + dev_err(card->dev, "Invalid platform name %s of tplg %s\n", + platform, mach->sof_tplg_filename); + return -EINVAL; + } + + for_each_card_prelinks(card, i, dai_link) { + char *tplg_dev_name; + + dev_dbg(card->dev, "dai_link %s id %d\n", dai_link->name, dai_link->id); + if (strstr(dai_link->name, "SimpleJack")) { + tplg_dev = TPLG_DEVICE_SDCA_JACK; + tplg_dev_name = "sdca-jack"; + } else if (strstr(dai_link->name, "SmartAmp")) { + tplg_dev = TPLG_DEVICE_SDCA_AMP; + tplg_dev_name = devm_kasprintf(card->dev, GFP_KERNEL, + "sdca-%damp", dai_link->num_cpus); + if (!tplg_dev_name) + return -ENOMEM; + } else if (strstr(dai_link->name, "SmartMic")) { + tplg_dev = TPLG_DEVICE_SDCA_MIC; + tplg_dev_name = "sdca-mic"; + } else if (strstr(dai_link->name, "dmic")) { + switch (mach_params.dmic_num) { + case 2: + tplg_dev_name = "dmic-2ch"; + break; + case 4: + tplg_dev_name = "dmic-4ch"; + break; + default: + dev_warn(card->dev, + "only -2ch and -4ch are supported for dmic\n"); + continue; + } + tplg_dev = TPLG_DEVICE_INTEL_PCH_DMIC; + } else if (strstr(dai_link->name, "iDisp")) { + tplg_dev = TPLG_DEVICE_HDMI; + tplg_dev_name = "hdmi-pcm5"; + + } else { + /* The dai link is not supported by separated tplg yet */ + dev_dbg(card->dev, + "dai_link %s is not supported by separated tplg yet\n", + dai_link->name); + return 0; + } + if (tplg_mask & BIT(tplg_dev)) + continue; + + tplg_mask |= BIT(tplg_dev); + + /* + * The tplg file naming rule is sof---id.tplg + * where is only required for the DMIC function as the nhlt blob + * is platform dependent. + */ + switch (tplg_dev) { + case TPLG_DEVICE_INTEL_PCH_DMIC: + (*tplg_files)[tplg_num] = devm_kasprintf(card->dev, GFP_KERNEL, + "%s/sof-%s-%s-id%d.tplg", + prefix, platform, + tplg_dev_name, dai_link->id); + break; + default: + (*tplg_files)[tplg_num] = devm_kasprintf(card->dev, GFP_KERNEL, + "%s/sof-%s-id%d.tplg", + prefix, tplg_dev_name, + dai_link->id); + break; + } + if (!(*tplg_files)[tplg_num]) + return -ENOMEM; + tplg_num++; + } + + dev_dbg(card->dev, "tplg_mask %#lx tplg_num %d\n", tplg_mask, tplg_num); + + /* Check presence of sub-topologies */ + for (i = 0; i < tplg_num; i++) { + ret = firmware_request_nowarn(&fw, (*tplg_files)[i], card->dev); + if (!ret) { + release_firmware(fw); + } else { + dev_dbg(card->dev, "Failed to open topology file: %s\n", (*tplg_files)[i]); + return 0; + } + } + + return tplg_num; +} + diff --git a/sound/soc/intel/common/sof-function-topology-lib.h b/sound/soc/intel/common/sof-function-topology-lib.h new file mode 100644 index 000000000000..e7d0c39d0788 --- /dev/null +++ b/sound/soc/intel/common/sof-function-topology-lib.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * soc-acpi-intel-get-tplg.h - get-tplg-files ops + * + * Copyright (c) 2025, Intel Corporation. + * + */ + +#ifndef _SND_SOC_ACPI_INTEL_GET_TPLG_H +#define _SND_SOC_ACPI_INTEL_GET_TPLG_H + +int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_mach *mach, + const char *prefix, const char ***tplg_files); + +#endif From 143b7a87aa0f949f81e2e80d72f1455667a3d5de Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 14 Apr 2025 14:32:34 +0800 Subject: [PATCH 06/11] ASoC: Intel: soc-acpi-intel-lnl-match: set get_function_tplg_files ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The audio configs with multi-function SDCA codecs can use the sof_sdw_get_tplg_files ops to get function topologies dynamically. Signed-off-by: Bard Liao Reviewed-by: Liam Girdwood Reviewed-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Link: https://patch.msgid.link/20250414063239.85200-7-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/soc-acpi-intel-lnl-match.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c index e04f6de746eb..a2bee667facb 100644 --- a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c @@ -8,6 +8,7 @@ #include #include +#include "sof-function-topology-lib.h" #include "soc-acpi-intel-sdca-quirks.h" #include "soc-acpi-intel-sdw-mockup-match.h" @@ -712,6 +713,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { .links = lnl_cs42l43_l0, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-lnl-cs42l43-l0.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0), @@ -730,6 +732,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { .links = lnl_rt722_only, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-lnl-rt722-l0.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = GENMASK(2, 0), @@ -748,14 +751,16 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { .links = lnl_sdw_rt712_vb_l2_rt1320_l1, .drv_name = "sof_sdw", .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, - .sof_tplg_filename = "sof-lnl-rt712-l2-rt1320-l1.tplg" + .sof_tplg_filename = "sof-lnl-rt712-l2-rt1320-l1.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(1) | BIT(2) | BIT(3), .links = lnl_sdw_rt713_vb_l2_rt1320_l13, .drv_name = "sof_sdw", .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, - .sof_tplg_filename = "sof-lnl-rt713-l2-rt1320-l13.tplg" + .sof_tplg_filename = "sof-lnl-rt713-l2-rt1320-l13.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, {}, }; From d348b4181cd15ed432c2ae7eb33ef1bb7dfd7527 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 14 Apr 2025 14:32:35 +0800 Subject: [PATCH 07/11] ASoC: Intel: soc-acpi-intel-arl-match: set get_function_tplg_files ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The audio configs with multi-function SDCA codecs can use the sof_sdw_get_tplg_files ops to get function topologies dynamically. Signed-off-by: Bard Liao Reviewed-by: Liam Girdwood Reviewed-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Link: https://patch.msgid.link/20250414063239.85200-8-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/soc-acpi-intel-arl-match.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-arl-match.c b/sound/soc/intel/common/soc-acpi-intel-arl-match.c index 32147dc9d2d6..73e581e93755 100644 --- a/sound/soc/intel/common/soc-acpi-intel-arl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-arl-match.c @@ -8,6 +8,7 @@ #include #include #include +#include "sof-function-topology-lib.h" static const struct snd_soc_acpi_endpoint single_endpoint = { .num = 0, @@ -436,42 +437,49 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = { .links = arl_cs42l43_l0_cs35l56_l23, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-arl-cs42l43-l0-cs35l56-l23.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0) | BIT(2) | BIT(3), .links = arl_cs42l43_l0_cs35l56_2_l23, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-arl-cs42l43-l0-cs35l56-l23.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0) | BIT(2) | BIT(3), .links = arl_cs42l43_l0_cs35l56_3_l23, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-arl-cs42l43-l0-cs35l56-l23.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0) | BIT(2), .links = arl_cs42l43_l0_cs35l56_l2, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-arl-cs42l43-l0-cs35l56-l2.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0), .links = arl_cs42l43_l0, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-arl-cs42l43-l0.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(2), .links = arl_cs42l43_l2, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-arl-cs42l43-l2.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(2) | BIT(3), .links = arl_cs42l43_l2_cs35l56_l3, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-arl-cs42l43-l2-cs35l56-l3.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = 0x1, /* link0 required */ @@ -490,6 +498,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = { .links = arl_rt722_l0_rt1320_l2, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-arl-rt722-l0_rt1320-l2.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, {}, }; From 5a0c4a0925f4525743ee08aa3930fdc256d920f0 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 14 Apr 2025 14:32:36 +0800 Subject: [PATCH 08/11] ASoC: Intel: soc-acpi-intel-ptl-match: add get_function_tplg_files ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The audio configs with multi-function SDCA codecs can use the sof_sdw_get_tplg_files ops to get function topologies dynamically. Signed-off-by: Bard Liao Reviewed-by: Liam Girdwood Reviewed-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Link: https://patch.msgid.link/20250414063239.85200-9-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- .../soc/intel/common/soc-acpi-intel-ptl-match.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c index 6603d8de501c..992729582562 100644 --- a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c @@ -8,6 +8,7 @@ #include #include +#include "sof-function-topology-lib.h" #include "soc-acpi-intel-sdca-quirks.h" #include "soc-acpi-intel-sdw-mockup-match.h" #include @@ -586,52 +587,60 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = { .links = ptl_rt721_l3, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-ptl-rt721.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0), .links = ptl_rt722_only, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-ptl-rt722.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(1), .links = ptl_rt722_l1, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-ptl-rt722.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(3), .links = ptl_rt722_l3, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-ptl-rt722.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(1) | BIT(2), .links = ptl_sdw_rt712_vb_l2_rt1320_l1, .drv_name = "sof_sdw", .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, - .sof_tplg_filename = "sof-ptl-rt712-l2-rt1320-l1.tplg" + .sof_tplg_filename = "sof-ptl-rt712-l2-rt1320-l1.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(2) | BIT(3), .links = ptl_sdw_rt712_vb_l3_rt1320_l2, .drv_name = "sof_sdw", .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, - .sof_tplg_filename = "sof-ptl-rt712-l3-rt1320-l2.tplg" + .sof_tplg_filename = "sof-ptl-rt712-l3-rt1320-l2.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(1) | BIT(2) | BIT(3), .links = ptl_sdw_rt713_vb_l2_rt1320_l13, .drv_name = "sof_sdw", .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, - .sof_tplg_filename = "sof-ptl-rt713-l2-rt1320-l13.tplg" + .sof_tplg_filename = "sof-ptl-rt713-l2-rt1320-l13.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(1) | BIT(2) | BIT(3), .links = ptl_sdw_rt713_vb_l3_rt1320_l12, .drv_name = "sof_sdw", .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, - .sof_tplg_filename = "sof-ptl-rt713-l3-rt1320-l12.tplg" + .sof_tplg_filename = "sof-ptl-rt713-l3-rt1320-l12.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, {}, }; From cf8a4ca84cbf52700beaa40850647040af06b749 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 14 Apr 2025 14:32:37 +0800 Subject: [PATCH 09/11] ASoC: Intel: soc-acpi-intel-mtl-match: add get_function_tplg_files ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The audio configs with multi-function SDCA codecs can use the sof_sdw_get_tplg_files ops to get function topologies dynamically. Signed-off-by: Bard Liao Reviewed-by: Liam Girdwood Reviewed-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Link: https://patch.msgid.link/20250414063239.85200-10-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/soc-acpi-intel-mtl-match.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c index 9e611e3667ad..af131d26bd33 100644 --- a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c @@ -11,6 +11,7 @@ #include #include #include +#include "sof-function-topology-lib.h" #include "soc-acpi-intel-sdca-quirks.h" #include "soc-acpi-intel-sdw-mockup-match.h" @@ -1083,12 +1084,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { .drv_name = "sof_sdw", .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, .sof_tplg_filename = "sof-mtl-rt712-vb-l0.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0), .links = mtl_712_l0, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-rt712-l0.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = GENMASK(2, 0), @@ -1101,30 +1104,35 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { .links = cs42l43_link0_cs35l56_link2_link3, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-cs42l43-l0-cs35l56-l23.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0) | BIT(1) | BIT(3), .links = cs42l43_link3_cs35l56_x4_link0_link1_spkagg, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-cs42l43-l3-cs35l56-l01-spkagg.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = GENMASK(2, 0), .links = mtl_cs42l43_cs35l56, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-cs42l43-l0-cs35l56-l12.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0) | BIT(1), .links = mtl_cs35l56_x8_link0_link1_fb, .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-cs35l56-l01-fb8.tplg" + .sof_tplg_filename = "sof-mtl-cs35l56-l01-fb8.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0), .links = mtl_cs42l43_l0, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-cs42l43-l0.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = GENMASK(3, 0), @@ -1143,6 +1151,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { .links = mtl_rt722_only, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-rt722-l0.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0), From 6d893cfb3d399a9eae95a8c23041dc42dc6dc18f Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 14 Apr 2025 14:32:38 +0800 Subject: [PATCH 10/11] ASoC: sdw_utils: split asoc_sdw_get_codec_name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently asoc_sdw_get_codec_name will return codec_info->codec_name if it is set. However, in some case we need the sdw codec name no matter if codec_info->codec_name is set or not. _asoc_sdw_get_codec_name() will be used in the follow up commit. Signed-off-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Ranjani Sridharan Link: https://patch.msgid.link/20250414063239.85200-11-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sdw_utils/soc_sdw_utils.c | 31 ++++++++++++++++++----------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index 5175818ff2c1..4c5ce01eae5b 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -934,10 +934,10 @@ static bool asoc_sdw_is_unique_device(const struct snd_soc_acpi_link_adr *adr_li return true; } -const char *asoc_sdw_get_codec_name(struct device *dev, - const struct asoc_sdw_codec_info *codec_info, - const struct snd_soc_acpi_link_adr *adr_link, - int adr_index) +static const char *_asoc_sdw_get_codec_name(struct device *dev, + const struct asoc_sdw_codec_info *codec_info, + const struct snd_soc_acpi_link_adr *adr_link, + int adr_index) { u64 adr = adr_link->adr_d[adr_index].adr; unsigned int sdw_version = SDW_VERSION(adr); @@ -947,17 +947,24 @@ const char *asoc_sdw_get_codec_name(struct device *dev, unsigned int part_id = SDW_PART_ID(adr); unsigned int class_id = SDW_CLASS_ID(adr); - if (codec_info->codec_name) - return devm_kstrdup(dev, codec_info->codec_name, GFP_KERNEL); - else if (asoc_sdw_is_unique_device(adr_link, sdw_version, mfg_id, part_id, - class_id, adr_index)) + if (asoc_sdw_is_unique_device(adr_link, sdw_version, mfg_id, part_id, + class_id, adr_index)) return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x", link_id, mfg_id, part_id, class_id); - else - return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x:%01x", - link_id, mfg_id, part_id, class_id, unique_id); - return NULL; + return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x:%01x", + link_id, mfg_id, part_id, class_id, unique_id); +} + +const char *asoc_sdw_get_codec_name(struct device *dev, + const struct asoc_sdw_codec_info *codec_info, + const struct snd_soc_acpi_link_adr *adr_link, + int adr_index) +{ + if (codec_info->codec_name) + return devm_kstrdup(dev, codec_info->codec_name, GFP_KERNEL); + + return _asoc_sdw_get_codec_name(dev, codec_info, adr_link, adr_index); } EXPORT_SYMBOL_NS(asoc_sdw_get_codec_name, "SND_SOC_SDW_UTILS"); From 4f8ef33dd44a3d1136d3934609b8a43e62aaaa0d Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 14 Apr 2025 14:32:39 +0800 Subject: [PATCH 11/11] ASoC: soc_sdw_utils: skip the endpoint that doesn't present MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A codec endpoint may not be used. We could check the present SDCA functions to know if the endpoint is used or not. Skip the endpoint which is not used. Signed-off-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Ranjani Sridharan Link: https://patch.msgid.link/20250414063239.85200-12-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sdw_utils/soc_sdw_utils.c | 137 +++++++++++++++++++++++++++- 1 file changed, 134 insertions(+), 3 deletions(-) diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index 4c5ce01eae5b..60b731673f3b 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -10,6 +10,7 @@ #include #include #include +#include #include static const struct snd_soc_dapm_widget generic_dmic_widgets[] = { @@ -1131,6 +1132,106 @@ struct asoc_sdw_dailink *asoc_sdw_find_dailink(struct asoc_sdw_dailink *dailinks } EXPORT_SYMBOL_NS(asoc_sdw_find_dailink, "SND_SOC_SDW_UTILS"); +static int asoc_sdw_get_dai_type(u32 type) +{ + switch (type) { + case SDCA_FUNCTION_TYPE_SMART_AMP: + case SDCA_FUNCTION_TYPE_SIMPLE_AMP: + return SOC_SDW_DAI_TYPE_AMP; + case SDCA_FUNCTION_TYPE_SMART_MIC: + case SDCA_FUNCTION_TYPE_SIMPLE_MIC: + case SDCA_FUNCTION_TYPE_SPEAKER_MIC: + return SOC_SDW_DAI_TYPE_MIC; + case SDCA_FUNCTION_TYPE_UAJ: + case SDCA_FUNCTION_TYPE_RJ: + case SDCA_FUNCTION_TYPE_SIMPLE_JACK: + return SOC_SDW_DAI_TYPE_JACK; + default: + return -EINVAL; + } +} + +/* + * Check if the SDCA endpoint is present by the SDW peripheral + * + * @dev: Device pointer + * @codec_info: Codec info pointer + * @adr_link: ACPI link address + * @adr_index: Index of the ACPI link address + * @end_index: Index of the endpoint + * + * Return: 1 if the endpoint is present, + * 0 if the endpoint is not present, + * negative error code. + */ + +static int is_sdca_endpoint_present(struct device *dev, + struct asoc_sdw_codec_info *codec_info, + const struct snd_soc_acpi_link_adr *adr_link, + int adr_index, int end_index) +{ + const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[adr_index]; + const struct snd_soc_acpi_endpoint *adr_end; + const struct asoc_sdw_dai_info *dai_info; + struct snd_soc_dai_link_component *dlc; + struct snd_soc_dai *codec_dai; + struct sdw_slave *slave; + struct device *sdw_dev; + const char *sdw_codec_name; + int i; + + dlc = kzalloc(sizeof(*dlc), GFP_KERNEL); + + adr_end = &adr_dev->endpoints[end_index]; + dai_info = &codec_info->dais[adr_end->num]; + + dlc->dai_name = dai_info->dai_name; + codec_dai = snd_soc_find_dai_with_mutex(dlc); + if (!codec_dai) { + dev_warn(dev, "codec dai %s not registered yet\n", dlc->dai_name); + kfree(dlc); + return -EPROBE_DEFER; + } + kfree(dlc); + + sdw_codec_name = _asoc_sdw_get_codec_name(dev, codec_info, + adr_link, adr_index); + if (!sdw_codec_name) + return -ENOMEM; + + sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name); + if (!sdw_dev) { + dev_err(dev, "codec %s not found\n", sdw_codec_name); + return -EINVAL; + } + + slave = dev_to_sdw_dev(sdw_dev); + if (!slave) + return -EINVAL; + + /* Make sure BIOS provides SDCA properties */ + if (!slave->sdca_data.interface_revision) { + dev_warn(&slave->dev, "SDCA properties not found in the BIOS\n"); + return 1; + } + + for (i = 0; i < slave->sdca_data.num_functions; i++) { + int dai_type = asoc_sdw_get_dai_type(slave->sdca_data.function[i].type); + + if (dai_type == dai_info->dai_type) { + dev_dbg(&slave->dev, "DAI type %d sdca function %s found\n", + dai_type, slave->sdca_data.function[i].name); + return 1; + } + } + + dev_dbg(&slave->dev, + "SDCA device function for DAI type %d not supported, skip endpoint\n", + dai_info->dai_type); + + return 0; +} + int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, struct asoc_sdw_dailink *soc_dais, struct asoc_sdw_endpoint *soc_ends, @@ -1159,6 +1260,7 @@ int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[i]; struct asoc_sdw_codec_info *codec_info; const char *codec_name; + bool check_sdca = false; if (!adr_dev->name_prefix) { dev_err(dev, "codec 0x%llx does not have a name prefix\n", @@ -1189,6 +1291,9 @@ int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, soc_end->include_sidecar = true; } + if (SDW_CLASS_ID(adr_dev->adr) && adr_dev->num_endpoints > 1) + check_sdca = true; + for (j = 0; j < adr_dev->num_endpoints; j++) { const struct snd_soc_acpi_endpoint *adr_end; const struct asoc_sdw_dai_info *dai_info; @@ -1199,9 +1304,35 @@ int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, dai_info = &codec_info->dais[adr_end->num]; soc_dai = asoc_sdw_find_dailink(soc_dais, adr_end); - if (dai_info->quirk && - !(dai_info->quirk_exclude ^ !!(dai_info->quirk & ctx->mc_quirk))) - continue; + /* + * quirk should have higher priority than the sdca properties + * in the BIOS. We can't always check the DAI quirk because we + * will set the mc_quirk when the BIOS doesn't provide the right + * information. The endpoint will be skipped if the dai_info-> + * quirk_exclude and mc_quirk are both not set if we always skip + * the endpoint according to the quirk information. We need to + * keep the endpoint if it is present in the BIOS. So, only + * check the DAI quirk when the mc_quirk is set or SDCA endpoint + * present check is not needed. + */ + if (dai_info->quirk & ctx->mc_quirk || !check_sdca) { + /* + * Check the endpoint if a matching quirk is set or SDCA + * endpoint check is not necessary + */ + if (dai_info->quirk && + !(dai_info->quirk_exclude ^ !!(dai_info->quirk & ctx->mc_quirk))) + continue; + } else { + /* Check SDCA codec endpoint if there is no matching quirk */ + ret = is_sdca_endpoint_present(dev, codec_info, adr_link, i, j); + if (ret < 0) + return ret; + + /* The endpoint is not present, skip */ + if (!ret) + continue; + } dev_dbg(dev, "Add dev: %d, 0x%llx end: %d, dai: %d, %c/%c to %s: %d\n",