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
195 changes: 188 additions & 7 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,191 @@ 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 topology_file tplg_files[TPLG_DEVICE_MAX];
struct snd_sof_pdata *sof_pdata = sdev->pdata;
struct snd_soc_dai_link *dai_link;
const struct firmware *fw;
bool load_default_tplg = false;
unsigned long tplg_mask = 0;
char platform[4];
int tplg_num = 0;
char *tplg_name;
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);
Copy link
Collaborator

Choose a reason for hiding this comment

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

can there be multiple SmartAmp link? You only going to record the first one. The naming of the device is also interesting, it is postfixed with a number of CPU dais?

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 don't support multiple SmartAmp link in the machine driver. And, yes, postfixing with a number of CPU dais is intentional. We need to set NUM_SDW_AMP_LINKS in the topology.

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

Copy link
Collaborator

Choose a reason for hiding this comment

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

right, but if something fails in between and we need to fallback to the monolithic topology then at line 2641 you will have sof-lnl-rt711.tplg instead the correct monolithic sof-lnl-rt711-4ch.tplg, or am I missing something?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

right, but if something fails in between and we need to fallback to the monolithic topology then at line 2641 you will have sof-lnl-rt711.tplg instead the correct monolithic sof-lnl-rt711-4ch.tplg, or am I missing something?

Yeah, I will restore the tplg file name when it fails back to the monolithic topology, thanks.

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

Choose a reason for hiding this comment

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

I know that you have excluded generic HDA, but things might fall through the cracks and picks this as HDMI where the PCM device id range is not 5,6,7. Which I suppose this fragment will provide.

How this naming going to scale with display audio serviced by SDW?

I'm not sure how this can be handled, but if we let HDA generic into \this logic, it is going to fail.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I added the be_id in the topology name, so that the PCM device id can be any number.


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

Choose a reason for hiding this comment

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

Normally we have 3 display audio PCMs, you record the first one only, how does this be_id (used in generating the topology fragment name) is related? The HDMI fragment will have all PCMs, or?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Currently, the topology will always have 3 display audio PCMs. The be_id represents the first ID. For example, the IDs are 5, 6, and 7 for sof-arl-sdca-hdmi-id5.tplg

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:
case TPLG_DEVICE_HDMI:
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 = firmware_request_nowarn(&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 @@ -2477,19 +2658,19 @@ 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
ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw);

if (ret < 0) {
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