From dfb10525f24111daf364173fca80145d00dcbac7 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 22 Dec 2025 17:47:04 +0800 Subject: [PATCH 1/6] ASoC: SOF: get nhlt from all topologies The existing code assumes there is only one NHLT blob either from BIOS or topology. As function topologies are used and each function topology contains a NHLT blob section, we need to search the matching NHLT blob from all the NHLT blobs. The commit suggests adding a list of NHLTs and search the NHLTs from the list. Signed-off-by: Bard Liao --- sound/soc/sof/intel/apl.c | 1 + sound/soc/sof/intel/cnl.c | 1 + sound/soc/sof/intel/hda-dai.c | 26 ++++++++++++-- sound/soc/sof/intel/icl.c | 1 + sound/soc/sof/intel/mtl.c | 1 + sound/soc/sof/intel/skl.c | 1 + sound/soc/sof/intel/tgl.c | 1 + sound/soc/sof/ipc4-priv.h | 10 ++++-- sound/soc/sof/ipc4-topology.c | 64 ++++++++++++++++++++++++----------- 9 files changed, 81 insertions(+), 25 deletions(-) diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index b0072601181efc..753da09301fa17 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -59,6 +59,7 @@ int sof_apl_ops_init(struct snd_sof_dev *sdev) return -ENOMEM; ipc4_data = sdev->private; + INIT_LIST_HEAD(&ipc4_data->nhlt_list); ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET; ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_1_5; diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 660c1475e5a43f..ec33cd18e669e2 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -406,6 +406,7 @@ int sof_cnl_ops_init(struct snd_sof_dev *sdev) return -ENOMEM; ipc4_data = sdev->private; + INIT_LIST_HEAD(&ipc4_data->nhlt_list); ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET; ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_1_8; diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 15faedeec16d78..7a88a2c84024e8 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -760,8 +760,20 @@ void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4 && !hda_use_tplg_nhlt) { struct sof_ipc4_fw_data *ipc4_data = sdev->private; + struct snd_ipc4_nhlt *entry; - ipc4_data->nhlt = intel_nhlt_init(sdev->dev); + entry = devm_kzalloc(sdev->dev, sizeof(*entry), GFP_KERNEL); + if (!entry) + return; + + entry->nhlt = intel_nhlt_init(sdev->dev); + if (!entry->nhlt) { + devm_kfree(sdev->dev, entry); + return; + } + + entry->from_acpi = true; + list_add(&entry->list, &ipc4_data->nhlt_list); } } EXPORT_SYMBOL_NS(hda_set_dai_drv_ops, "SND_SOC_SOF_INTEL_HDA_COMMON"); @@ -770,9 +782,17 @@ void hda_ops_free(struct snd_sof_dev *sdev) { if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) { struct sof_ipc4_fw_data *ipc4_data = sdev->private; + struct snd_ipc4_nhlt *entry; + struct snd_ipc4_nhlt *tmp; - if (!hda_use_tplg_nhlt) - intel_nhlt_free(ipc4_data->nhlt); + if (!hda_use_tplg_nhlt) { + list_for_each_entry_safe(entry, tmp, &ipc4_data->nhlt_list, list) { + if (entry->from_acpi && entry->nhlt) + intel_nhlt_free(entry->nhlt); + + list_del(&entry->list); + } + } kfree(sdev->private); sdev->private = NULL; diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c index c1018893750c4b..dc3d2b08d7ce66 100644 --- a/sound/soc/sof/intel/icl.c +++ b/sound/soc/sof/intel/icl.c @@ -127,6 +127,7 @@ int sof_icl_ops_init(struct snd_sof_dev *sdev) return -ENOMEM; ipc4_data = sdev->private; + INIT_LIST_HEAD(&ipc4_data->nhlt_list); ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET; ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_2; diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c index 68cf68135d054a..2a6e486f35e1fe 100644 --- a/sound/soc/sof/intel/mtl.c +++ b/sound/soc/sof/intel/mtl.c @@ -742,6 +742,7 @@ int sof_mtl_set_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *dsp_ops) return -ENOMEM; ipc4_data = sdev->private; + INIT_LIST_HEAD(&ipc4_data->nhlt_list); ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET; ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_2; diff --git a/sound/soc/sof/intel/skl.c b/sound/soc/sof/intel/skl.c index 90519ebd316810..f831143a7afc28 100644 --- a/sound/soc/sof/intel/skl.c +++ b/sound/soc/sof/intel/skl.c @@ -67,6 +67,7 @@ int sof_skl_ops_init(struct snd_sof_dev *sdev) return -ENOMEM; ipc4_data = sdev->private; + INIT_LIST_HEAD(&ipc4_data->nhlt_list); ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET_CAVS_1_5; ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_1_5; diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c index 7936361e2e39f5..09a8a4a2b017aa 100644 --- a/sound/soc/sof/intel/tgl.c +++ b/sound/soc/sof/intel/tgl.c @@ -95,6 +95,7 @@ int sof_tgl_ops_init(struct snd_sof_dev *sdev) return -ENOMEM; ipc4_data = sdev->private; + INIT_LIST_HEAD(&ipc4_data->nhlt_list); ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET; ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_2; diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h index a8cdf9bc750b4d..61dd48282dd392 100644 --- a/sound/soc/sof/ipc4-priv.h +++ b/sound/soc/sof/ipc4-priv.h @@ -58,13 +58,18 @@ struct sof_ipc4_fw_library { struct sof_ipc4_fw_module *modules; }; +struct snd_ipc4_nhlt { + struct list_head list; + void *nhlt; + bool from_acpi; +}; + /** * struct sof_ipc4_fw_data - IPC4-specific data * @manifest_fw_hdr_offset: FW header offset in the manifest * @fw_lib_xa: XArray for firmware libraries, including basefw (ID = 0) * Used to store the FW libraries and to manage the unique IDs of the * libraries. - * @nhlt: NHLT table either from the BIOS or the topology manifest * @mtrace_type: mtrace type supported on the booted platform * @mtrace_log_bytes: log bytes as reported by the firmware via fw_config reply * @num_playback_streams: max number of playback DMAs, needed for CHAIN_DMA offset @@ -74,6 +79,7 @@ struct sof_ipc4_fw_library { * base firmware * @fw_context_save: Firmware supports full context save and restore * @libraries_restored: The libraries have been retained during firmware boot + * @nhlt_list: The NHLT tables from the BIOS and the topology manifest * * @load_library: Callback function for platform dependent library loading * @pipeline_state_mutex: Mutex to protect pipeline triggers, ref counts, states and deletion @@ -81,7 +87,6 @@ struct sof_ipc4_fw_library { struct sof_ipc4_fw_data { u32 manifest_fw_hdr_offset; struct xarray fw_lib_xa; - void *nhlt; enum sof_ipc4_mtrace_type mtrace_type; u32 mtrace_log_bytes; int num_playback_streams; @@ -90,6 +95,7 @@ struct sof_ipc4_fw_data { u32 max_libs_count; bool fw_context_save; bool libraries_restored; + struct list_head nhlt_list; int (*load_library)(struct snd_sof_dev *sdev, struct sof_ipc4_fw_library *fw_lib, bool reload); diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 5a1f3b359f8a12..e8e2afa348d8b0 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -1765,12 +1765,13 @@ snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai u32 linktype, u8 dir, u32 **dst, u32 *len) { struct sof_ipc4_fw_data *ipc4_data = sdev->private; - struct nhlt_specific_cfg *cfg; + struct nhlt_specific_cfg *cfg = NULL; + struct snd_ipc4_nhlt *entry = NULL; int sample_rate, channel_count; bool format_change = false; int bit_depth, ret; u32 nhlt_type; - int dev_type = 0; + int dev_type = -EINVAL; /* convert to NHLT type */ switch (linktype) { @@ -1801,10 +1802,17 @@ snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai * Query the type for the port and then pass that information back * to the blob lookup function. */ - dev_type = intel_nhlt_ssp_device_type(sdev->dev, ipc4_data->nhlt, - dai_index); - if (dev_type < 0) + list_for_each_entry(entry, &ipc4_data->nhlt_list, list) { + dev_type = intel_nhlt_ssp_device_type(sdev->dev, entry->nhlt, + dai_index); + if (dev_type >= 0) + break; + } + if (dev_type < 0) { + dev_err(sdev->dev, "%s: No match for SSP%d in NHLT table\n", + __func__, dai_index); return dev_type; + } break; default: return 0; @@ -1814,9 +1822,14 @@ snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai dai_index, nhlt_type, dir, dev_type); /* find NHLT blob with matching params */ - cfg = intel_nhlt_get_endpoint_blob(sdev->dev, ipc4_data->nhlt, dai_index, nhlt_type, - bit_depth, bit_depth, channel_count, sample_rate, - dir, dev_type); + list_for_each_entry(entry, &ipc4_data->nhlt_list, list) { + cfg = intel_nhlt_get_endpoint_blob(sdev->dev, entry->nhlt, dai_index, + nhlt_type, bit_depth, bit_depth, + channel_count, sample_rate, dir, + dev_type); + if (cfg) + break; + } if (!cfg) { bool get_new_blob = false; @@ -1850,13 +1863,15 @@ snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai } if (get_new_blob) { - cfg = intel_nhlt_get_endpoint_blob(sdev->dev, ipc4_data->nhlt, - dai_index, nhlt_type, - bit_depth, bit_depth, - channel_count, sample_rate, - dir, dev_type); - if (cfg) - goto out; + list_for_each_entry(entry, &ipc4_data->nhlt_list, list) { + cfg = intel_nhlt_get_endpoint_blob(sdev->dev, entry->nhlt, + dai_index, nhlt_type, + bit_depth, bit_depth, + channel_count, sample_rate, + dir, dev_type); + if (cfg) + goto out; + } } dev_err(sdev->dev, @@ -3783,13 +3798,22 @@ static int sof_ipc4_parse_manifest(struct snd_soc_component *scomp, int index, switch (le32_to_cpu(manifest_tlv->type)) { case SOF_MANIFEST_DATA_TYPE_NHLT: - /* no NHLT in BIOS, so use the one from topology manifest */ - if (ipc4_data->nhlt) - break; - ipc4_data->nhlt = devm_kmemdup(sdev->dev, manifest_tlv->data, + struct snd_ipc4_nhlt *tplg_nhlt; + + /* Get the nhlt from topology manifest */ + tplg_nhlt = devm_kzalloc(sdev->dev, sizeof(*tplg_nhlt), GFP_KERNEL); + if (!tplg_nhlt) + return -ENOMEM; + + tplg_nhlt->nhlt = devm_kmemdup(sdev->dev, manifest_tlv->data, le32_to_cpu(manifest_tlv->size), GFP_KERNEL); - if (!ipc4_data->nhlt) + if (!tplg_nhlt->nhlt) return -ENOMEM; + + tplg_nhlt->from_acpi = false; + + list_add(&tplg_nhlt->list, &ipc4_data->nhlt_list); + break; default: dev_warn(scomp->dev, "Skipping unknown manifest data type %d\n", From feffa9fbb88de5f8a11cfab058e1a3fac475dc44 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 29 Jan 2026 11:23:35 +0800 Subject: [PATCH 2/6] ALSA: intel-nhlt: lower intel_nhlt_ssp_device_type log level To support multiple topology fragments per function, this function could be called multiple types with different NHLT blobs from different fragments. Downgrade the error message to debug level since the SSP device type could be found from one of the nhlt. Signed-off-by: Bard Liao --- sound/hda/core/intel-nhlt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/hda/core/intel-nhlt.c b/sound/hda/core/intel-nhlt.c index 6d72a871bda0b5..af4d469dbd764b 100644 --- a/sound/hda/core/intel-nhlt.c +++ b/sound/hda/core/intel-nhlt.c @@ -351,7 +351,7 @@ int intel_nhlt_ssp_device_type(struct device *dev, struct nhlt_acpi_table *nhlt, int i; if (!nhlt) { - dev_err(dev, "%s: NHLT table is missing (query for SSP%d)\n", + dev_dbg(dev, "%s: NHLT table is missing (query for SSP%d)\n", __func__, virtual_bus_id); return -EINVAL; } @@ -369,7 +369,7 @@ int intel_nhlt_ssp_device_type(struct device *dev, struct nhlt_acpi_table *nhlt, epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); } - dev_err(dev, "%s: No match for SSP%d in NHLT table\n", __func__, + dev_dbg(dev, "%s: No match for SSP%d in NHLT table\n", __func__, virtual_bus_id); dev_dbg(dev, "Available endpoints:\n"); From 1102cd5073179a6589d5a37826463b41cf650073 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 5 May 2026 13:28:34 +0800 Subject: [PATCH 3/6] ASoC: Intel: sof-function-topology-lib: create common helpers The existing code supports get_function_tplg_files callback for SoundWire machine driver only. Some common sections can be used to extend the support to other machines. Signed-off-by: Bard Liao --- .../intel/common/sof-function-topology-lib.c | 149 +++++++++++------- 1 file changed, 96 insertions(+), 53 deletions(-) diff --git a/sound/soc/intel/common/sof-function-topology-lib.c b/sound/soc/intel/common/sof-function-topology-lib.c index b6e5a40b78cc60..e8ed1b9617715f 100644 --- a/sound/soc/intel/common/sof-function-topology-lib.c +++ b/sound/soc/intel/common/sof-function-topology-lib.c @@ -28,6 +28,90 @@ enum tplg_device_id { #define SOF_INTEL_PLATFORM_NAME_MAX 4 +static int get_platform_name(struct snd_soc_card *card, + const struct snd_soc_acpi_mach *mach, char *platform) +{ + int ret; + + ret = sscanf(mach->sof_tplg_filename, "sof-%3s-*.tplg", platform); + if (ret != 1) { + dev_err(card->dev, "Invalid platform name of tplg %s\n", + mach->sof_tplg_filename); + return -EINVAL; + } + + return 0; +} + +static bool all_tplg_files_exist(struct device *dev, const char ***tplg_files, int tplg_num) +{ + const struct firmware *fw; + int ret; + int i; + + for (i = 0; i < tplg_num; i++) { + ret = firmware_request_nowarn(&fw, (*tplg_files)[i], dev); + if (!ret) { + release_firmware(fw); + } else { + dev_warn(dev, + "Failed to open topology file: %s, you might need to\n", + (*tplg_files)[i]); + dev_warn(dev, + "download it from https://github.com/thesofproject/sof-bin/\n"); + return false; + } + } + + return true; +} + +static char *get_tplg_filename(struct device *dev, const char *prefix, + const char *platform, const char *tplg_dev_name, + int dai_link_id, int tplg_dev) +{ + char *filename = NULL; + + /* + * The tplg file naming rule is sof---id.tplg + * where is only required for the devices that need NHLT blob like DMIC + * as the nhlt blob is platform dependent. + */ + switch (tplg_dev) { + case TPLG_DEVICE_INTEL_PCH_DMIC: + filename = devm_kasprintf(dev, GFP_KERNEL, "%s/sof-%s-%s-id%d.tplg", + prefix, platform, tplg_dev_name, dai_link_id); + break; + default: + filename = devm_kasprintf(dev, GFP_KERNEL, "%s/sof-%s-id%d.tplg", + prefix, tplg_dev_name, dai_link_id); + break; + } + + return filename; +} + +static int get_dmic_tplg_dev(struct device *dev, int dmic_num, + int *tplg_dev, char **tplg_dev_name) +{ + switch (dmic_num) { + case 2: + *tplg_dev_name = "dmic-2ch"; + break; + case 4: + *tplg_dev_name = "dmic-4ch"; + break; + default: + dev_warn(dev, + "unsupported number of dmics: %d\n", + dmic_num); + return -EINVAL; + } + *tplg_dev = TPLG_DEVICE_INTEL_PCH_DMIC; + + return 0; +} + 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, bool best_effort) { @@ -38,7 +122,6 @@ int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_ */ struct snd_soc_acpi_mach_params mach_params = card_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; @@ -46,12 +129,9 @@ int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_ 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; - } + ret = get_platform_name(card, mach, platform); + if (ret < 0) + return ret; for_each_card_prelinks(card, i, dai_link) { char *tplg_dev_name; @@ -70,20 +150,9 @@ int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_ 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, - "unsupported number of dmics: %d\n", - mach_params.dmic_num); + if (get_dmic_tplg_dev(card->dev, mach_params.dmic_num, + &tplg_dev, &tplg_dev_name) < 0) 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"; @@ -111,25 +180,9 @@ int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_ 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; - } + (*tplg_files)[tplg_num] = get_tplg_filename(card->dev, prefix, platform, + tplg_dev_name, dai_link->id, + tplg_dev); if (!(*tplg_files)[tplg_num]) return -ENOMEM; tplg_num++; @@ -138,20 +191,10 @@ int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_ 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_warn(card->dev, - "Failed to open topology file: %s, you might need to\n", - (*tplg_files)[i]); - dev_warn(card->dev, - "download it from https://github.com/thesofproject/sof-bin/\n"); - return 0; - } - } + if (all_tplg_files_exist(card->dev, tplg_files, tplg_num)) + return tplg_num; - return tplg_num; + /* return 0 to use monolithic topology */ + return 0; } EXPORT_SYMBOL_GPL(sof_sdw_get_tplg_files); From 6cb23c299be9f06335686fc901eed7b4cfb10c5b Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 5 May 2026 13:32:16 +0800 Subject: [PATCH 4/6] ASoC: Intel: sof-function-topology-lib: add I2S support for sof_sdw_get_tplg_files The Intel SOF SDW machine drive also supports I2S interface. Add related supports for the sof_sdw_get_tplg_files() callback. Signed-off-by: Bard Liao --- .../intel/common/sof-function-topology-lib.c | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/sound/soc/intel/common/sof-function-topology-lib.c b/sound/soc/intel/common/sof-function-topology-lib.c index e8ed1b9617715f..ac2f704d9511f3 100644 --- a/sound/soc/intel/common/sof-function-topology-lib.c +++ b/sound/soc/intel/common/sof-function-topology-lib.c @@ -19,6 +19,10 @@ enum tplg_device_id { TPLG_DEVICE_SDCA_MIC, TPLG_DEVICE_INTEL_PCH_DMIC, TPLG_DEVICE_HDMI, + TPLG_DEVICE_SSP_JACK, + TPLG_DEVICE_SSP_AMP, + TPLG_DEVICE_SSP_BT, + TPLG_DEVICE_SSP_HDMI_IN, TPLG_DEVICE_LOOPBACK_VIRTUAL, TPLG_DEVICE_MAX }; @@ -79,6 +83,10 @@ static char *get_tplg_filename(struct device *dev, const char *prefix, */ switch (tplg_dev) { case TPLG_DEVICE_INTEL_PCH_DMIC: + case TPLG_DEVICE_SSP_JACK: + case TPLG_DEVICE_SSP_AMP: + case TPLG_DEVICE_SSP_BT: + case TPLG_DEVICE_SSP_HDMI_IN: filename = devm_kasprintf(dev, GFP_KERNEL, "%s/sof-%s-%s-id%d.tplg", prefix, platform, tplg_dev_name, dai_link_id); break; @@ -112,6 +120,53 @@ static int get_dmic_tplg_dev(struct device *dev, int dmic_num, return 0; } +static int get_ssp_tplg_dev(struct device *dev, struct snd_soc_dai_link *dai_link, + u16 *hdmi_in_mask, int *tplg_dev, char **tplg_dev_name) +{ + unsigned int ssp_port; + + if (sscanf(dai_link->name, "SSP%d", &ssp_port) != 1) { + dev_err(dev, "Can't get SSP port from dai_link->name %s\n", dai_link->name); + return -EINVAL; + } + if (strstr(dai_link->name, "Codec")) { + /* + * Assume DAI link 0 is jack which is true in all existing + * machine drivers + */ + if (dai_link->id == 0) { + *tplg_dev = TPLG_DEVICE_SSP_JACK; + *tplg_dev_name = devm_kasprintf(dev, GFP_KERNEL, + "ssp%d-jack", ssp_port); + } else { + *tplg_dev = TPLG_DEVICE_SSP_AMP; + *tplg_dev_name = devm_kasprintf(dev, GFP_KERNEL, + "ssp%d-amp", ssp_port); + } + } else if (strstr(dai_link->name, "BT")) { + *tplg_dev = TPLG_DEVICE_SSP_BT; + *tplg_dev_name = devm_kasprintf(dev, GFP_KERNEL, + "ssp%d-bt", ssp_port); + } else if (strstr(dai_link->name, "HDMI")) { + *hdmi_in_mask |= BIT(ssp_port); + /* The number of HDMI in dai link is always 2 right now */ + if (hweight16(*hdmi_in_mask) != 2) + return -EINVAL; + + *tplg_dev = TPLG_DEVICE_SSP_HDMI_IN; + *tplg_dev_name = devm_kasprintf(dev, GFP_KERNEL, + "ssp%x-hdmiin", *hdmi_in_mask); + } else { + dev_warn(dev, + "unsupported SSP link %s\n", dai_link->name); + return -EINVAL; + } + if (!*tplg_dev_name) + return -ENOMEM; + + return 0; +} + 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, bool best_effort) { @@ -124,6 +179,7 @@ int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_ struct snd_soc_dai_link *dai_link; char platform[SOF_INTEL_PLATFORM_NAME_MAX]; unsigned long tplg_mask = 0; + u16 hdmi_in_mask = 0; int tplg_num = 0; int tplg_dev; int ret; @@ -156,6 +212,10 @@ int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_ } else if (strstr(dai_link->name, "iDisp")) { tplg_dev = TPLG_DEVICE_HDMI; tplg_dev_name = "hdmi-pcm5"; + } else if (strstr(dai_link->name, "SSP")) { + if (get_ssp_tplg_dev(card->dev, dai_link, &hdmi_in_mask, + &tplg_dev, &tplg_dev_name) < 0) + continue; } else if (strstr(dai_link->name, "Loopback_Virtual")) { tplg_dev = TPLG_DEVICE_LOOPBACK_VIRTUAL; /* From 56c212af5a1ca8827291b45d7031073fcb31dd63 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 10 Nov 2025 11:38:56 +0800 Subject: [PATCH 5/6] ASoC: Intel: sof-function-topology-lib: add get_function_topology for I2S machines Add sof_i2s_get_tplg_files() callback for Intel SOF I2S machines. Signed-off-by: Bard Liao --- .../intel/common/sof-function-topology-lib.c | 66 +++++++++++++++++++ .../intel/common/sof-function-topology-lib.h | 3 + 2 files changed, 69 insertions(+) diff --git a/sound/soc/intel/common/sof-function-topology-lib.c b/sound/soc/intel/common/sof-function-topology-lib.c index ac2f704d9511f3..d24c03d2ec1605 100644 --- a/sound/soc/intel/common/sof-function-topology-lib.c +++ b/sound/soc/intel/common/sof-function-topology-lib.c @@ -258,3 +258,69 @@ int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_ return 0; } EXPORT_SYMBOL_GPL(sof_sdw_get_tplg_files); + +int sof_i2s_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_mach *mach, + const char *prefix, const char ***tplg_files, bool best_effort) +{ + struct snd_soc_acpi_mach_params mach_params = mach->mach_params; + struct snd_soc_dai_link *dai_link; + char platform[SOF_INTEL_PLATFORM_NAME_MAX]; + unsigned long tplg_mask = 0; + u16 hdmi_in_mask = 0; + int tplg_num = 0; + int tplg_dev; + int ret; + int i; + + ret = get_platform_name(card, mach, platform); + if (ret < 0) + return ret; + + 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, "SSP")) { + if (get_ssp_tplg_dev(card->dev, dai_link, &hdmi_in_mask, + &tplg_dev, &tplg_dev_name) < 0) + continue; + } else if (strstr(dai_link->name, "dmic")) { + if (get_dmic_tplg_dev(card->dev, mach_params.dmic_num, + &tplg_dev, &tplg_dev_name) < 0) + continue; + } 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); + if (best_effort) + continue; + + return 0; + } + if (tplg_mask & BIT(tplg_dev)) + continue; + + tplg_mask |= BIT(tplg_dev); + + (*tplg_files)[tplg_num] = get_tplg_filename(card->dev, prefix, platform, + tplg_dev_name, dai_link->id, + tplg_dev); + 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 */ + if (all_tplg_files_exist(card->dev, tplg_files, tplg_num)) + return tplg_num; + + /* return 0 to use monolithic topology */ + return 0; +} +EXPORT_SYMBOL_GPL(sof_i2s_get_tplg_files); diff --git a/sound/soc/intel/common/sof-function-topology-lib.h b/sound/soc/intel/common/sof-function-topology-lib.h index f358f8c52d7854..9755e97709685e 100644 --- a/sound/soc/intel/common/sof-function-topology-lib.h +++ b/sound/soc/intel/common/sof-function-topology-lib.h @@ -12,4 +12,7 @@ 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, bool best_effort); +int sof_i2s_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_mach *mach, + const char *prefix, const char ***tplg_files, bool best_effort); + #endif From 1ba0b97085af831fc7ec0fde869b46672c1da1b9 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 1 Dec 2025 21:37:18 +0800 Subject: [PATCH 6/6] ASoC: soc-acpi-intel-ptl-match: I2S machines: use function topology Use sof_i2s_get_tplg_files() for SOF es83x6 machines. Signed-off-by: Bard Liao --- sound/soc/intel/common/soc-acpi-intel-ptl-match.c | 2 ++ 1 file changed, 2 insertions(+) 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 3b7818355ff645..18282cd1743d5c 100644 --- a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c @@ -51,12 +51,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_machines[] = { .drv_name = "ptl_es83x6_c1_h02", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &ptl_lt6911_hdmi, + .get_function_tplg_files = sof_i2s_get_tplg_files, .sof_tplg_filename = "sof-ptl-es83x6-ssp1-hdmi-ssp02.tplg", }, { .comp_ids = &ptl_essx_83x6, .drv_name = "sof-essx8336", .sof_tplg_filename = "sof-ptl-es8336", /* the tplg suffix is added at run time */ + .get_function_tplg_files = sof_i2s_get_tplg_files, .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER | SND_SOC_ACPI_TPLG_INTEL_SSP_MSB | SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER,