Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

load topology for each device #5209

Open
wants to merge 3 commits into
base: topic/sof-dev
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 176 additions & 5 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 @@ -571,7 +572,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);
bardliao marked this conversation as resolved.
Show resolved Hide resolved
if (!tuples[*num_copied_tuples].value.s)
return -ENOMEM;
} else {
struct snd_soc_tplg_vendor_value_elem *elem;

Expand Down Expand Up @@ -2284,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 @@ -2307,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 @@ -2460,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";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and for the other checks: we need to update this match list every time a new component got introduced to products and we have new tplg fragment?
Is it going to scale?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to update this match list when a new dai links got introduced. Given that we didn't update dai links in our machine drivers. I think we will only update the match list occasionally.

} 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");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are you dropping the 2ch/4ch from the original name?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because I would like to load the topology without dmic. For example, sof-hda-generic-2ch.tplg -> sof-hda-generic.tplg or similar.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the tplg_name is used only for loading the fallback, legacy, monolithic topology, in which case the fragments are not loaded. On a machine which have DMIC, you will load the topology w/o DMIC

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not exactly, tplg_name topology will be also loaded if not all dai links are handled. For example, if the monolithic topology is sof-lnl-rt711-4ch, then sof-lnl-rt711 and sof-lnl-dmic-4ch-id5 will be loaded

} 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;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why continue?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no supported topology for the dai link, but we want to load the topology for other dai links. Then the not handled dai links will be handled at line 2620 when the renamed topology is loaded.

}
if (tplg_mask & BIT(tplg_dev))
continue;
tplg_mask |= BIT(tplg_dev);
tplg_files[tplg_num].be_id = dai_link->id;
bardliao marked this conversation as resolved.
Show resolved Hide resolved
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);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a clean documentation as commit message and comments to explain clearly the expected file naming and also an update to sof-docs must be done.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Totally agree.

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);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the sof_create_ipc_file_profile() will check if the firmware and the topology file (the monolithic one!) is in place.
The means that you always need to have the monolithic tplg and the split components installed, otherwise the profile is rejected and if not fallback IPC version is available then we will fail.

I have a feeling that this split tplg handling has to touch the fw-file-profile.c as well.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I am thinking this open, too. My idea is that we don't touch the monolithic topology. I.e. the monolithic topology will coexist with the split topologies. So that sof_create_ipc_file_profile() will still valid.

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);
Expand All @@ -2477,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
Expand All @@ -2485,11 +2656,11 @@ 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);

out:
if (ret >= 0 && sdev->led_present)
ret = snd_ctl_led_request();

Expand Down
Loading