summaryrefslogtreecommitdiff
path: root/sound/soc/sof/topology.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/sof/topology.c')
-rw-r--r--sound/soc/sof/topology.c235
1 files changed, 190 insertions, 45 deletions
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index 698129dccc7d..c1083ea4624a 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -3,7 +3,7 @@
// This file is provided under a dual BSD/GPLv2 license. When using or
// redistributing this file, you may do so under either license.
//
-// Copyright(c) 2018 Intel Corporation. All rights reserved.
+// Copyright(c) 2018 Intel Corporation
//
// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
//
@@ -19,6 +19,10 @@
#include "sof-audio.h"
#include "ops.h"
+static bool disable_function_topology;
+module_param(disable_function_topology, bool, 0444);
+MODULE_PARM_DESC(disable_function_topology, "Disable function topology loading");
+
#define COMP_ID_UNASSIGNED 0xffffffff
/*
* Constants used in the computation of linear volume gain
@@ -289,13 +293,15 @@ static const struct sof_dai_types sof_dais[] = {
{"ALH", SOF_DAI_INTEL_ALH},
{"SAI", SOF_DAI_IMX_SAI},
{"ESAI", SOF_DAI_IMX_ESAI},
- {"ACP", SOF_DAI_AMD_BT},
+ {"ACPBT", SOF_DAI_AMD_BT},
{"ACPSP", SOF_DAI_AMD_SP},
{"ACPDMIC", SOF_DAI_AMD_DMIC},
{"ACPHS", SOF_DAI_AMD_HS},
{"AFE", SOF_DAI_MEDIATEK_AFE},
{"ACPSP_VIRTUAL", SOF_DAI_AMD_SP_VIRTUAL},
{"ACPHS_VIRTUAL", SOF_DAI_AMD_HS_VIRTUAL},
+ {"MICFIL", SOF_DAI_IMX_MICFIL},
+ {"ACP_SDW", SOF_DAI_AMD_SDW},
};
@@ -405,6 +411,10 @@ static const struct sof_topology_token stream_tokens[] = {
offsetof(struct snd_sof_pcm, stream[0].d0i3_compatible)},
{SOF_TKN_STREAM_CAPTURE_COMPATIBLE_D0I3, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16,
offsetof(struct snd_sof_pcm, stream[1].d0i3_compatible)},
+ {SOF_TKN_STREAM_PLAYBACK_PAUSE_SUPPORTED, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16,
+ offsetof(struct snd_sof_pcm, stream[0].pause_supported)},
+ {SOF_TKN_STREAM_CAPTURE_PAUSE_SUPPORTED, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16,
+ offsetof(struct snd_sof_pcm, stream[1].pause_supported)},
};
/* Leds */
@@ -565,7 +575,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;
@@ -1057,7 +1071,7 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp,
struct snd_sof_dai *dai)
{
struct snd_soc_card *card = scomp->card;
- struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_pcm_runtime *rtd, *full, *partial;
struct snd_soc_dai *cpu_dai;
int stream;
int i;
@@ -1074,12 +1088,22 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp,
else
goto end;
+ full = NULL;
+ partial = NULL;
list_for_each_entry(rtd, &card->rtd_list, list) {
/* does stream match DAI link ? */
- if (!rtd->dai_link->stream_name ||
- !strstr(rtd->dai_link->stream_name, w->sname))
- continue;
+ if (rtd->dai_link->stream_name) {
+ if (!strcmp(rtd->dai_link->stream_name, w->sname)) {
+ full = rtd;
+ break;
+ } else if (strstr(rtd->dai_link->stream_name, w->sname)) {
+ partial = rtd;
+ }
+ }
+ }
+ rtd = full ? full : partial;
+ if (rtd) {
for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
/*
* Please create DAI widget in the right order
@@ -1117,10 +1141,11 @@ static void sof_disconnect_dai_widget(struct snd_soc_component *scomp,
{
struct snd_soc_card *card = scomp->card;
struct snd_soc_pcm_runtime *rtd;
+ const char *sname = w->sname;
struct snd_soc_dai *cpu_dai;
int i, stream;
- if (!w->sname)
+ if (!sname)
return;
if (w->id == snd_soc_dapm_dai_out)
@@ -1133,7 +1158,7 @@ static void sof_disconnect_dai_widget(struct snd_soc_component *scomp,
list_for_each_entry(rtd, &card->rtd_list, list) {
/* does stream match DAI link ? */
if (!rtd->dai_link->stream_name ||
- strcmp(w->sname, rtd->dai_link->stream_name))
+ !strstr(rtd->dai_link->stream_name, sname))
continue;
for_each_rtd_cpu_dais(rtd, i, cpu_dai)
@@ -1266,8 +1291,8 @@ static int sof_widget_parse_tokens(struct snd_soc_component *scomp, struct snd_s
struct snd_sof_tuple *new_tuples;
num_tuples += token_list[object_token_list[i]].count * (num_sets - 1);
- new_tuples = krealloc(swidget->tuples,
- sizeof(*new_tuples) * num_tuples, GFP_KERNEL);
+ new_tuples = krealloc_array(swidget->tuples,
+ num_tuples, sizeof(*new_tuples), GFP_KERNEL);
if (!new_tuples) {
ret = -ENOMEM;
goto err;
@@ -1346,7 +1371,7 @@ static int sof_parse_pin_binding(struct snd_sof_widget *swidget,
/* copy pin binding array to swidget only if it is defined in topology */
if (pin_binding[0]) {
- pb = kmemdup(pin_binding, num_pins * sizeof(char *), GFP_KERNEL);
+ pb = kmemdup_array(pin_binding, num_pins, sizeof(char *), GFP_KERNEL);
if (!pb) {
ret = -ENOMEM;
goto err;
@@ -1366,6 +1391,20 @@ err:
return ret;
}
+static int get_w_no_wname_in_long_name(void *elem, void *object, u32 offset)
+{
+ struct snd_soc_tplg_vendor_value_elem *velem = elem;
+ struct snd_soc_dapm_widget *w = object;
+
+ w->no_wname_in_kcontrol_name = !!le32_to_cpu(velem->value);
+ return 0;
+}
+
+static const struct sof_topology_token dapm_widget_tokens[] = {
+ {SOF_TKN_COMP_NO_WNAME_IN_KCONTROL_NAME, SND_SOC_TPLG_TUPLE_TYPE_BOOL,
+ get_w_no_wname_in_long_name, 0}
+};
+
/* external widget init - used for any driver specific init */
static int sof_widget_ready(struct snd_soc_component *scomp, int index,
struct snd_soc_dapm_widget *w,
@@ -1396,6 +1435,14 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
ida_init(&swidget->output_queue_ida);
ida_init(&swidget->input_queue_ida);
+ ret = sof_parse_tokens(scomp, w, dapm_widget_tokens, ARRAY_SIZE(dapm_widget_tokens),
+ priv->array, le32_to_cpu(priv->size));
+ if (ret < 0) {
+ dev_err(scomp->dev, "failed to parse dapm widget tokens for %s\n",
+ w->name);
+ goto widget_free;
+ }
+
ret = sof_parse_tokens(scomp, swidget, comp_pin_tokens,
ARRAY_SIZE(comp_pin_tokens), priv->array,
le32_to_cpu(priv->size));
@@ -1506,10 +1553,9 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
/* check token parsing reply */
if (ret < 0) {
dev_err(scomp->dev,
- "error: failed to add widget id %d type %d name : %s stream %s\n",
- tw->shift, swidget->id, tw->name,
- strnlen(tw->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) > 0
- ? tw->sname : "none");
+ "failed to add widget type %d name : %s stream %s\n",
+ swidget->id, tw->name, strnlen(tw->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) > 0
+ ? tw->sname : "none");
goto widget_free;
}
@@ -1713,8 +1759,10 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index,
/* perform pcm set op */
if (ipc_pcm_ops && ipc_pcm_ops->pcm_setup) {
ret = ipc_pcm_ops->pcm_setup(sdev, spcm);
- if (ret < 0)
+ if (ret < 0) {
+ kfree(spcm);
return ret;
+ }
}
dai_drv->dobj.private = spcm;
@@ -1863,9 +1911,9 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_
return -ENOMEM;
slink->num_hw_configs = le32_to_cpu(cfg->num_hw_configs);
- slink->hw_configs = kmemdup(cfg->hw_config,
- sizeof(*slink->hw_configs) * slink->num_hw_configs,
- GFP_KERNEL);
+ slink->hw_configs = kmemdup_array(cfg->hw_config,
+ slink->num_hw_configs, sizeof(*slink->hw_configs),
+ GFP_KERNEL);
if (!slink->hw_configs) {
kfree(slink);
return -ENOMEM;
@@ -1930,6 +1978,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_
token_id = SOF_ACPDMIC_TOKENS;
num_tuples += token_list[SOF_ACPDMIC_TOKENS].count;
break;
+ case SOF_DAI_AMD_BT:
case SOF_DAI_AMD_SP:
case SOF_DAI_AMD_HS:
case SOF_DAI_AMD_SP_VIRTUAL:
@@ -1937,6 +1986,14 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_
token_id = SOF_ACPI2S_TOKENS;
num_tuples += token_list[SOF_ACPI2S_TOKENS].count;
break;
+ case SOF_DAI_IMX_MICFIL:
+ token_id = SOF_MICFIL_TOKENS;
+ num_tuples += token_list[SOF_MICFIL_TOKENS].count;
+ break;
+ case SOF_DAI_AMD_SDW:
+ token_id = SOF_ACP_SDW_TOKENS;
+ num_tuples += token_list[SOF_ACP_SDW_TOKENS].count;
+ break;
default:
break;
}
@@ -2015,6 +2072,8 @@ static int sof_link_unload(struct snd_soc_component *scomp, struct snd_soc_dobj
if (!slink)
return 0;
+ slink->link->platforms->name = NULL;
+
kfree(slink->tuples);
list_del(&slink->list);
kfree(slink->hw_configs);
@@ -2157,6 +2216,8 @@ static int sof_complete(struct snd_soc_component *scomp)
struct snd_sof_widget *pipe_widget = spipe->pipe_widget;
struct snd_sof_widget *swidget;
+ pipe_widget->instance_id = -EINVAL;
+
/* Update the scheduler widget's IPC structure */
if (widget_ops && widget_ops[pipe_widget->id].ipc_setup) {
ret = widget_ops[pipe_widget->id].ipc_setup(pipe_widget);
@@ -2241,7 +2302,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 struct snd_soc_tplg_ops sof_tplg_ops = {
+static const 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,
@@ -2263,8 +2324,10 @@ static struct snd_soc_tplg_ops sof_tplg_ops = {
.link_load = sof_link_load,
.link_unload = sof_link_unload,
- /* completion - called at completion of firmware loading */
- .complete = sof_complete,
+ /*
+ * No need to set the complete callback. sof_complete will be called explicitly after
+ * topology loading is complete.
+ */
/* manifest - optional to inform component of manifest */
.manifest = sof_manifest,
@@ -2315,26 +2378,55 @@ static int sof_dspless_widget_ready(struct snd_soc_component *scomp, int index,
struct snd_soc_dapm_widget *w,
struct snd_soc_tplg_dapm_widget *tw)
{
+ struct snd_soc_tplg_private *priv = &tw->priv;
+ int ret;
+
+ /* for snd_soc_dapm_widget.no_wname_in_kcontrol_name */
+ ret = sof_parse_tokens(scomp, w, dapm_widget_tokens,
+ ARRAY_SIZE(dapm_widget_tokens),
+ priv->array, le32_to_cpu(priv->size));
+ if (ret < 0) {
+ dev_err(scomp->dev, "failed to parse dapm widget tokens for %s\n",
+ w->name);
+ return ret;
+ }
+
if (WIDGET_IS_DAI(w->id)) {
+ static const struct sof_topology_token dai_tokens[] = {
+ {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, 0}};
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_sof_widget *swidget;
- struct snd_sof_dai dai;
- int ret;
+ struct snd_sof_dai *sdai;
swidget = kzalloc(sizeof(*swidget), GFP_KERNEL);
if (!swidget)
return -ENOMEM;
- memset(&dai, 0, sizeof(dai));
+ sdai = kzalloc(sizeof(*sdai), GFP_KERNEL);
+ if (!sdai) {
+ kfree(swidget);
+ return -ENOMEM;
+ }
+
+ ret = sof_parse_tokens(scomp, &sdai->type, dai_tokens, ARRAY_SIZE(dai_tokens),
+ priv->array, le32_to_cpu(priv->size));
+ if (ret < 0) {
+ dev_err(scomp->dev, "Failed to parse DAI tokens for %s\n", tw->name);
+ kfree(swidget);
+ kfree(sdai);
+ return ret;
+ }
- ret = sof_connect_dai_widget(scomp, w, tw, &dai);
+ ret = sof_connect_dai_widget(scomp, w, tw, sdai);
if (ret) {
kfree(swidget);
+ kfree(sdai);
return ret;
}
swidget->scomp = scomp;
swidget->widget = w;
+ swidget->private = sdai;
mutex_init(&swidget->setup_mutex);
w->dobj.private = swidget;
list_add(&swidget->list, &sdev->widget_list);
@@ -2358,6 +2450,7 @@ static int sof_dspless_widget_unload(struct snd_soc_component *scomp,
/* remove and free swidget object */
list_del(&swidget->list);
+ kfree(swidget->private);
kfree(swidget);
}
@@ -2377,7 +2470,7 @@ static int sof_dspless_link_load(struct snd_soc_component *scomp, int index,
return 0;
}
-static struct snd_soc_tplg_ops sof_dspless_tplg_ops = {
+static const struct snd_soc_tplg_ops sof_dspless_tplg_ops = {
/* external widget init - used for any driver specific init */
.widget_ready = sof_dspless_widget_ready,
.widget_unload = sof_dspless_widget_unload,
@@ -2401,36 +2494,88 @@ static struct snd_soc_tplg_ops sof_dspless_tplg_ops = {
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;
+ const char *tplg_filename_prefix = sof_pdata->tplg_filename_prefix;
const struct firmware *fw;
+ const char **tplg_files;
+ int tplg_cnt = 0;
int ret;
+ int i;
- dev_dbg(scomp->dev, "loading topology:%s\n", file);
+ tplg_files = kcalloc(scomp->card->num_links, sizeof(char *), GFP_KERNEL);
+ if (!tplg_files)
+ return -ENOMEM;
- ret = request_firmware(&fw, file, scomp->dev);
- if (ret < 0) {
- dev_err(scomp->dev, "error: tplg request firmware %s failed err: %d\n",
- file, ret);
- dev_err(scomp->dev,
- "you may need to download the firmware from https://github.com/thesofproject/sof-bin/\n");
- return ret;
+ if (!sof_pdata->disable_function_topology && !disable_function_topology &&
+ sof_pdata->machine && sof_pdata->machine->get_function_tplg_files) {
+ tplg_cnt = sof_pdata->machine->get_function_tplg_files(scomp->card,
+ sof_pdata->machine,
+ tplg_filename_prefix,
+ &tplg_files);
+ if (tplg_cnt < 0) {
+ kfree(tplg_files);
+ return tplg_cnt;
+ }
}
- 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);
+ /*
+ * The monolithic topology will be used if there is no get_function_tplg_files
+ * callback or the callback returns 0.
+ */
+ if (!tplg_cnt) {
+ if (strstr(file, "dummy")) {
+ dev_err(scomp->dev,
+ "Function topology is required, please upgrade sof-firmware\n");
+ return -EINVAL;
+ }
+ tplg_files[0] = file;
+ tplg_cnt = 1;
+ dev_info(scomp->dev, "loading topology: %s\n", file);
+ } else {
+ dev_info(scomp->dev, "Using function topologies instead %s\n", file);
+ }
- if (ret < 0) {
- dev_err(scomp->dev, "error: tplg component load failed %d\n",
- ret);
- ret = -EINVAL;
+ for (i = 0; i < tplg_cnt; i++) {
+ /* Only print the file names if the function topologies are used */
+ if (tplg_files[0] != file)
+ dev_info(scomp->dev, "loading topology %d: %s\n", i, tplg_files[i]);
+
+ ret = request_firmware(&fw, tplg_files[i], scomp->dev);
+ if (ret < 0) {
+ /*
+ * snd_soc_tplg_component_remove(scomp) will be called
+ * if snd_soc_tplg_component_load(scomp) failed and all
+ * objects in the scomp will be removed. No need to call
+ * snd_soc_tplg_component_remove(scomp) here.
+ */
+ dev_err(scomp->dev, "tplg request firmware %s failed err: %d\n",
+ tplg_files[i], ret);
+ goto out;
+ }
+
+ 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 %s component load failed %d\n",
+ tplg_files[i], ret);
+ goto out;
+ }
}
- release_firmware(fw);
+ /* call sof_complete when topologies are loaded successfully */
+ ret = sof_complete(scomp);
+out:
if (ret >= 0 && sdev->led_present)
ret = snd_ctl_led_request();
+ kfree(tplg_files);
+
return ret;
}
EXPORT_SYMBOL(snd_sof_load_topology);