From 3264fc3d8d0b6b28de50986714968a6c9b100a1b 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 | 155 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 152 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 4e06991f4e908b..9c09c11a48f78c 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" @@ -2286,7 +2287,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, @@ -2309,7 +2310,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, @@ -2462,15 +2463,161 @@ 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, +}; + +struct topology_file { + char *device; + char *file; + int be_id; +}; + +static bool is_platform_support_separated_tplg(const char *platform) +{ + + if (!strcmp(platform, "hda") || !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; + + 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; + + 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); + } 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; + if (strstr(file, "hda-generic")) { + tplg_device = "idisp"; + } else { + 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); + + 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); + 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); + + if (ret < 0) { + dev_err(scomp->dev, "error: tplg component load failed %d\n", + ret); + release_firmware(fw); + return -EINVAL; + } + + release_firmware(fw); + } + /* 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); @@ -2479,6 +2626,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 @@ -2492,6 +2640,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();