From 712242b54701cafcb65a0d59056d6c5562bfc0c1 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 7 Jul 2026 21:25:13 +0800 Subject: [PATCH 1/3] ASoC: sdca: add sdca_get_mic_count helper We can get how many mic transducers are connected to the codec. The information will be used for selecting the topology with proper dmic channel number. Signed-off-by: Bard Liao --- include/sound/sdca.h | 5 ++ sound/soc/sdca/sdca_functions.c | 118 ++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) diff --git a/include/sound/sdca.h b/include/sound/sdca.h index 2bdf4e333e0449..61e4015f81547e 100644 --- a/include/sound/sdca.h +++ b/include/sound/sdca.h @@ -65,6 +65,7 @@ enum sdca_quirk { void sdca_lookup_functions(struct sdw_slave *slave); void sdca_lookup_swft(struct sdw_slave *slave); void sdca_lookup_interface_revision(struct sdw_slave *slave); +int sdca_get_mic_count(struct sdw_slave *slave, struct sdca_function_desc *function); bool sdca_device_quirk_match(struct sdw_slave *slave, enum sdca_quirk quirk); int sdca_dev_register_functions(struct sdw_slave *slave); void sdca_dev_unregister_functions(struct sdw_slave *slave); @@ -74,6 +75,10 @@ void sdca_dev_unregister_functions(struct sdw_slave *slave); static inline void sdca_lookup_functions(struct sdw_slave *slave) {} static inline void sdca_lookup_swft(struct sdw_slave *slave) {} static inline void sdca_lookup_interface_revision(struct sdw_slave *slave) {} +static inline int sdca_get_mic_count(struct sdw_slave *slave, struct sdca_function_desc *function) +{ + return 0; +} static inline bool sdca_device_quirk_match(struct sdw_slave *slave, enum sdca_quirk quirk) { return false; diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index 77940bd6b33c95..ed35c0dc93798b 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -200,6 +200,124 @@ void sdca_lookup_functions(struct sdw_slave *slave) } EXPORT_SYMBOL_NS(sdca_lookup_functions, "SND_SOC_SDCA"); +int sdca_get_mic_count(struct sdw_slave *slave, struct sdca_function_desc *function) +{ + struct fwnode_handle *function_node; + u32 *entity_list __free(kfree) = NULL; + u32 transducer_count = 0; + u32 terminal_type; + int num_entities; + int ret; + int i; + + if (!function || !function->node) + return -EINVAL; + + function_node = function->node; + + num_entities = fwnode_property_count_u32(function_node, + "mipi-sdca-entity-id-list"); + if (num_entities < 0) { + dev_err(&slave->dev, + "%pfwP: failed to get entity id list count for function type %u: %d\n", + function_node, function->type, num_entities); + return num_entities; + } + if (num_entities == 0) { + dev_warn(&slave->dev, + "%pfwP: empty entity id list for function type %u\n", + function_node, function->type); + return -ENODATA; + } + if (num_entities > SDCA_MAX_ENTITY_COUNT) { + dev_err(&slave->dev, + "%pfwP: entity number %d is invalid for function type %u\n", + function_node, num_entities, function->type); + return -EINVAL; + } + + entity_list = kcalloc(num_entities, sizeof(*entity_list), GFP_KERNEL); + if (!entity_list) + return -ENOMEM; + + ret = fwnode_property_read_u32_array(function_node, + "mipi-sdca-entity-id-list", + entity_list, num_entities); + if (ret) { + dev_err(&slave->dev, + "%pfwP: failed to read entity id list for function type %u: %d\n", + function_node, function->type, ret); + return ret; + } + + for (i = 0; i < num_entities; i++) { + struct fwnode_handle *entity_node; + const char *entity_label = ""; + u32 entity_type; + char entity_property[SDCA_PROPERTY_LENGTH]; + + /* DisCo uses upper-case for hex numbers */ + snprintf(entity_property, sizeof(entity_property), + "mipi-sdca-entity-id-0x%X-subproperties", entity_list[i]); + + entity_node = fwnode_get_named_child_node(function_node, entity_property); + if (!entity_node) { + dev_dbg(&slave->dev, "%pfwP: missing entity node %s\n", + function_node, entity_property); + continue; + } + + fwnode_property_read_string(entity_node, "mipi-sdca-entity-label", + &entity_label); + + ret = fwnode_property_read_u32(entity_node, "mipi-sdca-entity-type", + &entity_type); + if (ret) { + dev_info(&slave->dev, + "SDCA function %s (type %u adr 0x%x): entity 0x%x label %s type missing\n", + function->name, function->type, function->adr, + entity_list[i], entity_label); + goto put_entity_node; + } + + dev_dbg(&slave->dev, + "SDCA function %s (type %u adr 0x%x): entity 0x%x label %s type 0x%x\n", + function->name, function->type, function->adr, + entity_list[i], entity_label, entity_type); + + if (entity_type == SDCA_ENTITY_TYPE_IT) { + ret = fwnode_property_read_u32(entity_node, + "mipi-sdca-terminal-type", + &terminal_type); + if (ret) + goto put_entity_node; + + if (terminal_type != SDCA_TERM_TYPE_MICROPHONE_TRANSDUCER && + terminal_type != SDCA_TERM_TYPE_MICROPHONE_ARRAY_TRANSDUCER) + goto put_entity_node; + + ret = fwnode_property_read_u32(entity_node, + "mipi-sdca-terminal-transducer-count", + &transducer_count); + if (ret) + goto put_entity_node; + + dev_dbg(&slave->dev, + "SDCA function %s entity %s: mipi-sdca-terminal-type=%#x mipi-sdca-terminal-transducer-count=%u\n", + function->name, entity_label, terminal_type, transducer_count); + + fwnode_handle_put(entity_node); + break; + } + +put_entity_node: + fwnode_handle_put(entity_node); + } + + return transducer_count; +} +EXPORT_SYMBOL_NS(sdca_get_mic_count, "SND_SOC_SDCA"); + struct raw_init_write { __le32 addr; u8 val; From 11293dbe9ca2f0536e8ba5696489325c52e111d7 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 14 Jul 2026 20:14:09 +0800 Subject: [PATCH 2/3] ASoC: Intel: sof_sdw: get sdw dmic number The commit get the sdca sdw dmic number form the mipi-sdca-cluster-channel-id property. The dai link name will be used to select the function topology with a proper dmic channel number. Signed-off-by: Bard Liao --- sound/soc/intel/boards/sof_sdw.c | 78 ++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index d5383c8dcdf099..3e9ecf60a75d3b 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -14,6 +14,9 @@ #include #include #include +#include +#include +#include #include #include "sof_sdw_common.h" #include "../../codecs/rt711.h" @@ -900,6 +903,23 @@ static const struct snd_soc_ops sdw_ops = { static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"}; +static struct sdw_slave *sof_sdw_get_peripheral_by_codec_name(const char *codec_name) +{ + struct snd_soc_component *component; + + component = snd_soc_lookup_component_by_name(codec_name); + if (!component) + return NULL; + + if (is_sdw_slave(component->dev)) + return dev_to_sdw_dev(component->dev); + + if (component->dev->parent && is_sdw_slave(component->dev->parent)) + return dev_to_sdw_dev(component->dev->parent); + + return NULL; +} + static int create_sdw_dailink(struct snd_soc_card *card, struct asoc_sdw_dailink *sof_dai, struct snd_soc_dai_link **dai_links, @@ -1011,12 +1031,69 @@ static int create_sdw_dailink(struct snd_soc_card *card, codecs[j].name = sof_end->codec_name; codecs[j].dai_name = sof_end->dai_info->dai_name; + if (sof_end->dai_info->dai_type == SOC_SDW_DAI_TYPE_MIC && mach_params->dmic_num > 0) { dev_warn(dev, "Both SDW DMIC and PCH DMIC are present, if incorrect, please set kernel params snd_sof_intel_hda_generic dmic_num=0 to disable PCH DMIC\n"); } j++; + + if (sof_end->dai_info->dai_type == SOC_SDW_DAI_TYPE_MIC) { + struct sdw_slave *peripheral; + const char *codec_name = sof_end->codec_name; + int sdw_mic_num; + int k; + + peripheral = sof_sdw_get_peripheral_by_codec_name(codec_name); + if (!peripheral) { + /* + * asoc_sdw_parse_sdw_endpoints() is already checked + * peripheral is not NULL, so this should never happen. + */ + dev_err(dev, "Can't get peripheral for codec %s\n", + sof_end->codec_name); + return -EINVAL; + } + + for (k = 0; k < peripheral->sdca_data.num_functions; k++) { + struct sdca_function_desc *function = + &peripheral->sdca_data.function[k]; + char *tmp = name; + + if (function->type != SDCA_FUNCTION_TYPE_SMART_MIC) + continue; + + sdw_mic_num = sdca_get_mic_count(peripheral, function); + if (sdw_mic_num < 0) { + dev_dbg(dev, "%s mic count query failed: %d\n", + sof_end->codec_name, sdw_mic_num); + continue; + } + dev_dbg(dev, "%s mic num %d\n", + sof_end->codec_name, sdw_mic_num); + + if (strstr(name, "ch")) + continue; + + if (sdw_mic_num != 1 && sdw_mic_num != 2 && + sdw_mic_num != 4 && sdw_mic_num != 8) { + dev_warn(dev, + "%s mic count %d is not supported\n", + sof_end->codec_name, sdw_mic_num); + continue; + } + + name = devm_kasprintf(dev, GFP_KERNEL, "%s-%dch", + name, sdw_mic_num); + if (!name) + return -ENOMEM; + + devm_kfree(dev, tmp); + break; + } + } + } WARN_ON(i != num_cpus || j != num_codecs); @@ -1582,3 +1659,4 @@ MODULE_AUTHOR("Pierre-Louis Bossart "); MODULE_LICENSE("GPL v2"); MODULE_IMPORT_NS("SND_SOC_INTEL_HDA_DSP_COMMON"); MODULE_IMPORT_NS("SND_SOC_SDW_UTILS"); +MODULE_IMPORT_NS("SND_SOC_SDCA"); From a4325234647945ef9962833c611d9fe064c8a92d Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 20 Jul 2026 10:56:47 +0800 Subject: [PATCH 3/3] ASoC: Intel: sof-function-topology-lib: add different channel number sdw dmic support Now the machine driver provides the sdca dmic channel number information in the dai link name. We can use the information to select different topologies for different dmic channels. To be backward compatible, the 2ch sdca dmic topology will still use the "sdca-mic" topology. Signed-off-by: Bard Liao --- sound/soc/intel/common/sof-function-topology-lib.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/soc/intel/common/sof-function-topology-lib.c b/sound/soc/intel/common/sof-function-topology-lib.c index b6e5a40b78cc60..5173c052097fb2 100644 --- a/sound/soc/intel/common/sof-function-topology-lib.c +++ b/sound/soc/intel/common/sof-function-topology-lib.c @@ -66,8 +66,18 @@ int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_ "sdca-%damp", dai_link->num_cpus); if (!tplg_dev_name) return -ENOMEM; + } else if (strstr(dai_link->name, "SmartMic-8ch")) { + tplg_dev = TPLG_DEVICE_SDCA_MIC; + tplg_dev_name = "sdca-mic-8ch"; + } else if (strstr(dai_link->name, "SmartMic-4ch")) { + tplg_dev = TPLG_DEVICE_SDCA_MIC; + tplg_dev_name = "sdca-mic-4ch"; + } else if (strstr(dai_link->name, "SmartMic-1ch")) { + tplg_dev = TPLG_DEVICE_SDCA_MIC; + tplg_dev_name = "sdca-mic-1ch"; } else if (strstr(dai_link->name, "SmartMic")) { tplg_dev = TPLG_DEVICE_SDCA_MIC; + /* Use the default 2ch topology */ tplg_dev_name = "sdca-mic"; } else if (strstr(dai_link->name, "dmic")) { switch (mach_params.dmic_num) {