diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index 5175818ff2c1f8..2258bce8d3c605 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -10,6 +10,7 @@ #include #include #include +#include #include static const struct snd_soc_dapm_widget generic_dmic_widgets[] = { @@ -934,10 +935,10 @@ static bool asoc_sdw_is_unique_device(const struct snd_soc_acpi_link_adr *adr_li return true; } -const char *asoc_sdw_get_codec_name(struct device *dev, - const struct asoc_sdw_codec_info *codec_info, - const struct snd_soc_acpi_link_adr *adr_link, - int adr_index) +static const char *_asoc_sdw_get_codec_name(struct device *dev, + const struct asoc_sdw_codec_info *codec_info, + const struct snd_soc_acpi_link_adr *adr_link, + int adr_index) { u64 adr = adr_link->adr_d[adr_index].adr; unsigned int sdw_version = SDW_VERSION(adr); @@ -947,17 +948,24 @@ const char *asoc_sdw_get_codec_name(struct device *dev, unsigned int part_id = SDW_PART_ID(adr); unsigned int class_id = SDW_CLASS_ID(adr); - if (codec_info->codec_name) - return devm_kstrdup(dev, codec_info->codec_name, GFP_KERNEL); - else if (asoc_sdw_is_unique_device(adr_link, sdw_version, mfg_id, part_id, - class_id, adr_index)) + if (asoc_sdw_is_unique_device(adr_link, sdw_version, mfg_id, part_id, + class_id, adr_index)) return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x", link_id, mfg_id, part_id, class_id); - else - return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x:%01x", - link_id, mfg_id, part_id, class_id, unique_id); - return NULL; + return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x:%01x", + link_id, mfg_id, part_id, class_id, unique_id); +} + +const char *asoc_sdw_get_codec_name(struct device *dev, + const struct asoc_sdw_codec_info *codec_info, + const struct snd_soc_acpi_link_adr *adr_link, + int adr_index) +{ + if (codec_info->codec_name) + return devm_kstrdup(dev, codec_info->codec_name, GFP_KERNEL); + + return _asoc_sdw_get_codec_name(dev, codec_info, adr_link, adr_index); } EXPORT_SYMBOL_NS(asoc_sdw_get_codec_name, "SND_SOC_SDW_UTILS"); @@ -1124,6 +1132,106 @@ struct asoc_sdw_dailink *asoc_sdw_find_dailink(struct asoc_sdw_dailink *dailinks } EXPORT_SYMBOL_NS(asoc_sdw_find_dailink, "SND_SOC_SDW_UTILS"); +static int asoc_sdw_get_dai_type(u32 type) +{ + switch (type) { + case SDCA_FUNCTION_TYPE_SMART_AMP: + case SDCA_FUNCTION_TYPE_SIMPLE_AMP: + return SOC_SDW_DAI_TYPE_AMP; + case SDCA_FUNCTION_TYPE_SMART_MIC: + case SDCA_FUNCTION_TYPE_SIMPLE_MIC: + case SDCA_FUNCTION_TYPE_SPEAKER_MIC: + return SOC_SDW_DAI_TYPE_MIC; + case SDCA_FUNCTION_TYPE_UAJ: + case SDCA_FUNCTION_TYPE_RJ: + case SDCA_FUNCTION_TYPE_SIMPLE_JACK: + return SOC_SDW_DAI_TYPE_JACK; + default: + return -EINVAL; + } +} + +/* + * Check if the SDCA endpoint is present by the SDW peripheral + * + * @dev: Device pointer + * @codec_info: Codec info pointer + * @adr_link: ACPI link address + * @adr_index: Index of the ACPI link address + * @end_index: Index of the endpoint + * + * Return: 1 if the endpoint is present, + * 0 if the endpoint is not present, + * negative error code. + */ + +static int is_sdca_endpoint_present(struct device *dev, + struct asoc_sdw_codec_info *codec_info, + const struct snd_soc_acpi_link_adr *adr_link, + int adr_index, int end_index) +{ + const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[adr_index]; + const struct snd_soc_acpi_endpoint *adr_end; + const struct asoc_sdw_dai_info *dai_info; + struct snd_soc_dai_link_component *dlc; + struct snd_soc_dai *codec_dai; + struct sdw_slave *slave; + struct device *sdw_dev; + const char *sdw_codec_name; + int i; + + dlc = kzalloc(sizeof(*dlc), GFP_KERNEL); + + adr_end = &adr_dev->endpoints[end_index]; + dai_info = &codec_info->dais[adr_end->num]; + + dlc->dai_name = dai_info->dai_name; + codec_dai = snd_soc_find_dai_with_mutex(dlc); + if (!codec_dai) { + dev_warn(dev, "codec dai %s not registered yet\n", dlc->dai_name); + kfree(dlc); + return -EPROBE_DEFER; + } + kfree(dlc); + + sdw_codec_name = _asoc_sdw_get_codec_name(dev, codec_info, + adr_link, adr_index); + if (!sdw_codec_name) + return -ENOMEM; + + sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name); + if (!sdw_dev) { + dev_err(dev, "codec %s not found\n", sdw_codec_name); + return -EINVAL; + } + + slave = dev_to_sdw_dev(sdw_dev); + if (!slave) + return -EINVAL; + + /* Make sure BIOS provides SDCA properties */ + if (!slave->sdca_data.interface_revision) { + dev_warn(&slave->dev, "SDCA properties not found in the BIOS\n"); + return 1; + } + + for (i = 0; i < slave->sdca_data.num_functions; i++) { + int dai_type = asoc_sdw_get_dai_type(slave->sdca_data.function[i].type); + + if (dai_type == dai_info->dai_type) { + dev_dbg(&slave->dev, "DAI type %d sdca function %s found\n", + dai_type, slave->sdca_data.function[i].name); + return 1; + } + } + + dev_dbg(&slave->dev, + "SDCA device function for DAI type %d not supported, skip endpoint\n", + dai_info->dai_type); + + return 0; +} + int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, struct asoc_sdw_dailink *soc_dais, struct asoc_sdw_endpoint *soc_ends, @@ -1152,6 +1260,7 @@ int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[i]; struct asoc_sdw_codec_info *codec_info; const char *codec_name; + bool check_sdca = false; if (!adr_dev->name_prefix) { dev_err(dev, "codec 0x%llx does not have a name prefix\n", @@ -1182,6 +1291,9 @@ int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, soc_end->include_sidecar = true; } + if (SDW_CLASS_ID(adr_dev->adr) && adr_dev->num_endpoints > 1) + check_sdca = true; + for (j = 0; j < adr_dev->num_endpoints; j++) { const struct snd_soc_acpi_endpoint *adr_end; const struct asoc_sdw_dai_info *dai_info; @@ -1196,6 +1308,22 @@ int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, !(dai_info->quirk_exclude ^ !!(dai_info->quirk & ctx->mc_quirk))) continue; + /* + * Skip the sdca endpoint presence check if machine quirk is set. + * In other words, quirk should have higher priority than the sdca + * properties in the BIOS. + */ + if (check_sdca && !(dai_info->quirk & ctx->mc_quirk)) { + ret = is_sdca_endpoint_present(dev, codec_info, + adr_link, i, j); + if (ret < 0) + return ret; + + /* The endpoint is not present, skip */ + if (!ret) + continue; + } + dev_dbg(dev, "Add dev: %d, 0x%llx end: %d, dai: %d, %c/%c to %s: %d\n", ffs(adr_link->mask) - 1, adr_dev->adr,