* [PATCH v2 1/7] ASoC: SOF: get nhlt from all topologies
2026-09-23 5:28 [PATCH v2 0/7] ALSA/ASoC: Intel: add I2S function topology support Bard Liao
@ 2026-09-23 5:28 ` Bard Liao
2026-09-23 5:28 ` [PATCH v2 2/7] ALSA: intel-nhlt: lower intel_nhlt_ssp_device_type log level Bard Liao
` (6 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Bard Liao @ 2026-09-23 5:28 UTC (permalink / raw)
To: broonie, tiwai; +Cc: linux-sound, bard.liao, peter.ujfalusi
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 <yung-chuan.liao@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
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 | 63 ++++++++++++++++++++++++-----------
9 files changed, 80 insertions(+), 25 deletions(-)
diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c
index b0072601181e..753da09301fa 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 660c1475e5a4..ec33cd18e669 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 bb44d4f8a4da..c8729b6d82bd 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 549bb4ca73e1..196caba14be0 100644
--- a/sound/soc/sof/intel/icl.c
+++ b/sound/soc/sof/intel/icl.c
@@ -131,6 +131,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 3d67d6777f1b..2506506f813a 100644
--- a/sound/soc/sof/intel/mtl.c
+++ b/sound/soc/sof/intel/mtl.c
@@ -762,6 +762,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 90519ebd3168..f831143a7afc 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 7936361e2e39..09a8a4a2b017 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 a8cdf9bc750b..61dd48282dd3 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 24a1175dd690..f6617dcbf789 100644
--- a/sound/soc/sof/ipc4-topology.c
+++ b/sound/soc/sof/ipc4-topology.c
@@ -1844,12 +1844,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) {
@@ -1880,10 +1881,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;
@@ -1893,9 +1901,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;
@@ -1929,13 +1942,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,
@@ -4069,6 +4084,7 @@ static int sof_ipc4_parse_manifest(struct snd_soc_component *scomp, int index,
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
struct sof_manifest_tlv *manifest_tlv;
+ struct snd_ipc4_nhlt *tplg_nhlt;
struct sof_manifest *manifest;
u32 size = le32_to_cpu(man->priv.size);
u8 *man_ptr = man->priv.data;
@@ -4104,13 +4120,20 @@ 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,
+ /* 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",
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v2 2/7] ALSA: intel-nhlt: lower intel_nhlt_ssp_device_type log level
2026-09-23 5:28 [PATCH v2 0/7] ALSA/ASoC: Intel: add I2S function topology support Bard Liao
2026-09-23 5:28 ` [PATCH v2 1/7] ASoC: SOF: get nhlt from all topologies Bard Liao
@ 2026-09-23 5:28 ` Bard Liao
2026-09-23 5:28 ` [PATCH v2 3/7] ASoC: Intel: sof-function-topology-lib: create common helpers Bard Liao
` (5 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Bard Liao @ 2026-09-23 5:28 UTC (permalink / raw)
To: broonie, tiwai; +Cc: linux-sound, bard.liao, peter.ujfalusi
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 <yung-chuan.liao@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
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 6d72a871bda0..af4d469dbd76 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");
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v2 3/7] ASoC: Intel: sof-function-topology-lib: create common helpers
2026-09-23 5:28 [PATCH v2 0/7] ALSA/ASoC: Intel: add I2S function topology support Bard Liao
2026-09-23 5:28 ` [PATCH v2 1/7] ASoC: SOF: get nhlt from all topologies Bard Liao
2026-09-23 5:28 ` [PATCH v2 2/7] ALSA: intel-nhlt: lower intel_nhlt_ssp_device_type log level Bard Liao
@ 2026-09-23 5:28 ` Bard Liao
2026-09-23 5:28 ` [PATCH v2 4/7] ASoC: Intel: sof-function-topology-lib: skip non-exist file in best_effort mode Bard Liao
` (4 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Bard Liao @ 2026-09-23 5:28 UTC (permalink / raw)
To: broonie, tiwai; +Cc: linux-sound, bard.liao, peter.ujfalusi
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 <yung-chuan.liao@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
.../intel/common/sof-function-topology-lib.c | 139 ++++++++++++------
1 file changed, 90 insertions(+), 49 deletions(-)
diff --git a/sound/soc/intel/common/sof-function-topology-lib.c b/sound/soc/intel/common/sof-function-topology-lib.c
index b6e5a40b78cc..657c7b8e06e8 100644
--- a/sound/soc/intel/common/sof-function-topology-lib.c
+++ b/sound/soc/intel/common/sof-function-topology-lib.c
@@ -28,6 +28,86 @@ 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 tplg_files_exist(struct device *dev, const char *tplg_files)
+{
+ const struct firmware *fw;
+ int ret;
+
+ ret = firmware_request_nowarn(&fw, tplg_files, dev);
+ if (!ret) {
+ release_firmware(fw);
+ return true;
+ }
+
+ dev_warn(dev,
+ "Failed to open topology file: %s, you might need to\n",
+ tplg_files);
+ dev_warn(dev,
+ "download it from https://github.com/thesofproject/sof-bin/\n");
+ return false;
+}
+
+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-<platform>-<function>-id<BE id number>.tplg
+ * where <platform> 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 +118,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 +125,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 +146,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 +176,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-<platform>-<function>-id<BE id number>.tplg
- * where <platform> 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++;
@@ -139,17 +188,9 @@ int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_
/* 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");
+ if (!tplg_files_exist(card->dev, (*tplg_files)[i]))
+ /* return 0 to use monolithic topology */
return 0;
- }
}
return tplg_num;
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v2 4/7] ASoC: Intel: sof-function-topology-lib: skip non-exist file in best_effort mode
2026-09-23 5:28 [PATCH v2 0/7] ALSA/ASoC: Intel: add I2S function topology support Bard Liao
` (2 preceding siblings ...)
2026-09-23 5:28 ` [PATCH v2 3/7] ASoC: Intel: sof-function-topology-lib: create common helpers Bard Liao
@ 2026-09-23 5:28 ` Bard Liao
2026-09-23 5:28 ` [PATCH v2 5/7] ASoC: Intel: sof-function-topology-lib: add I2S support for sof_sdw_get_tplg_files Bard Liao
` (3 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Bard Liao @ 2026-09-23 5:28 UTC (permalink / raw)
To: broonie, tiwai; +Cc: linux-sound, bard.liao, peter.ujfalusi
Currently we will fallback to the monolithic topology if any function
topology is missing. But the monolithic topology may not exist if
best_effort is set. We should load the existing function topologies in
the case.
Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
.../intel/common/sof-function-topology-lib.c | 30 +++++++++++--------
1 file changed, 17 insertions(+), 13 deletions(-)
diff --git a/sound/soc/intel/common/sof-function-topology-lib.c b/sound/soc/intel/common/sof-function-topology-lib.c
index 657c7b8e06e8..e1049bb85a13 100644
--- a/sound/soc/intel/common/sof-function-topology-lib.c
+++ b/sound/soc/intel/common/sof-function-topology-lib.c
@@ -121,6 +121,7 @@ int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_
char platform[SOF_INTEL_PLATFORM_NAME_MAX];
unsigned long tplg_mask = 0;
int tplg_num = 0;
+ char *tplg_file;
int tplg_dev;
int ret;
int i;
@@ -174,25 +175,28 @@ int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_
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])
+ tplg_file = get_tplg_filename(card->dev, prefix, platform, tplg_dev_name,
+ dai_link->id, tplg_dev);
+ if (!tplg_file)
return -ENOMEM;
+
+ /* Check presence of sub-topologies */
+ if (!tplg_files_exist(card->dev, tplg_file)) {
+ devm_kfree(card->dev, tplg_file);
+ if (best_effort)
+ continue;
+
+ return 0;
+ }
+
+ tplg_mask |= BIT(tplg_dev);
+
+ (*tplg_files)[tplg_num] = tplg_file;
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++) {
- if (!tplg_files_exist(card->dev, (*tplg_files)[i]))
- /* return 0 to use monolithic topology */
- return 0;
- }
-
return tplg_num;
}
EXPORT_SYMBOL_GPL(sof_sdw_get_tplg_files);
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v2 5/7] ASoC: Intel: sof-function-topology-lib: add I2S support for sof_sdw_get_tplg_files
2026-09-23 5:28 [PATCH v2 0/7] ALSA/ASoC: Intel: add I2S function topology support Bard Liao
` (3 preceding siblings ...)
2026-09-23 5:28 ` [PATCH v2 4/7] ASoC: Intel: sof-function-topology-lib: skip non-exist file in best_effort mode Bard Liao
@ 2026-09-23 5:28 ` Bard Liao
2026-09-23 5:28 ` [PATCH v2 6/7] ASoC: Intel: sof-function-topology-lib: add get_function_topology for I2S machines Bard Liao
` (2 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Bard Liao @ 2026-09-23 5:28 UTC (permalink / raw)
To: broonie, tiwai; +Cc: linux-sound, bard.liao, peter.ujfalusi
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 <yung-chuan.liao@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
.../intel/common/sof-function-topology-lib.c | 62 ++++++++++++++++++-
1 file changed, 61 insertions(+), 1 deletion(-)
diff --git a/sound/soc/intel/common/sof-function-topology-lib.c b/sound/soc/intel/common/sof-function-topology-lib.c
index e1049bb85a13..b53f9037feff 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
};
@@ -70,11 +74,15 @@ static char *get_tplg_filename(struct device *dev, const char *prefix,
/*
* The tplg file naming rule is sof-<platform>-<function>-id<BE id number>.tplg
- * where <platform> is only required for the devices that need NHLT blob like DMIC
+ * where <platform> is required for functions that depend on NHLT blobs (e.g. DMIC/SSP)
* as the nhlt blob is platform dependent.
*/
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;
@@ -108,6 +116,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%u", &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)
{
@@ -120,6 +175,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;
char *tplg_file;
int tplg_dev;
@@ -153,6 +209,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;
/*
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v2 6/7] ASoC: Intel: sof-function-topology-lib: add get_function_topology for I2S machines
2026-09-23 5:28 [PATCH v2 0/7] ALSA/ASoC: Intel: add I2S function topology support Bard Liao
` (4 preceding siblings ...)
2026-09-23 5:28 ` [PATCH v2 5/7] ASoC: Intel: sof-function-topology-lib: add I2S support for sof_sdw_get_tplg_files Bard Liao
@ 2026-09-23 5:28 ` Bard Liao
2026-09-23 5:28 ` [PATCH v2 7/7] ASoC: soc-acpi-intel-ptl-match: I2S machines: use function topology Bard Liao
2026-09-23 8:41 ` [PATCH v2 0/7] ALSA/ASoC: Intel: add I2S function topology support Mark Brown
7 siblings, 0 replies; 9+ messages in thread
From: Bard Liao @ 2026-09-23 5:28 UTC (permalink / raw)
To: broonie, tiwai; +Cc: linux-sound, bard.liao, peter.ujfalusi
Add sof_i2s_get_tplg_files() callback for Intel SOF I2S machines.
Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
.../intel/common/sof-function-topology-lib.c | 72 +++++++++++++++++++
.../intel/common/sof-function-topology-lib.h | 3 +
2 files changed, 75 insertions(+)
diff --git a/sound/soc/intel/common/sof-function-topology-lib.c b/sound/soc/intel/common/sof-function-topology-lib.c
index b53f9037feff..57e089f8e721 100644
--- a/sound/soc/intel/common/sof-function-topology-lib.c
+++ b/sound/soc/intel/common/sof-function-topology-lib.c
@@ -260,3 +260,75 @@ int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_
return tplg_num;
}
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;
+ char *tplg_file;
+ 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_file = get_tplg_filename(card->dev, prefix, platform, tplg_dev_name,
+ dai_link->id, tplg_dev);
+ if (!tplg_file)
+ return -ENOMEM;
+
+ /* Check presence of sub-topologies */
+ if (!tplg_files_exist(card->dev, tplg_file)) {
+ devm_kfree(card->dev, tplg_file);
+ if (best_effort)
+ continue;
+
+ return 0;
+ }
+
+ tplg_mask |= BIT(tplg_dev);
+
+ (*tplg_files)[tplg_num] = tplg_file;
+ tplg_num++;
+ }
+
+ dev_dbg(card->dev, "tplg_mask %#lx tplg_num %d\n", tplg_mask, tplg_num);
+
+ return tplg_num;
+}
+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 f358f8c52d78..9755e9770968 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
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v2 7/7] ASoC: soc-acpi-intel-ptl-match: I2S machines: use function topology
2026-09-23 5:28 [PATCH v2 0/7] ALSA/ASoC: Intel: add I2S function topology support Bard Liao
` (5 preceding siblings ...)
2026-09-23 5:28 ` [PATCH v2 6/7] ASoC: Intel: sof-function-topology-lib: add get_function_topology for I2S machines Bard Liao
@ 2026-09-23 5:28 ` Bard Liao
2026-09-23 8:41 ` [PATCH v2 0/7] ALSA/ASoC: Intel: add I2S function topology support Mark Brown
7 siblings, 0 replies; 9+ messages in thread
From: Bard Liao @ 2026-09-23 5:28 UTC (permalink / raw)
To: broonie, tiwai; +Cc: linux-sound, bard.liao, peter.ujfalusi
Use sof_i2s_get_tplg_files() for SOF es83x6 machines.
Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
sound/soc/intel/common/soc-acpi-intel-ptl-match.c | 3 +++
1 file changed, 3 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 dd84e97836c9..0ce51c44bfe6 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,
@@ -65,6 +67,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_machines[] = {
{
.id = "INTC10B0",
.drv_name = "ptl_lt6911_hdmi_ssp",
+ .get_function_tplg_files = sof_i2s_get_tplg_files,
.sof_tplg_filename = "sof-ptl-hdmi-ssp02.tplg",
},
{},
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH v2 0/7] ALSA/ASoC: Intel: add I2S function topology support
2026-09-23 5:28 [PATCH v2 0/7] ALSA/ASoC: Intel: add I2S function topology support Bard Liao
` (6 preceding siblings ...)
2026-09-23 5:28 ` [PATCH v2 7/7] ASoC: soc-acpi-intel-ptl-match: I2S machines: use function topology Bard Liao
@ 2026-09-23 8:41 ` Mark Brown
7 siblings, 0 replies; 9+ messages in thread
From: Mark Brown @ 2026-09-23 8:41 UTC (permalink / raw)
To: Bard Liao; +Cc: tiwai, linux-sound, bard.liao, peter.ujfalusi
[-- Attachment #1: Type: text/plain, Size: 441 bytes --]
On Wed, Sep 23, 2026 at 01:28:25PM +0800, Bard Liao wrote:
> Currently, function topology is only used by the SoundWire machines.
> Extend the support to the I2S machines.
>
> The only ALSA change is to lower the log level. Let's go through the
> ASoC tree.
>
> v2: change sscanf(dai_link->name, "SSP%d", &ssp_port) to %u as ssp_port
> is unsigned int.
As I mentioned yesterday please send an incremental change for this.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread