Skip to content

Commit

Permalink
ASoC: SOF: topology: load multiple topologies
Browse files Browse the repository at this point in the history
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 <yung-chuan.liao@linux.intel.com>
  • Loading branch information
bardliao committed Dec 13, 2024
1 parent 7c8cf93 commit f5eb366
Showing 1 changed file with 180 additions and 3 deletions.
183 changes: 180 additions & 3 deletions sound/soc/sof/topology.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <linux/errno.h>
#include <linux/firmware.h>
#include <linux/workqueue.h>
#include <sound/soc_sdw_utils.h>
#include <sound/tlv.h>
#include <uapi/sound/sof/tokens.h>
#include "sof-priv.h"
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -2464,15 +2465,189 @@ static const struct snd_soc_tplg_ops sof_dspless_tplg_ops = {
.bytes_ext_ops_count = ARRAY_SIZE(sof_dspless_bytes_ext_ops),
};

enum tplg_device_id {
TPLG_DEVICE_SDW_JACK,
TPLG_DEVICE_SDW_AMP,
TPLG_DEVICE_SDW_MIC,
TPLG_DEVICE_PCH_DMIC,
TPLG_DEVICE_HDMI,
TPLG_DEVICE_MAX
};

#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;
int dev;
};

/* 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[TPLG_DEVICE_MAX];
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)
goto legacy_tplg;

for_each_card_prelinks(scomp->card, i, dai_link) {
char *tplg_device;

if (tplg_num >= TPLG_DEVICE_MAX) {
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_PCH_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_files[tplg_num].dev = tplg_dev;;
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++) {
switch (tplg_files[i].dev) {
case TPLG_DEVICE_SDW_JACK:
case TPLG_DEVICE_SDW_AMP:
case TPLG_DEVICE_SDW_MIC:
tplg_files[i].file = devm_kasprintf(sdev->dev, GFP_KERNEL,
"%s/sof-%s-id%d.tplg",
sof_pdata->tplg_filename_prefix,
tplg_files[i].device,
tplg_files[i].be_id);
break;
default:
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);
break;
}
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) {
/* Restore the default firmware name */
tplg_name = (char *)file;
dev_dbg(scomp->dev, "Fail back to %s\n", tplg_name);
goto legacy_tplg;
}

dev_err(scomp->dev, "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, "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);
Expand All @@ -2481,6 +2656,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
Expand All @@ -2492,6 +2668,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();

Expand Down

0 comments on commit f5eb366

Please sign in to comment.