From 88c6af79721914206ce2d685d6f74471f7ab8959 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 7 Jul 2026 21:25:13 +0800 Subject: [PATCH 1/4] 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 e01d91eb3cc812..b49f0f742fce14 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 74fea057b9c7e25fed755c4debf1ab1071628ef7 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 14 Jul 2026 20:14:09 +0800 Subject: [PATCH 2/4] 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 | 72 ++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index b263f554dcc9a9..90bec32201179d 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" @@ -903,6 +906,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, @@ -951,6 +971,7 @@ static int create_sdw_dailink(struct snd_soc_card *card, int num_cpus = hweight32(sof_dai->link_mask[stream]); int num_codecs = sof_dai->num_devs[stream]; int playback, capture; + int sdw_mic_num = 0; int cur_link = 0; int i = 0, j = 0; char *name; @@ -1014,12 +1035,62 @@ 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 mic_count; + 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]; + + if (function->type != SDCA_FUNCTION_TYPE_SMART_MIC) + continue; + + mic_count = sdca_get_mic_count(peripheral, function); + if (mic_count < 0) + return mic_count; + + dev_dbg(dev, "%s mic num %d\n", + sof_end->codec_name, mic_count); + sdw_mic_num += mic_count; + } + } + } + + switch (sdw_mic_num) { + case 1: + case 2: + case 4: + case 8: + name = devm_kasprintf(dev, GFP_KERNEL, "%s-%dch", + name, sdw_mic_num); + if (!name) + return -ENOMEM; + break; + default: + dev_warn(dev, "mic count %d is not supported\n", sdw_mic_num); + break; } WARN_ON(i != num_cpus || j != num_codecs); @@ -1585,3 +1656,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 522a0d483997709b0cb33fdb65c51bce5d7b6507 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 20 Jul 2026 10:56:47 +0800 Subject: [PATCH 3/4] 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 1935a537cdefec..ccceba19970d16 100644 --- a/sound/soc/intel/common/sof-function-topology-lib.c +++ b/sound/soc/intel/common/sof-function-topology-lib.c @@ -200,8 +200,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")) { if (get_dmic_tplg_dev(card->dev, mach_params.dmic_num, From 9734695664e4c88290d9579b237e118ac6df29d2 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 14 Sep 2026 22:21:37 +0800 Subject: [PATCH 4/4] ASoC: sof_sdw: add cfg-sdw-dmic to component string It will provide the SoundWire dmic number information to UCM and will be used for setting PCM channel number. Signed-off-by: Bard Liao --- sound/soc/intel/boards/sof_sdw.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 90bec32201179d..d30500a1425131 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -1087,6 +1087,13 @@ static int create_sdw_dailink(struct snd_soc_card *card, name, sdw_mic_num); if (!name) return -ENOMEM; + + /* Update card components for sdw dmic components */ + card->components = devm_kasprintf(card->dev, GFP_KERNEL, + "%s cfg-sdw-dmic:%d", + card->components, sdw_mic_num); + if (!card->components) + return -ENOMEM; break; default: dev_warn(dev, "mic count %d is not supported\n", sdw_mic_num); @@ -1585,6 +1592,11 @@ static int mc_probe(struct platform_device *pdev) for (i = 0; i < ctx->codec_info_list_count; i++) codec_info_list[i].amp_num = 0; + /* Create an empty card->components */ + card->components = ""; + if (!card->components) + return -ENOMEM; + ret = sof_card_dai_links_create(card); if (ret < 0) return ret; @@ -1598,7 +1610,7 @@ static int mc_probe(struct platform_device *pdev) amp_num += codec_info_list[i].amp_num; card->components = devm_kasprintf(&pdev->dev, GFP_KERNEL, - " cfg-amp:%d", amp_num); + "%s cfg-amp:%d", card->components, amp_num); if (!card->components) return -ENOMEM;