From 9e958d667c92e5c8bff9d86cecec2b9ca561975a Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 28 Oct 2024 16:03:37 +0800 Subject: [PATCH 1/3] ASoC: SOF: topology: allocate string for tuples The existing code point tuples[*num_copied_tuples].value.s to elem->string which works fine if there is only one topology is handled and all the tuples are handled before the topology fw is released. However, we will handle multiple topologies and the tuples may be used after the topology fw is release. Ues devm_kasprintf to allocate the string for the tuples to avoid invalid access. Signed-off-by: Bard Liao --- sound/soc/sof/topology.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 688cc7ac17148a..234dcc3402ad5a 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -571,7 +571,11 @@ static int sof_copy_tuples(struct snd_sof_dev *sdev, struct snd_soc_tplg_vendor_ continue; tuples[*num_copied_tuples].token = tokens[j].token; - tuples[*num_copied_tuples].value.s = elem->string; + tuples[*num_copied_tuples].value.s = + devm_kasprintf(sdev->dev, GFP_KERNEL, + "%s", elem->string); + if (!tuples[*num_copied_tuples].value.s) + return -ENOMEM; } else { struct snd_soc_tplg_vendor_value_elem *elem; From b0a6bab69637ed2c60007762991ceddae9405c07 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 19 Nov 2024 15:54:16 +0800 Subject: [PATCH 2/3] ASoC: SOF: topology: don't convert error code No need to convert the return value of snd_soc_tplg_component_load(). Signed-off-by: Bard Liao --- sound/soc/sof/topology.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 234dcc3402ad5a..89309f0668ac7b 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2489,7 +2489,6 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) if (ret < 0) { dev_err(scomp->dev, "error: tplg component load failed %d\n", ret); - ret = -EINVAL; } release_firmware(fw); From 788e9bff6db3e12454c52cc5bce67ed8f6c30123 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 14 Oct 2024 21:14:48 +0800 Subject: [PATCH 3/3] 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 | 174 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 171 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 89309f0668ac7b..f4be627dcdaa69 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,180 @@ 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 = devm_kasprintf(sdev->dev, GFP_KERNEL, + "%s", file); + if (sdev->pdata->ipc_type == SOF_IPC_TYPE_3) + goto legacy_tplg; + + ret = sscanf(sof_pdata->tplg_filename, "sof-%3s-*.tplg", platform); + if (ret != 1 || !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)) { + /* Restore the default firmware name */ + tplg_name = (char *)file; + 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 +2647,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 +2660,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();