From 95da27bfb4134973a86170a410f0b2d549152093 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 14 Oct 2024 21:14:48 +0800 Subject: [PATCH] ASoC: SOF: topology: load multiple topologies Get device information from dai links and load topology for each device. This allow user create a topology for single device. The driver will select the needed topologies and we don't need to create topologies for each product. Signed-off-by: Bard Liao --- sound/soc/sof/topology.c | 170 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 167 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 89309f0668ac7b..14b757c7bc274a 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include "sof-priv.h" @@ -2288,7 +2289,7 @@ static const struct snd_soc_tplg_bytes_ext_ops sof_bytes_ext_ops[] = { {SOF_TPLG_KCTL_BYTES_VOLATILE_RO, snd_sof_bytes_ext_volatile_get}, }; -static const struct snd_soc_tplg_ops sof_tplg_ops = { +static struct snd_soc_tplg_ops sof_tplg_ops = { /* external kcontrol init - used for any driver specific init */ .control_load = sof_control_load, .control_unload = sof_control_unload, @@ -2311,7 +2312,7 @@ static const struct snd_soc_tplg_ops sof_tplg_ops = { .link_unload = sof_link_unload, /* completion - called at completion of firmware loading */ - .complete = sof_complete, + /* complete will be added in the last tplg ops */ /* manifest - optional to inform component of manifest */ .manifest = sof_manifest, @@ -2464,15 +2465,176 @@ static const struct snd_soc_tplg_ops sof_dspless_tplg_ops = { .bytes_ext_ops_count = ARRAY_SIZE(sof_dspless_bytes_ext_ops), }; +#define MAX_TPLG_NUM 5 + +enum tplg_device_id { + TPLG_DEVICE_SDW_JACK, + TPLG_DEVICE_SDW_AMP, + TPLG_DEVICE_SDW_MIC, + TPLG_DEVICE_HOST_DMIC, + TPLG_DEVICE_HDMI, +}; + +#define SDCA_DEVICE_MASK (BIT(TPLG_DEVICE_SDW_JACK) | BIT(TPLG_DEVICE_SDW_AMP) | \ + BIT(TPLG_DEVICE_SDW_MIC)) + +struct topology_file { + char *device; + char *file; + int be_id; +}; + +static bool is_platform_support_separated_tplg(const char *platform) +{ + if (!strcmp(platform, "mtl") || !strcmp(platform, "lnl") || !strcmp(platform, "ptl")) + return true; + + return false; +} + +/* The code is from https://stackoverflow.com/questions/47116974/remove-a-substring-from-a-string-in-c */ +static char *strremove(char *str, const char *sub) +{ + size_t len = strlen(sub); + + if (len > 0) { + char *p = str; + size_t size = 0; + + while ((p = strstr(p, sub)) != NULL) { + size = (size == 0) ? (p - str) + strlen(p + len) + 1 : size - len; + memmove(p, p + len, size - (p - str)); + } + } + return str; +} + int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_pdata *sof_pdata = sdev->pdata; + struct topology_file tplg_files[MAX_TPLG_NUM]; + struct snd_soc_dai_link *dai_link; const struct firmware *fw; + bool load_default_tplg = false; + unsigned long tplg_mask = 0; + char platform[4]; + char *tplg_name; + int tplg_num = 0; + int tplg_dev; int ret; + int i; dev_dbg(scomp->dev, "loading topology:%s\n", file); - ret = request_firmware(&fw, file, scomp->dev); + tplg_name = (char *)file; + if (sdev->pdata->ipc_type == SOF_IPC_TYPE_3) + goto legacy_tplg; + + ret = sscanf(sof_pdata->tplg_filename, "sof-%3s-*.tplg", platform); + if (!is_platform_support_separated_tplg(platform)) + goto legacy_tplg; + + for_each_card_prelinks(scomp->card, i, dai_link) { + char *tplg_device; + + if (tplg_num >= MAX_TPLG_NUM) { + dev_err(scomp->dev, + "Invalid tplg_num %d, check what happened\n", tplg_num); + return -EINVAL; + } + + dev_dbg(scomp->dev, "dai_link %s id %d\n", dai_link->name, dai_link->id); + if (strstr(dai_link->name, "SimpleJack")) { + tplg_dev = TPLG_DEVICE_SDW_JACK; + tplg_device = "sdca-jack"; + } else if (strstr(dai_link->name, "SmartAmp")) { + tplg_dev = TPLG_DEVICE_SDW_AMP; + tplg_device = devm_kasprintf(sdev->dev, GFP_KERNEL, + "sdca-%damp", dai_link->num_cpus); + if (!tplg_device) + return -ENOMEM; + } else if (strstr(dai_link->name, "SmartMic")) { + tplg_dev = TPLG_DEVICE_SDW_MIC; + tplg_device = "sdca-mic"; + } else if (strstr(dai_link->name, "dmic")) { + if (strstr(file, "-2ch")) { + tplg_device = "dmic-2ch"; + tplg_name = strremove(tplg_name, "-2ch"); + } else if (strstr(file, "-4ch")) { + tplg_device = "dmic-4ch"; + tplg_name = strremove(tplg_name, "-4ch"); + } else { + dev_warn(scomp->dev, + "only -2ch and -4ch are supported for dmic\n"); + continue; + } + tplg_dev = TPLG_DEVICE_HOST_DMIC; + } else if (strstr(dai_link->name, "iDisp")) { + tplg_dev = TPLG_DEVICE_HDMI; + tplg_device = "sdca-hdmi"; + + } else { + /* The dai link is not supported by sperated tplg yet */ + load_default_tplg = true; + continue; + } + if (tplg_mask & BIT(tplg_dev)) + continue; + tplg_mask |= BIT(tplg_dev); + tplg_files[tplg_num].be_id = dai_link->id; + tplg_files[tplg_num].device = tplg_device; + tplg_num++; + } + dev_dbg(scomp->dev, "tplg_mask %#lx tplg_num %d\n", tplg_mask, tplg_num); + + /* Currently, only SDCA topology supported */ + if (!(tplg_mask & SDCA_DEVICE_MASK)) + goto legacy_tplg; + + for (i = 0; i < tplg_num; i++) { + tplg_files[i].file = devm_kasprintf(sdev->dev, GFP_KERNEL, + "%s/sof-%s-%s-id%d.tplg", + sof_pdata->tplg_filename_prefix, platform, + tplg_files[i].device, + tplg_files[i].be_id); + if (!tplg_files[i].file) + return -ENOMEM; + + dev_dbg(scomp->dev, "Requesting %d %s\n", i, tplg_files[i].file); + ret = request_firmware(&fw, tplg_files[i].file, scomp->dev); + if (ret < 0) { + if (i == 0) { + dev_dbg(scomp->dev, "Fail back to %s\n", tplg_name); + goto legacy_tplg; + } + + dev_err(scomp->dev, "error: tplg request firmware %s failed err: %d\n", + tplg_files[i].file, ret); + goto out; + } + /* set complete = sof_complete if it is the last topology */ + if (!load_default_tplg && i == tplg_num - 1) + sof_tplg_ops.complete = sof_complete; + if (sdev->dspless_mode_selected) + ret = snd_soc_tplg_component_load(scomp, &sof_dspless_tplg_ops, fw); + else + ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw); + + release_firmware(fw); + + if (ret < 0) { + dev_err(scomp->dev, "error: tplg component load failed %d\n", + ret); + return ret; + } + } + /* Load topology successfully, goto out */ + if (!load_default_tplg) + goto out; + +legacy_tplg: + ret = request_firmware(&fw, tplg_name, scomp->dev); if (ret < 0) { dev_err(scomp->dev, "error: tplg request firmware %s failed err: %d\n", file, ret); @@ -2481,6 +2643,7 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) return ret; } + sof_tplg_ops.complete = sof_complete; if (sdev->dspless_mode_selected) ret = snd_soc_tplg_component_load(scomp, &sof_dspless_tplg_ops, fw); else @@ -2493,6 +2656,7 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) release_firmware(fw); +out: if (ret >= 0 && sdev->led_present) ret = snd_ctl_led_request();