diff --git a/sound/soc/intel/common/sof-function-topology-lib.c b/sound/soc/intel/common/sof-function-topology-lib.c index 0daa7d83808be6..f2ebbe85b31795 100644 --- a/sound/soc/intel/common/sof-function-topology-lib.c +++ b/sound/soc/intel/common/sof-function-topology-lib.c @@ -19,6 +19,7 @@ enum tplg_device_id { TPLG_DEVICE_SDCA_MIC, TPLG_DEVICE_INTEL_PCH_DMIC, TPLG_DEVICE_HDMI, + TPLG_DEVICE_LOOPBACK_VIRTUAL, TPLG_DEVICE_MAX }; @@ -27,16 +28,22 @@ enum tplg_device_id { #define SOF_INTEL_PLATFORM_NAME_MAX 4 +struct tplg_device { + enum tplg_device_id id; + int dai_link_id; + const char *name; +}; + 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) { struct snd_soc_acpi_mach_params mach_params = mach->mach_params; struct snd_soc_dai_link *dai_link; + struct tplg_device *tplg_devs; const struct firmware *fw; char platform[SOF_INTEL_PLATFORM_NAME_MAX]; unsigned long tplg_mask = 0; int tplg_num = 0; - int tplg_dev; int ret; int i; @@ -47,29 +54,33 @@ int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_ return -EINVAL; } - for_each_card_prelinks(card, i, dai_link) { - char *tplg_dev_name; + /* The worst case is that each dai link uses a topology */ + tplg_devs = devm_kcalloc(card->dev, card->num_links, sizeof(*tplg_devs), GFP_KERNEL); + if (!tplg_devs) + return -ENOMEM; + for_each_card_prelinks(card, i, dai_link) { dev_dbg(card->dev, "dai_link %s id %d\n", dai_link->name, dai_link->id); if (strstr(dai_link->name, "SimpleJack")) { - tplg_dev = TPLG_DEVICE_SDCA_JACK; - tplg_dev_name = "sdca-jack"; + tplg_devs[tplg_num].id = TPLG_DEVICE_SDCA_JACK; + tplg_devs[tplg_num].name = "sdca-jack"; + } else if (strstr(dai_link->name, "SmartAmp")) { - tplg_dev = TPLG_DEVICE_SDCA_AMP; - tplg_dev_name = devm_kasprintf(card->dev, GFP_KERNEL, + tplg_devs[tplg_num].id = TPLG_DEVICE_SDCA_AMP; + tplg_devs[tplg_num].name = devm_kasprintf(card->dev, GFP_KERNEL, "sdca-%damp", dai_link->num_cpus); - if (!tplg_dev_name) + if (!tplg_devs[tplg_num].name) return -ENOMEM; } else if (strstr(dai_link->name, "SmartMic")) { - tplg_dev = TPLG_DEVICE_SDCA_MIC; - tplg_dev_name = "sdca-mic"; + tplg_devs[tplg_num].id = TPLG_DEVICE_SDCA_MIC; + tplg_devs[tplg_num].name = "sdca-mic"; } else if (strstr(dai_link->name, "dmic")) { switch (mach_params.dmic_num) { case 2: - tplg_dev_name = "dmic-2ch"; + tplg_devs[tplg_num].name = "dmic-2ch"; break; case 4: - tplg_dev_name = "dmic-4ch"; + tplg_devs[tplg_num].name = "dmic-4ch"; break; default: dev_warn(card->dev, @@ -77,11 +88,19 @@ int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_ mach_params.dmic_num); continue; } - tplg_dev = TPLG_DEVICE_INTEL_PCH_DMIC; + tplg_devs[tplg_num].id = TPLG_DEVICE_INTEL_PCH_DMIC; } else if (strstr(dai_link->name, "iDisp")) { - tplg_dev = TPLG_DEVICE_HDMI; - tplg_dev_name = "hdmi-pcm5"; - + tplg_devs[tplg_num].id = TPLG_DEVICE_HDMI; + tplg_devs[tplg_num].name = "hdmi-pcm5"; + } else if (strstr(dai_link->name, "Loopback_Virtual")) { + tplg_devs[tplg_num].id = TPLG_DEVICE_LOOPBACK_VIRTUAL; + /* + * Mark the LOOPBACK_VIRTUAL device but not create the LOOPBACK_VIRTUAL + * topology. The information will be used to create the SoundWire + * topologyes that may or may not include the echo reference + */ + tplg_mask |= BIT(tplg_devs[tplg_num].id); + continue; } else { /* The dai link is not supported by separated tplg yet */ dev_dbg(card->dev, @@ -92,39 +111,76 @@ int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_ return 0; } - if (tplg_mask & BIT(tplg_dev)) + if (tplg_mask & BIT(tplg_devs[tplg_num].id)) continue; - tplg_mask |= BIT(tplg_dev); + tplg_devs[tplg_num].dai_link_id = dai_link->id; + tplg_mask |= BIT(tplg_devs[tplg_num].id); + 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++) { /* * The tplg file naming rule is sof---id.tplg * where is only required for the DMIC function as the nhlt blob * is platform dependent. */ - switch (tplg_dev) { + switch (tplg_devs[i].id) { case TPLG_DEVICE_INTEL_PCH_DMIC: - (*tplg_files)[tplg_num] = devm_kasprintf(card->dev, GFP_KERNEL, + (*tplg_files)[i] = devm_kasprintf(card->dev, GFP_KERNEL, "%s/sof-%s-%s-id%d.tplg", prefix, platform, - tplg_dev_name, dai_link->id); + tplg_devs[i].name, + tplg_devs[i].dai_link_id); + break; + case TPLG_DEVICE_SDCA_JACK: + case TPLG_DEVICE_SDCA_AMP: + if (tplg_mask & BIT(TPLG_DEVICE_LOOPBACK_VIRTUAL)) { + /* Use the topology with echo reference */ + /* + * The echo reference DAI should be created in the first + * function topology that with the echo reference support. + * SDCA JCAK function topology is always loaded before SDCA AMP, + * so if the jack exists, create the echo reference DAI in the + * jack topology, otherwise create it in the amp topology. + */ + const char *ref_name; + if (tplg_devs[i].id == TPLG_DEVICE_SDCA_AMP && + tplg_mask & BIT(TPLG_DEVICE_SDCA_JACK)) + ref_name = "ref"; + else + ref_name = "ref-dai"; + + (*tplg_files)[i] = devm_kasprintf(card->dev, GFP_KERNEL, + "%s/sof-%s-%s-id%d.tplg", + prefix, + tplg_devs[i].name, + ref_name, + tplg_devs[i].dai_link_id); + } else { + /* Use the topology without echo reference */ + (*tplg_files)[i] = devm_kasprintf(card->dev, GFP_KERNEL, + "%s/sof-%s-id%d.tplg", + prefix, + tplg_devs[i].name, + tplg_devs[i].dai_link_id); + } + break; + case TPLG_DEVICE_LOOPBACK_VIRTUAL: + /* No function topology is needed for the LOOPBACK_VIRTUAL DAI link */ break; default: - (*tplg_files)[tplg_num] = devm_kasprintf(card->dev, GFP_KERNEL, + (*tplg_files)[i] = devm_kasprintf(card->dev, GFP_KERNEL, "%s/sof-%s-id%d.tplg", - prefix, tplg_dev_name, - dai_link->id); + prefix, tplg_devs[i].name, + tplg_devs[i].dai_link_id); break; } - if (!(*tplg_files)[tplg_num]) + if (!(*tplg_files)[i]) return -ENOMEM; - 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++) { ret = firmware_request_nowarn(&fw, (*tplg_files)[i], card->dev); if (!ret) { release_firmware(fw);