From dae132dc0069150cefd7bd29f34d443c1ebed5ab 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 | 106 ++++++++++++++++++++++++++++++++ 2 files changed, 111 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..4b350aa241c40c 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -200,6 +200,112 @@ 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 || 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 5656b76807a9896c1b2135da7ff1cff034d608b8 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 | 69 ++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index d5383c8dcdf099..9d416b838d9ae3 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,60 @@ 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 (sdw_mic_num <= 0 || strstr(name, "ch")) + 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 +1650,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 9bb63e3fa8a40435b6fb524fd14efe2eb5c24e8c 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 | 7 +++++++ 1 file changed, 7 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..8d82256d0171b4 100644 --- a/sound/soc/intel/common/sof-function-topology-lib.c +++ b/sound/soc/intel/common/sof-function-topology-lib.c @@ -66,8 +66,15 @@ 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")) { 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) {