diff --git a/.files b/.files
index 4e5cd85..62fa09e 100644
Binary files a/.files and b/.files differ
diff --git a/.rev b/.rev
index bbf4836..d973fa6 100644
--- a/.rev
+++ b/.rev
@@ -1581,4 +1581,12 @@
758568
+
+ 27de3982a69fe8c4175a40c495f09d45
+ 1.2.1.2
+
+ dimstar_suse
+
+ 766334
+
diff --git a/0032-Update-the-attributes.m4-macro-file-from-xine.patch b/0032-Update-the-attributes.m4-macro-file-from-xine.patch
new file mode 100644
index 0000000..73a916c
--- /dev/null
+++ b/0032-Update-the-attributes.m4-macro-file-from-xine.patch
@@ -0,0 +1,128 @@
+From 9e2bbccfcc8069a676519149a280f20c1e05f0ac Mon Sep 17 00:00:00 2001
+From: David Ward
+Date: Fri, 3 Jan 2020 13:05:51 -0500
+Subject: [PATCH 32/63] Update the attributes.m4 macro file from xine
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This file was imported from the xine project. Update it to the current
+revision, which resolves the "no AC_LANG_SOURCE call detected in body"
+warnings with Autoconf 2.68 or later.
+
+Cc: Diego Pettenò
+Signed-off-by: David Ward
+Signed-off-by: Jaroslav Kysela
+---
+ m4/attributes.m4 | 33 ++++++++++++++++++++-------------
+ 1 file changed, 20 insertions(+), 13 deletions(-)
+
+diff --git a/m4/attributes.m4 b/m4/attributes.m4
+index e86456a468e5..3d9c256a09a2 100644
+--- a/m4/attributes.m4
++++ b/m4/attributes.m4
+@@ -1,6 +1,6 @@
+ dnl Macros to check the presence of generic (non-typed) symbols.
+-dnl Copyright (c) 2006-2007 Diego Pettenò
+-dnl Copyright (c) 2006-2007 xine project
++dnl Copyright (c) 2006-2008 Diego Pettenò
++dnl Copyright (c) 2006-2008 xine project
+ dnl
+ dnl This program is free software; you can redistribute it and/or modify
+ dnl it under the terms of the GNU General Public License as published by
+@@ -25,7 +25,7 @@ dnl License when using or distributing such scripts, even though portions
+ dnl of the text of the Macro appear in them. The GNU General Public
+ dnl License (GPL) does govern all other use of the material that
+ dnl constitutes the Autoconf Macro.
+-dnl
++dnl
+ dnl This special exception to the GPL applies to versions of the
+ dnl Autoconf Macro released by this project. When you make and
+ dnl distribute a modified version of the Autoconf Macro, you may extend
+@@ -39,7 +39,7 @@ AC_DEFUN([CC_CHECK_CFLAGS_SILENT], [
+ AC_CACHE_VAL(AS_TR_SH([cc_cv_cflags_$1]),
+ [ac_save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $1"
+- AC_COMPILE_IFELSE([int a;],
++ AC_COMPILE_IFELSE([AC_LANG_SOURCE([int a;])],
+ [eval "AS_TR_SH([cc_cv_cflags_$1])='yes'"],
+ [eval "AS_TR_SH([cc_cv_cflags_$1])='no'"])
+ CFLAGS="$ac_save_CFLAGS"
+@@ -71,7 +71,7 @@ AC_DEFUN([CC_CHECK_CFLAG_APPEND], [
+ )
+
+ AS_IF([eval test x$]AS_TR_SH([cc_cv_cflags_$1])[ = xyes],
+- [CFLAGS="$CFLAGS $1"; $2], [$3])
++ [CFLAGS="$CFLAGS $1"; DEBUG_CFLAGS="$DEBUG_CFLAGS $1"; $2], [$3])
+ ])
+
+ dnl CC_CHECK_CFLAGS_APPEND([FLAG1 FLAG2], [action-if-found], [action-if-not])
+@@ -89,7 +89,7 @@ AC_DEFUN([CC_CHECK_LDFLAGS], [
+ AS_TR_SH([cc_cv_ldflags_$1]),
+ [ac_save_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS $1"
+- AC_LINK_IFELSE([int main() { return 1; }],
++ AC_LINK_IFELSE([AC_LANG_SOURCE([int main() { return 1; }])],
+ [eval "AS_TR_SH([cc_cv_ldflags_$1])='yes'"],
+ [eval "AS_TR_SH([cc_cv_ldflags_$1])="])
+ LDFLAGS="$ac_save_LDFLAGS"
+@@ -109,14 +109,21 @@ AC_DEFUN([CC_NOUNDEFINED], [
+ dnl FreeBSD (et al.) does not complete linking for shared objects when pthreads
+ dnl are requested, as different implementations are present; to avoid problems
+ dnl use -Wl,-z,defs only for those platform not behaving this way.
+- *-freebsd*) ;;
++ dnl
++ dnl MinGW platforms: for libraries required -no-undefined,
++ dnl use it only for libraries in mingw32-w64
++
++ *-freebsd* | *-openbsd*) ;;
++ *-mingw*)
++ LDFLAGS_NOUNDEFINED="-no-undefined"
++ ;;
+ *)
+ dnl First of all check for the --no-undefined variant of GNU ld. This allows
+ dnl for a much more readable commandline, so that people can understand what
+ dnl it does without going to look for what the heck -z defs does.
+- for possible_flags in "-Wl,--no-undefined" "-Wl,-z,defs"; do
++ for possible_flags in "-Wl,--no-undefined" "-Wl,-z,defs"; do
+ CC_CHECK_LDFLAGS([$possible_flags], [LDFLAGS_NOUNDEFINED="$possible_flags"])
+- break
++ if test "x$LDFLAGS_NOUNDEFINED" = "x"; then break; fi
+ done
+ ;;
+ esac
+@@ -147,7 +154,7 @@ AC_DEFUN([CC_CHECK_ATTRIBUTE], [
+ AS_TR_SH([cc_cv_attribute_$1]),
+ [ac_save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $cc_cv_werror"
+- AC_COMPILE_IFELSE([$3],
++ AC_COMPILE_IFELSE([AC_LANG_SOURCE([$3])],
+ [eval "AS_TR_SH([cc_cv_attribute_$1])='yes'"],
+ [eval "AS_TR_SH([cc_cv_attribute_$1])='no'"])
+ CFLAGS="$ac_save_CFLAGS"
+@@ -257,7 +264,7 @@ AC_DEFUN([CC_FLAG_VISIBILITY], [
+ cc_cv_flag_visibility='yes',
+ cc_cv_flag_visibility='no')
+ CFLAGS="$cc_flag_visibility_save_CFLAGS"])
+-
++
+ AS_IF([test "x$cc_cv_flag_visibility" = "xyes"],
+ [AC_DEFINE([SUPPORT_FLAG_VISIBILITY], 1,
+ [Define this if the compiler supports the -fvisibility flag])
+@@ -295,11 +302,11 @@ AC_DEFUN([CC_ATTRIBUTE_ALIGNED], [
+ [ac_save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $cc_cv_werror"
+ for cc_attribute_align_try in 64 32 16 8 4 2; do
+- AC_COMPILE_IFELSE([
++ AC_COMPILE_IFELSE([AC_LANG_SOURCE([
+ int main() {
+ static char c __attribute__ ((aligned($cc_attribute_align_try))) = 0;
+ return c;
+- }], [cc_cv_attribute_aligned=$cc_attribute_align_try; break])
++ }])], [cc_cv_attribute_aligned=$cc_attribute_align_try; break])
+ done
+ CFLAGS="$ac_save_CFLAGS"
+ ])
+--
+2.16.4
+
diff --git a/0033-topology-avoid-to-use-the-atoi-directly-when-expecte.patch b/0033-topology-avoid-to-use-the-atoi-directly-when-expecte.patch
new file mode 100644
index 0000000..493513b
--- /dev/null
+++ b/0033-topology-avoid-to-use-the-atoi-directly-when-expecte.patch
@@ -0,0 +1,763 @@
+From f373bf1f6eea0f2037a7714e9b55aa65fa00b889 Mon Sep 17 00:00:00 2001
+From: Jaroslav Kysela
+Date: Sat, 14 Dec 2019 13:36:09 +0100
+Subject: [PATCH 33/63] topology: avoid to use the atoi() directly when
+ expected
+
+Signed-off-by: Jaroslav Kysela
+---
+ src/topology/channel.c | 10 ++--
+ src/topology/ctl.c | 37 ++++++------
+ src/topology/dapm.c | 32 +++++-----
+ src/topology/data.c | 14 ++---
+ src/topology/elem.c | 9 ++-
+ src/topology/ops.c | 2 +-
+ src/topology/parser.c | 29 +++++++++
+ src/topology/pcm.c | 148 ++++++++++++++++++++--------------------------
+ src/topology/tplg_local.h | 2 +
+ 9 files changed, 150 insertions(+), 133 deletions(-)
+
+diff --git a/src/topology/channel.c b/src/topology/channel.c
+index 60f7e219a674..4569eb31e0c5 100644
+--- a/src/topology/channel.c
++++ b/src/topology/channel.c
+@@ -80,8 +80,8 @@ int tplg_parse_channel(snd_tplg_t *tplg, snd_config_t *cfg,
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ struct snd_soc_tplg_channel *channel = private;
+- const char *id, *value;
+- int channel_id;
++ const char *id;
++ int channel_id, value;
+
+ if (tplg->channel_idx >= SND_SOC_TPLG_MAX_CHAN)
+ return -EINVAL;
+@@ -109,13 +109,13 @@ int tplg_parse_channel(snd_tplg_t *tplg, snd_config_t *cfg,
+ continue;
+
+ /* get value */
+- if (snd_config_get_string(n, &value) < 0)
++ if (tplg_get_integer(n, &value, 0) < 0)
+ continue;
+
+ if (strcmp(id, "reg") == 0)
+- channel->reg = atoi(value);
++ channel->reg = value;
+ else if (strcmp(id, "shift") == 0)
+- channel->shift = atoi(value);
++ channel->shift = value;
+
+ tplg_dbg("\t\t%s = %s\n", id, value);
+ }
+diff --git a/src/topology/ctl.c b/src/topology/ctl.c
+index 1db0b16eac95..e1896f46c576 100644
+--- a/src/topology/ctl.c
++++ b/src/topology/ctl.c
+@@ -286,7 +286,8 @@ static int tplg_parse_tlv_dbscale(snd_config_t *cfg, struct tplg_elem *elem)
+ snd_config_t *n;
+ struct snd_soc_tplg_ctl_tlv *tplg_tlv;
+ struct snd_soc_tplg_tlv_dbscale *scale;
+- const char *id = NULL, *value = NULL;
++ const char *id = NULL;
++ int val;
+
+ tplg_dbg(" scale: %s\n", elem->id);
+
+@@ -310,18 +311,18 @@ static int tplg_parse_tlv_dbscale(snd_config_t *cfg, struct tplg_elem *elem)
+ }
+
+ /* get value */
+- if (snd_config_get_string(n, &value) < 0)
++ if (tplg_get_integer(n, &val, 0))
+ continue;
+
+- tplg_dbg("\t%s = %s\n", id, value);
++ tplg_dbg("\t%s = %i\n", id, val);
+
+ /* get TLV data */
+ if (strcmp(id, "min") == 0)
+- scale->min = atoi(value);
++ scale->min = val;
+ else if (strcmp(id, "step") == 0)
+- scale->step = atoi(value);
++ scale->step = val;
+ else if (strcmp(id, "mute") == 0)
+- scale->mute = atoi(value);
++ scale->mute = val;
+ else
+ SNDERR("error: unknown key %s\n", id);
+ }
+@@ -372,7 +373,7 @@ int tplg_parse_control_bytes(snd_tplg_t *tplg,
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id, *val = NULL;
+- int err;
++ int err, ival;
+ bool access_set = false, tlv_set = false;
+
+ elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_BYTES);
+@@ -398,37 +399,37 @@ int tplg_parse_control_bytes(snd_tplg_t *tplg,
+ continue;
+
+ if (strcmp(id, "base") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ if (tplg_get_integer(n, &ival, 0))
+ return -EINVAL;
+
+- be->base = atoi(val);
++ be->base = ival;
+ tplg_dbg("\t%s: %d\n", id, be->base);
+ continue;
+ }
+
+ if (strcmp(id, "num_regs") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ if (tplg_get_integer(n, &ival, 0))
+ return -EINVAL;
+
+- be->num_regs = atoi(val);
++ be->num_regs = ival;
+ tplg_dbg("\t%s: %d\n", id, be->num_regs);
+ continue;
+ }
+
+ if (strcmp(id, "max") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ if (tplg_get_integer(n, &ival, 0))
+ return -EINVAL;
+
+- be->max = atoi(val);
++ be->max = ival;
+ tplg_dbg("\t%s: %d\n", id, be->max);
+ continue;
+ }
+
+ if (strcmp(id, "mask") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ if (tplg_get_integer(n, &ival, 16))
+ return -EINVAL;
+
+- be->mask = strtol(val, NULL, 16);
++ be->mask = ival;
+ tplg_dbg("\t%s: %d\n", id, be->mask);
+ continue;
+ }
+@@ -598,7 +599,7 @@ int tplg_parse_control_mixer(snd_tplg_t *tplg,
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id, *val = NULL;
+- int err, j;
++ int err, j, ival;
+ bool access_set = false, tlv_set = false;
+
+ elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_MIXER);
+@@ -647,10 +648,10 @@ int tplg_parse_control_mixer(snd_tplg_t *tplg,
+ }
+
+ if (strcmp(id, "max") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ if (tplg_get_integer(n, &ival, 0))
+ return -EINVAL;
+
+- mc->max = atoi(val);
++ mc->max = ival;
+ tplg_dbg("\t%s: %d\n", id, mc->max);
+ continue;
+ }
+diff --git a/src/topology/dapm.c b/src/topology/dapm.c
+index ce46913192df..c6fd793d0d72 100644
+--- a/src/topology/dapm.c
++++ b/src/topology/dapm.c
+@@ -426,7 +426,7 @@ int tplg_parse_dapm_graph(snd_tplg_t *tplg, snd_config_t *cfg,
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ int err;
+- const char *graph_id, *val = NULL;
++ const char *graph_id;
+ int index = -1;
+
+ if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
+@@ -445,9 +445,10 @@ int tplg_parse_dapm_graph(snd_tplg_t *tplg, snd_config_t *cfg,
+ }
+
+ if (strcmp(id, "index") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ if (tplg_get_integer(n, &index, 0))
++ return -EINVAL;
++ if (index < 0)
+ return -EINVAL;
+- index = atoi(val);
+ }
+
+ if (strcmp(id, "lines") == 0) {
+@@ -479,6 +480,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg,
+ snd_config_t *n;
+ const char *id, *val = NULL;
+ int widget_type, err;
++ int ival;
+
+ elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_DAPM_WIDGET);
+ if (!elem)
+@@ -540,55 +542,55 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg,
+ }
+
+ if (strcmp(id, "shift") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ if (tplg_get_integer(n, &ival, 0))
+ return -EINVAL;
+
+- widget->shift = atoi(val);
++ widget->shift = ival;
+ tplg_dbg("\t%s: %d\n", id, widget->shift);
+ continue;
+ }
+
+ if (strcmp(id, "reg") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ if (tplg_get_integer(n, &ival, 0))
+ return -EINVAL;
+
+- widget->reg = atoi(val);
++ widget->reg = ival;
+ tplg_dbg("\t%s: %d\n", id, widget->reg);
+ continue;
+ }
+
+ if (strcmp(id, "invert") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ if (tplg_get_integer(n, &ival, 0))
+ return -EINVAL;
+
+- widget->invert = atoi(val);
++ widget->invert = ival;
+ tplg_dbg("\t%s: %d\n", id, widget->invert);
+ continue;
+ }
+
+ if (strcmp(id, "subseq") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ if (tplg_get_integer(n, &ival, 0))
+ return -EINVAL;
+
+- widget->subseq= atoi(val);
++ widget->subseq = ival;
+ tplg_dbg("\t%s: %d\n", id, widget->subseq);
+ continue;
+ }
+
+ if (strcmp(id, "event_type") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ if (tplg_get_integer(n, &ival, 0))
+ return -EINVAL;
+
+- widget->event_type = atoi(val);
++ widget->event_type = ival;
+ tplg_dbg("\t%s: %d\n", id, widget->event_type);
+ continue;
+ }
+
+ if (strcmp(id, "event_flags") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ if (tplg_get_integer(n, &ival, 0))
+ return -EINVAL;
+
+- widget->event_flags = atoi(val);
++ widget->event_flags = ival;
+ tplg_dbg("\t%s: %d\n", id, widget->event_flags);
+ continue;
+ }
+diff --git a/src/topology/data.c b/src/topology/data.c
+index 729ce1f4c0b6..0edfe54f3383 100644
+--- a/src/topology/data.c
++++ b/src/topology/data.c
+@@ -800,10 +800,10 @@ int tplg_parse_tokens(snd_tplg_t *tplg, snd_config_t *cfg,
+ {
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+- const char *id, *value;
++ const char *id;
+ struct tplg_elem *elem;
+ struct tplg_vendor_tokens *tokens;
+- int num_tokens = 0;
++ int num_tokens = 0, value;
+
+ elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_TOKEN);
+ if (!elem)
+@@ -830,12 +830,12 @@ int tplg_parse_tokens(snd_tplg_t *tplg, snd_config_t *cfg,
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+- if (snd_config_get_string(n, &value) < 0)
++ if (tplg_get_integer(n, &value, 0))
+ continue;
+
+ snd_strlcpy(tokens->token[tokens->num_tokens].id, id,
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+- tokens->token[tokens->num_tokens].value = atoi(value);
++ tokens->token[tokens->num_tokens].value = value;
+ tplg_dbg("\t\t %s : %d\n", tokens->token[tokens->num_tokens].id,
+ tokens->token[tokens->num_tokens].value);
+ tokens->num_tokens++;
+@@ -1013,7 +1013,7 @@ int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg,
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id, *val = NULL;
+- int err = 0;
++ int err = 0, ival;
+ struct tplg_elem *elem;
+
+ elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_DATA);
+@@ -1071,10 +1071,10 @@ int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg,
+ }
+
+ if (strcmp(id, "type") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ if (tplg_get_integer(n, &ival, 0))
+ return -EINVAL;
+
+- elem->vendor_type = atoi(val);
++ elem->vendor_type = ival;
+ tplg_dbg("\t%s: %d\n", id, elem->index);
+ continue;
+ }
+diff --git a/src/topology/elem.c b/src/topology/elem.c
+index d8618cc9d983..f2076f7958aa 100644
+--- a/src/topology/elem.c
++++ b/src/topology/elem.c
+@@ -153,7 +153,7 @@ struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg,
+ enum snd_tplg_type type)
+ {
+ struct tplg_elem *elem;
+- const char *id, *val = NULL;
++ const char *id;
+ int obj_size = 0;
+ void *obj;
+ snd_config_iterator_t i, next;
+@@ -178,11 +178,14 @@ struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg,
+ if (snd_config_get_id(n, &id))
+ continue;
+ if (strcmp(id, "index") == 0) {
+- if (snd_config_get_string(n, &val) < 0) {
++ if (tplg_get_integer(n, &elem->index, 0)) {
++ free(elem);
++ return NULL;
++ }
++ if (elem->index < 0) {
+ free(elem);
+ return NULL;
+ }
+- elem->index = atoi(val);
+ }
+ }
+ } else if (name != NULL)
+diff --git a/src/topology/ops.c b/src/topology/ops.c
+index 0f4295a2e50c..073acdcb1453 100644
+--- a/src/topology/ops.c
++++ b/src/topology/ops.c
+@@ -42,7 +42,7 @@ static int lookup_ops(const char *c)
+ }
+
+ /* cant find string name in our table so we use its ID number */
+- return atoi(c);
++ return strtol(c, NULL, 0);
+ }
+
+ /* Parse Control operations. Ops can come from standard names above or
+diff --git a/src/topology/parser.c b/src/topology/parser.c
+index 5940692da2e2..7e657809307d 100644
+--- a/src/topology/parser.c
++++ b/src/topology/parser.c
+@@ -21,6 +21,35 @@
+ #include "list.h"
+ #include "tplg_local.h"
+
++/*
++ * Get integer value
++ */
++int tplg_get_integer(snd_config_t *n, int *val, int base)
++{
++ const char *str;
++ long lval;
++ int err;
++
++ switch (snd_config_get_type(n)) {
++ case SND_CONFIG_TYPE_INTEGER:
++ err = snd_config_get_integer(n, &lval);
++ if (err < 0)
++ return err;
++ if (lval < INT_MIN || lval > INT_MAX)
++ return -EINVAL;
++ *val = lval;
++ return err;
++ case SND_CONFIG_TYPE_STRING:
++ err = snd_config_get_string(n, &str);
++ if (err < 0)
++ return err;
++ *val = strtol(str, NULL, base);
++ return 0;
++ default:
++ return -EINVAL;
++ }
++}
++
+ /*
+ * Parse compound
+ */
+diff --git a/src/topology/pcm.c b/src/topology/pcm.c
+index 98a8df979e24..d6c52b4752ee 100644
+--- a/src/topology/pcm.c
++++ b/src/topology/pcm.c
+@@ -368,6 +368,24 @@ static int split_rate(struct snd_soc_tplg_stream_caps *caps, char *str)
+ return 0;
+ }
+
++static int parse_unsigned(snd_config_t *n, unsigned int *dst)
++{
++ int ival;
++
++ if (tplg_get_integer(n, &ival, 0) < 0)
++ return -EINVAL;
++
++ *dst = ival;
++#if TPLG_DEBUG
++ {
++ const char *id;
++ if (snd_config_get_id(n, &id) >= 0)
++ tplg_dbg("\t\t%s: %d\n", id, *dst);
++ }
++#endif
++ return 0;
++}
++
+ /* Parse pcm stream capabilities */
+ int tplg_parse_stream_caps(snd_tplg_t *tplg,
+ snd_config_t *cfg,
+@@ -402,10 +420,10 @@ int tplg_parse_stream_caps(snd_tplg_t *tplg,
+ if (id[0] == '#')
+ continue;
+
+- if (snd_config_get_string(n, &val) < 0)
+- return -EINVAL;
+-
+ if (strcmp(id, "formats") == 0) {
++ if (snd_config_get_string(n, &val) < 0)
++ return -EINVAL;
++
+ s = strdup(val);
+ if (s == NULL)
+ return -ENOMEM;
+@@ -421,6 +439,9 @@ int tplg_parse_stream_caps(snd_tplg_t *tplg,
+ }
+
+ if (strcmp(id, "rates") == 0) {
++ if (snd_config_get_string(n, &val) < 0)
++ return -EINVAL;
++
+ s = strdup(val);
+ if (!s)
+ return -ENOMEM;
+@@ -436,68 +457,68 @@ int tplg_parse_stream_caps(snd_tplg_t *tplg,
+ }
+
+ if (strcmp(id, "rate_min") == 0) {
+- sc->rate_min = atoi(val);
+- tplg_dbg("\t\t%s: %d\n", id, sc->rate_min);
++ if (parse_unsigned(n, &sc->rate_min))
++ return -EINVAL;
+ continue;
+ }
+
+ if (strcmp(id, "rate_max") == 0) {
+- sc->rate_max = atoi(val);
+- tplg_dbg("\t\t%s: %d\n", id, sc->rate_max);
++ if (parse_unsigned(n, &sc->rate_max))
++ return -EINVAL;
+ continue;
+ }
+
+ if (strcmp(id, "channels_min") == 0) {
+- sc->channels_min = atoi(val);
+- tplg_dbg("\t\t%s: %d\n", id, sc->channels_min);
++ if (parse_unsigned(n, &sc->channels_min))
++ return -EINVAL;
+ continue;
+ }
+
+ if (strcmp(id, "channels_max") == 0) {
+- sc->channels_max = atoi(val);
+- tplg_dbg("\t\t%s: %d\n", id, sc->channels_max);
++ if (parse_unsigned(n, &sc->channels_max))
++ return -EINVAL;
+ continue;
+ }
+
+ if (strcmp(id, "periods_min") == 0) {
+- sc->periods_min = atoi(val);
+- tplg_dbg("\t\t%s: %d\n", id, sc->periods_min);
++ if (parse_unsigned(n, &sc->periods_min))
++ return -EINVAL;
+ continue;
+ }
+
+ if (strcmp(id, "periods_max") == 0) {
+- sc->periods_max = atoi(val);
+- tplg_dbg("\t\t%s: %d\n", id, sc->periods_max);
++ if (parse_unsigned(n, &sc->periods_max))
++ return -EINVAL;
+ continue;
+ }
+
+ if (strcmp(id, "period_size_min") == 0) {
+- sc->period_size_min = atoi(val);
+- tplg_dbg("\t\t%s: %d\n", id, sc->period_size_min);
++ if (parse_unsigned(n, &sc->period_size_min))
++ return -EINVAL;
+ continue;
+ }
+
+ if (strcmp(id, "period_size_max") == 0) {
+- sc->period_size_max = atoi(val);
+- tplg_dbg("\t\t%s: %d\n", id, sc->period_size_max);
++ if (parse_unsigned(n, &sc->period_size_max))
++ return -EINVAL;
+ continue;
+ }
+
+ if (strcmp(id, "buffer_size_min") == 0) {
+- sc->buffer_size_min = atoi(val);
+- tplg_dbg("\t\t%s: %d\n", id, sc->buffer_size_min);
++ if (parse_unsigned(n, &sc->buffer_size_min))
++ return -EINVAL;
+ continue;
+ }
+
+ if (strcmp(id, "buffer_size_max") == 0) {
+- sc->buffer_size_max = atoi(val);
+- tplg_dbg("\t\t%s: %d\n", id, sc->buffer_size_max);
++ if (parse_unsigned(n, &sc->buffer_size_max))
++ return -EINVAL;
+ continue;
+ }
+
+ if (strcmp(id, "sig_bits") == 0) {
+- sc->sig_bits = atoi(val);
+- tplg_dbg("\t\t%s: %d\n", id, sc->sig_bits);
++ if (parse_unsigned(n, &sc->sig_bits))
++ return -EINVAL;
+ continue;
+ }
+
+@@ -674,11 +695,8 @@ int tplg_parse_pcm(snd_tplg_t *tplg, snd_config_t *cfg,
+ continue;
+
+ if (strcmp(id, "id") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ if (parse_unsigned(n, &pcm->pcm_id))
+ return -EINVAL;
+-
+- pcm->pcm_id = atoi(val);
+- tplg_dbg("\t%s: %d\n", id, pcm->pcm_id);
+ continue;
+ }
+
+@@ -784,30 +802,21 @@ int tplg_parse_dai(snd_tplg_t *tplg, snd_config_t *cfg,
+ continue;
+
+ if (strcmp(id, "id") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ if (parse_unsigned(n, &dai->dai_id))
+ return -EINVAL;
+-
+- dai->dai_id = atoi(val);
+- tplg_dbg("\t%s: %d\n", id, dai->dai_id);
+ continue;
+ }
+
+ if (strcmp(id, "playback") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ if (parse_unsigned(n, &dai->playback))
+ return -EINVAL;
+-
+- dai->playback = atoi(val);
+- tplg_dbg("\t%s: %d\n", id, dai->playback);
+ continue;
+ }
+
+
+ if (strcmp(id, "capture") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ if (parse_unsigned(n, &dai->capture))
+ return -EINVAL;
+-
+- dai->capture = atoi(val);
+- tplg_dbg("\t%s: %d\n", id, dai->capture);
+ continue;
+ }
+
+@@ -949,11 +958,8 @@ int tplg_parse_link(snd_tplg_t *tplg,
+ continue;
+
+ if (strcmp(id, "id") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ if (parse_unsigned(n, &link->id))
+ return -EINVAL;
+-
+- link->id = atoi(val);
+- tplg_dbg("\t%s: %d\n", id, link->id);
+ continue;
+ }
+
+@@ -975,10 +981,8 @@ int tplg_parse_link(snd_tplg_t *tplg,
+ }
+
+ if (strcmp(id, "default_hw_conf_id") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ if (parse_unsigned(n, &link->default_hw_config_id))
+ return -EINVAL;
+-
+- link->default_hw_config_id = atoi(val);
+ continue;
+ }
+
+@@ -1030,7 +1034,7 @@ int tplg_parse_cc(snd_tplg_t *tplg,
+ struct tplg_elem *elem;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+- const char *id, *val = NULL;
++ const char *id;
+
+ elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_CC);
+ if (!elem)
+@@ -1054,11 +1058,8 @@ int tplg_parse_cc(snd_tplg_t *tplg,
+ continue;
+
+ if (strcmp(id, "id") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ if (parse_unsigned(n, &link->id))
+ return -EINVAL;
+-
+- link->id = atoi(val);
+- tplg_dbg("\t%s: %d\n", id, link->id);
+ continue;
+ }
+
+@@ -1130,11 +1131,8 @@ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg,
+ continue;
+
+ if (strcmp(id, "id") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ if (parse_unsigned(n, &hw_cfg->id))
+ return -EINVAL;
+-
+- hw_cfg->id = atoi(val);
+- tplg_dbg("\t%s: %d\n", id, hw_cfg->id);
+ continue;
+ }
+
+@@ -1173,10 +1171,8 @@ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg,
+
+ if (strcmp(id, "bclk_freq") == 0 ||
+ strcmp(id, "bclk_rate") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ if (parse_unsigned(n, &hw_cfg->bclk_rate))
+ return -EINVAL;
+-
+- hw_cfg->bclk_rate = atoi(val);
+ continue;
+ }
+
+@@ -1223,19 +1219,15 @@ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg,
+
+ if (strcmp(id, "fsync_freq") == 0 ||
+ strcmp(id, "fsync_rate") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ if (parse_unsigned(n, &hw_cfg->fsync_rate))
+ return -EINVAL;
+-
+- hw_cfg->fsync_rate = atoi(val);
+ continue;
+ }
+
+ if (strcmp(id, "mclk_freq") == 0 ||
+ strcmp(id, "mclk_rate") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ if (parse_unsigned(n, &hw_cfg->mclk_rate))
+ return -EINVAL;
+-
+- hw_cfg->mclk_rate = atoi(val);
+ continue;
+ }
+
+@@ -1275,50 +1267,38 @@ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg,
+ }
+
+ if (strcmp(id, "tdm_slots") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ if (parse_unsigned(n, &hw_cfg->tdm_slots))
+ return -EINVAL;
+-
+- hw_cfg->tdm_slots = atoi(val);
+ continue;
+ }
+
+ if (strcmp(id, "tdm_slot_width") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ if (parse_unsigned(n, &hw_cfg->tdm_slot_width))
+ return -EINVAL;
+-
+- hw_cfg->tdm_slot_width = atoi(val);
+ continue;
+ }
+
+ if (strcmp(id, "tx_slots") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ if (parse_unsigned(n, &hw_cfg->tx_slots))
+ return -EINVAL;
+-
+- hw_cfg->tx_slots = atoi(val);
+ continue;
+ }
+
+ if (strcmp(id, "rx_slots") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ if (parse_unsigned(n, &hw_cfg->rx_slots))
+ return -EINVAL;
+-
+- hw_cfg->rx_slots = atoi(val);
+ continue;
+ }
+
+ if (strcmp(id, "tx_channels") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ if (parse_unsigned(n, &hw_cfg->tx_channels))
+ return -EINVAL;
+-
+- hw_cfg->tx_channels = atoi(val);
+ continue;
+ }
+
+ if (strcmp(id, "rx_channels") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ if (parse_unsigned(n, &hw_cfg->rx_channels))
+ return -EINVAL;
+-
+- hw_cfg->rx_channels = atoi(val);
+ continue;
+ }
+
+diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h
+index 101491a42e60..991e0b4121bd 100644
+--- a/src/topology/tplg_local.h
++++ b/src/topology/tplg_local.h
+@@ -280,6 +280,8 @@ struct tplg_elem *tplg_elem_lookup(struct list_head *base,
+ struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg,
+ snd_config_t *cfg, const char *name, enum snd_tplg_type type);
+
++int tplg_get_integer(snd_config_t *n, int *val, int base);
++
+ int tplg_parse_channel(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ snd_config_t *cfg, void *private);
+
+--
+2.16.4
+
diff --git a/0034-topology-use-snd_config_get_bool-instead-own-impleme.patch b/0034-topology-use-snd_config_get_bool-instead-own-impleme.patch
new file mode 100644
index 0000000..fe43544
--- /dev/null
+++ b/0034-topology-use-snd_config_get_bool-instead-own-impleme.patch
@@ -0,0 +1,207 @@
+From 5925a6d870331c631f85ed4e18a8c5e6459b3c36 Mon Sep 17 00:00:00 2001
+From: Jaroslav Kysela
+Date: Sat, 14 Dec 2019 13:50:04 +0100
+Subject: [PATCH 34/63] topology: use snd_config_get_bool() instead own
+ implementation
+
+Signed-off-by: Jaroslav Kysela
+---
+ src/topology/ctl.c | 9 +++------
+ src/topology/dapm.c | 9 ++++-----
+ src/topology/data.c | 15 ++++++++++++---
+ src/topology/pcm.c | 29 +++++++++++++++--------------
+ 4 files changed, 34 insertions(+), 28 deletions(-)
+
+diff --git a/src/topology/ctl.c b/src/topology/ctl.c
+index e1896f46c576..9190efefb575 100644
+--- a/src/topology/ctl.c
++++ b/src/topology/ctl.c
+@@ -657,13 +657,10 @@ int tplg_parse_control_mixer(snd_tplg_t *tplg,
+ }
+
+ if (strcmp(id, "invert") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ ival = snd_config_get_bool(n);
++ if (ival < 0)
+ return -EINVAL;
+-
+- if (strcmp(val, "true") == 0)
+- mc->invert = 1;
+- else if (strcmp(val, "false") == 0)
+- mc->invert = 0;
++ mc->invert = ival;
+
+ tplg_dbg("\t%s: %d\n", id, mc->invert);
+ continue;
+diff --git a/src/topology/dapm.c b/src/topology/dapm.c
+index c6fd793d0d72..ad7092107896 100644
+--- a/src/topology/dapm.c
++++ b/src/topology/dapm.c
+@@ -479,8 +479,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg,
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id, *val = NULL;
+- int widget_type, err;
+- int ival;
++ int widget_type, err, ival;
+
+ elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_DAPM_WIDGET);
+ if (!elem)
+@@ -531,11 +530,11 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg,
+ }
+
+ if (strcmp(id, "no_pm") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ ival = snd_config_get_bool(n);
++ if (ival < 0)
+ return -EINVAL;
+
+- if (strcmp(val, "true") == 0)
+- widget->reg = -1;
++ widget->reg = ival ? -1 : 0;
+
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+diff --git a/src/topology/data.c b/src/topology/data.c
+index 0edfe54f3383..6b1337b39bad 100644
+--- a/src/topology/data.c
++++ b/src/topology/data.c
+@@ -557,6 +557,7 @@ static int parse_tuple_set(snd_config_t *cfg,
+ unsigned int type, num_tuples = 0;
+ struct tplg_tuple *tuple;
+ unsigned long int tuple_val;
++ int ival;
+
+ snd_config_get_id(cfg, &id);
+
+@@ -607,25 +608,33 @@ static int parse_tuple_set(snd_config_t *cfg,
+
+ switch (type) {
+ case SND_SOC_TPLG_TUPLE_TYPE_UUID:
++ if (snd_config_get_string(n, &value) < 0)
++ continue;
+ if (get_uuid(value, tuple->uuid) < 0)
+ goto err;
+ break;
+
+ case SND_SOC_TPLG_TUPLE_TYPE_STRING:
++ if (snd_config_get_string(n, &value) < 0)
++ continue;
+ snd_strlcpy(tuple->string, value,
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+ tplg_dbg("\t\t%s = %s\n", tuple->token, tuple->string);
+ break;
+
+ case SND_SOC_TPLG_TUPLE_TYPE_BOOL:
+- if (strcmp(value, "true") == 0)
+- tuple->value = 1;
++ ival = snd_config_get_bool(n);
++ if (ival < 0)
++ continue;
++ tuple->value = ival;
+ tplg_dbg("\t\t%s = %d\n", tuple->token, tuple->value);
+ break;
+
+ case SND_SOC_TPLG_TUPLE_TYPE_BYTE:
+ case SND_SOC_TPLG_TUPLE_TYPE_SHORT:
+ case SND_SOC_TPLG_TUPLE_TYPE_WORD:
++ if (snd_config_get_string(n, &value) < 0)
++ continue;
+ errno = 0;
+ /* no support for negative value */
+ tuple_val = strtoul(value, NULL, 0);
+@@ -1012,7 +1021,7 @@ int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg,
+ {
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+- const char *id, *val = NULL;
++ const char *id;
+ int err = 0, ival;
+ struct tplg_elem *elem;
+
+diff --git a/src/topology/pcm.c b/src/topology/pcm.c
+index d6c52b4752ee..6364e24f3c43 100644
+--- a/src/topology/pcm.c
++++ b/src/topology/pcm.c
+@@ -669,8 +669,8 @@ int tplg_parse_pcm(snd_tplg_t *tplg, snd_config_t *cfg,
+ struct tplg_elem *elem;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+- const char *id, *val = NULL;
+- int err;
++ const char *id;
++ int err, ival;
+
+ elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_PCM);
+ if (!elem)
+@@ -709,11 +709,11 @@ int tplg_parse_pcm(snd_tplg_t *tplg, snd_config_t *cfg,
+ }
+
+ if (strcmp(id, "compress") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ ival = snd_config_get_bool(n);
++ if (ival < 0)
+ return -EINVAL;
+
+- if (strcmp(val, "true") == 0)
+- pcm->compress = 1;
++ pcm->compress = ival;
+
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+@@ -1107,7 +1107,7 @@ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg,
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id, *val = NULL;
+- int ret;
++ int ret, ival;
+
+ elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_HW_CONFIG);
+ if (!elem)
+@@ -1178,11 +1178,11 @@ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg,
+
+ if (strcmp(id, "bclk_invert") == 0 ||
+ strcmp(id, "invert_bclk") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ ival = snd_config_get_bool(n);
++ if (ival < 0)
+ return -EINVAL;
+
+- if (!strcmp(val, "true"))
+- hw_cfg->invert_bclk = true;
++ hw_cfg->invert_bclk = ival;
+ continue;
+ }
+
+@@ -1209,11 +1209,11 @@ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg,
+
+ if (strcmp(id, "fsync_invert") == 0 ||
+ strcmp(id, "invert_fsync") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ ival = snd_config_get_bool(n);
++ if (ival < 0)
+ return -EINVAL;
+
+- if (!strcmp(val, "true"))
+- hw_cfg->invert_fsync = true;
++ hw_cfg->invert_fsync = ival;
+ continue;
+ }
+
+@@ -1254,10 +1254,11 @@ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg,
+
+ if (strcmp(id, "pm_gate_clocks") == 0 ||
+ strcmp(id, "clock_gated") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
++ ival = snd_config_get_bool(n);
++ if (ival < 0)
+ return -EINVAL;
+
+- if (!strcmp(val, "true"))
++ if (ival)
+ hw_cfg->clock_gated =
+ SND_SOC_TPLG_DAI_CLK_GATE_GATED;
+ else
+--
+2.16.4
+
diff --git a/0035-topology-fix-tplg_get_integer-handle-errno-ERANGE.patch b/0035-topology-fix-tplg_get_integer-handle-errno-ERANGE.patch
new file mode 100644
index 0000000..a41fe67
--- /dev/null
+++ b/0035-topology-fix-tplg_get_integer-handle-errno-ERANGE.patch
@@ -0,0 +1,39 @@
+From 1047a5f3c0d39a3b0579db027f52d7facdf44077 Mon Sep 17 00:00:00 2001
+From: Jaroslav Kysela
+Date: Sat, 14 Dec 2019 13:52:18 +0100
+Subject: [PATCH 35/63] topology: fix tplg_get_integer() - handle errno ==
+ ERANGE
+
+Signed-off-by: Jaroslav Kysela
+---
+ src/topology/parser.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/src/topology/parser.c b/src/topology/parser.c
+index 7e657809307d..667c8d45517b 100644
+--- a/src/topology/parser.c
++++ b/src/topology/parser.c
+@@ -36,14 +36,19 @@ int tplg_get_integer(snd_config_t *n, int *val, int base)
+ if (err < 0)
+ return err;
+ if (lval < INT_MIN || lval > INT_MAX)
+- return -EINVAL;
++ return -ERANGE;
+ *val = lval;
+ return err;
+ case SND_CONFIG_TYPE_STRING:
+ err = snd_config_get_string(n, &str);
+ if (err < 0)
+ return err;
++ errno = 0;
+ *val = strtol(str, NULL, base);
++ if (errno == ERANGE)
++ return -ERANGE;
++ if (errno && *val == 0)
++ return -EINVAL;
+ return 0;
+ default:
+ return -EINVAL;
+--
+2.16.4
+
diff --git a/0036-topology-add-tplg_get_unsigned-function.patch b/0036-topology-add-tplg_get_unsigned-function.patch
new file mode 100644
index 0000000..6703729
--- /dev/null
+++ b/0036-topology-add-tplg_get_unsigned-function.patch
@@ -0,0 +1,172 @@
+From 14e43a11873d14ec6f16967c83629237ef44ac38 Mon Sep 17 00:00:00 2001
+From: Jaroslav Kysela
+Date: Sat, 14 Dec 2019 14:05:49 +0100
+Subject: [PATCH 36/63] topology: add tplg_get_unsigned() function
+
+Signed-off-by: Jaroslav Kysela
+---
+ src/topology/data.c | 19 +++++--------------
+ src/topology/parser.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++
+ src/topology/pcm.c | 13 ++-----------
+ src/topology/tplg_local.h | 1 +
+ 4 files changed, 55 insertions(+), 25 deletions(-)
+
+diff --git a/src/topology/data.c b/src/topology/data.c
+index 6b1337b39bad..9807445e8c37 100644
+--- a/src/topology/data.c
++++ b/src/topology/data.c
+@@ -556,7 +556,7 @@ static int parse_tuple_set(snd_config_t *cfg,
+ struct tplg_tuple_set *set;
+ unsigned int type, num_tuples = 0;
+ struct tplg_tuple *tuple;
+- unsigned long int tuple_val;
++ unsigned int tuple_val;
+ int ival;
+
+ snd_config_get_id(cfg, &id);
+@@ -598,10 +598,6 @@ static int parse_tuple_set(snd_config_t *cfg,
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+- /* get value */
+- if (snd_config_get_string(n, &value) < 0)
+- continue;
+-
+ tuple = &set->tuple[set->num_tuples];
+ snd_strlcpy(tuple->token, id,
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+@@ -633,14 +629,9 @@ static int parse_tuple_set(snd_config_t *cfg,
+ case SND_SOC_TPLG_TUPLE_TYPE_BYTE:
+ case SND_SOC_TPLG_TUPLE_TYPE_SHORT:
+ case SND_SOC_TPLG_TUPLE_TYPE_WORD:
+- if (snd_config_get_string(n, &value) < 0)
+- continue;
+- errno = 0;
+- /* no support for negative value */
+- tuple_val = strtoul(value, NULL, 0);
+- if ((errno == ERANGE && tuple_val == ULONG_MAX)
+- || (errno != 0 && tuple_val == 0)) {
+- SNDERR("error: tuple %s:strtoul fail\n", id);
++ ival = tplg_get_unsigned(n, &tuple_val, 0);
++ if (ival < 0) {
++ SNDERR("error: tuple %s: %s\n", id, snd_strerror(ival));
+ goto err;
+ }
+
+@@ -654,7 +645,7 @@ static int parse_tuple_set(snd_config_t *cfg,
+ goto err;
+ }
+
+- tuple->value = (unsigned int) tuple_val;
++ tuple->value = tuple_val;
+ tplg_dbg("\t\t%s = 0x%x\n", tuple->token, tuple->value);
+ break;
+
+diff --git a/src/topology/parser.c b/src/topology/parser.c
+index 667c8d45517b..f56ad97e42b9 100644
+--- a/src/topology/parser.c
++++ b/src/topology/parser.c
+@@ -55,6 +55,53 @@ int tplg_get_integer(snd_config_t *n, int *val, int base)
+ }
+ }
+
++/*
++ * Get unsigned integer value
++ */
++int tplg_get_unsigned(snd_config_t *n, unsigned *val, int base)
++{
++ const char *str;
++ long lval;
++ long long llval;
++ unsigned long uval;
++ int err;
++
++ switch (snd_config_get_type(n)) {
++ case SND_CONFIG_TYPE_INTEGER:
++ err = snd_config_get_integer(n, &lval);
++ if (err < 0)
++ return err;
++ if (lval < 0 || lval > UINT_MAX)
++ return -ERANGE;
++ *val = lval;
++ return err;
++ case SND_CONFIG_TYPE_INTEGER64:
++ err = snd_config_get_integer64(n, &llval);
++ if (err < 0)
++ return err;
++ if (llval < 0 || llval > UINT_MAX)
++ return -ERANGE;
++ *val = llval;
++ return err;
++ case SND_CONFIG_TYPE_STRING:
++ err = snd_config_get_string(n, &str);
++ if (err < 0)
++ return err;
++ errno = 0;
++ uval = strtoul(str, NULL, base);
++ if (errno == ERANGE && uval == ULONG_MAX)
++ return -ERANGE;
++ if (errno && uval == 0)
++ return -EINVAL;
++ if (uval > UINT_MAX)
++ return -ERANGE;
++ *val = uval;
++ return 0;
++ default:
++ return -EINVAL;
++ }
++}
++
+ /*
+ * Parse compound
+ */
+diff --git a/src/topology/pcm.c b/src/topology/pcm.c
+index 6364e24f3c43..9b87549cabbd 100644
+--- a/src/topology/pcm.c
++++ b/src/topology/pcm.c
+@@ -606,8 +606,7 @@ static int tplg_parse_fe_dai(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ struct snd_soc_tplg_pcm *pcm = elem->pcm;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+- const char *id, *value = NULL;
+- unsigned long int id_val;
++ const char *id;
+
+ snd_config_get_id(cfg, &id);
+ tplg_dbg("\t\tFE DAI %s:\n", id);
+@@ -622,19 +621,11 @@ static int tplg_parse_fe_dai(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ continue;
+
+ if (strcmp(id, "id") == 0) {
+- if (snd_config_get_string(n, &value) < 0)
+- continue;
+- errno = 0;
+- /* no support for negative value */
+- id_val = strtoul(value, NULL, 0);
+- if ((errno == ERANGE && id_val == ULONG_MAX)
+- || (errno != 0 && id_val == 0)
+- || id_val > UINT_MAX) {
++ if (tplg_get_unsigned(n, &pcm->dai_id, 0)) {
+ SNDERR("error: invalid fe dai ID\n");
+ return -EINVAL;
+ }
+
+- pcm->dai_id = (int) id_val;
+ tplg_dbg("\t\t\tindex: %d\n", pcm->dai_id);
+ }
+ }
+diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h
+index 991e0b4121bd..e16c78d49709 100644
+--- a/src/topology/tplg_local.h
++++ b/src/topology/tplg_local.h
+@@ -281,6 +281,7 @@ struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg,
+ snd_config_t *cfg, const char *name, enum snd_tplg_type type);
+
+ int tplg_get_integer(snd_config_t *n, int *val, int base);
++int tplg_get_unsigned(snd_config_t *n, unsigned *val, int base);
+
+ int tplg_parse_channel(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ snd_config_t *cfg, void *private);
+--
+2.16.4
+
diff --git a/0037-topology-convert-builder-to-use-the-mallocated-memor.patch b/0037-topology-convert-builder-to-use-the-mallocated-memor.patch
new file mode 100644
index 0000000..7ee7750
--- /dev/null
+++ b/0037-topology-convert-builder-to-use-the-mallocated-memor.patch
@@ -0,0 +1,296 @@
+From 39fb37fef5bd3b3fa7a63e06a5f0a147197fddb9 Mon Sep 17 00:00:00 2001
+From: Jaroslav Kysela
+Date: Sat, 14 Dec 2019 19:13:53 +0100
+Subject: [PATCH 37/63] topology: convert builder to use the mallocated memory
+
+Signed-off-by: Jaroslav Kysela
+---
+ src/topology/builder.c | 88 +++++++++++++++++++++++++++++++++++++----------
+ src/topology/parser.c | 74 ++++++++++++++++-----------------------
+ src/topology/tplg_local.h | 5 +--
+ 3 files changed, 103 insertions(+), 64 deletions(-)
+
+diff --git a/src/topology/builder.c b/src/topology/builder.c
+index 1a22a453695a..40943b56e4aa 100644
+--- a/src/topology/builder.c
++++ b/src/topology/builder.c
+@@ -29,7 +29,7 @@ static void verbose(snd_tplg_t *tplg, const char *fmt, ...)
+ return;
+
+ va_start(va, fmt);
+- fprintf(stdout, "0x%6.6zx/%6.6zd - ", tplg->out_pos, tplg->out_pos);
++ fprintf(stdout, "0x%6.6zx/%6.6zd - ", tplg->bin_pos, tplg->bin_pos);
+ vfprintf(stdout, fmt, va);
+ va_end(va);
+ }
+@@ -37,18 +37,11 @@ static void verbose(snd_tplg_t *tplg, const char *fmt, ...)
+ /* write a block, track the position */
+ static ssize_t twrite(snd_tplg_t *tplg, void *data, size_t data_size)
+ {
+- ssize_t r = write(tplg->out_fd, data, data_size);
+- if (r != (ssize_t)data_size) {
+- if (r < 0) {
+- SNDERR("error: unable to write: %s", strerror(errno));
+- return -errno;
+- }
+- tplg->out_pos += r;
+- SNDERR("error: unable to write (partial)");
++ if (tplg->bin_pos + data_size > tplg->bin_size)
+ return -EIO;
+- }
+- tplg->out_pos += r;
+- return r;
++ memcpy(tplg->bin + tplg->bin_pos, data, data_size);
++ tplg->bin_pos += data_size;
++ return data_size;
+ }
+
+ /* write out block header to output file */
+@@ -71,12 +64,12 @@ static ssize_t write_block_header(snd_tplg_t *tplg, unsigned int type,
+ hdr.count = count;
+
+ /* make sure file offset is aligned with the calculated HDR offset */
+- if (tplg->out_pos != tplg->next_hdr_pos) {
++ if (tplg->bin_pos != tplg->next_hdr_pos) {
+ SNDERR("error: New header is at offset 0x%zx but file"
+ " offset 0x%zx is %s by %ld bytes\n",
+- tplg->next_hdr_pos, tplg->out_pos,
+- tplg->out_pos > tplg->next_hdr_pos ? "ahead" : "behind",
+- labs(tplg->out_pos - tplg->next_hdr_pos));
++ tplg->next_hdr_pos, tplg->bin_pos,
++ tplg->bin_pos > tplg->next_hdr_pos ? "ahead" : "behind",
++ labs(tplg->bin_pos - tplg->next_hdr_pos));
+ return -EINVAL;
+ }
+
+@@ -163,6 +156,41 @@ static int write_elem_block(snd_tplg_t *tplg,
+ return 0;
+ }
+
++static size_t calc_manifest_size(snd_tplg_t *tplg)
++{
++ return sizeof(struct snd_soc_tplg_hdr) +
++ sizeof(tplg->manifest) +
++ tplg->manifest.priv.size;
++}
++
++static size_t calc_real_size(struct list_head *base)
++{
++ struct list_head *pos;
++ struct tplg_elem *elem, *elem_next;
++ size_t size = 0;
++
++ list_for_each(pos, base) {
++
++ elem = list_entry(pos, struct tplg_elem, list);
++
++ /* compound elems have already been copied to other elems */
++ if (elem->compound_elem)
++ continue;
++
++ if (elem->size <= 0)
++ continue;
++
++ size += elem->size;
++
++ elem_next = list_entry(pos->next, struct tplg_elem, list);
++
++ if ((pos->next == base) || (elem_next->index != elem->index))
++ size += sizeof(struct snd_soc_tplg_hdr);
++ }
++
++ return size;
++}
++
+ static size_t calc_block_size(struct list_head *base)
+ {
+ struct list_head *pos;
+@@ -277,9 +305,27 @@ int tplg_write_data(snd_tplg_t *tplg)
+ };
+
+ ssize_t ret;
+- size_t size;
++ size_t total_size, size;
+ unsigned int index;
+
++ /* calculate total size */
++ total_size = calc_manifest_size(tplg);
++ for (index = 0; index < ARRAY_SIZE(wtable); index++) {
++ wptr = &wtable[index];
++ size = calc_real_size(wptr->list);
++ total_size += size;
++ }
++
++ /* allocate new binary output */
++ free(tplg->bin);
++ tplg->bin = malloc(total_size);
++ tplg->bin_pos = 0;
++ tplg->bin_size = total_size;
++ if (tplg->bin == NULL) {
++ tplg->bin_size = 0;
++ return -ENOMEM;
++ }
++
+ /* write manifest */
+ ret = write_manifest_data(tplg);
+ if (ret < 0) {
+@@ -306,7 +352,13 @@ int tplg_write_data(snd_tplg_t *tplg)
+ }
+ }
+
+- verbose(tplg, "total size is 0x%zx/%zd\n", tplg->out_pos, tplg->out_pos);
++ verbose(tplg, "total size is 0x%zx/%zd\n", tplg->bin_pos, tplg->bin_pos);
++
++ if (total_size != tplg->bin_pos) {
++ SNDERR("total size mismatch (%zd != %zd)\n",
++ total_size, tplg->bin_pos);
++ return -EINVAL;
++ }
+
+ return 0;
+ }
+diff --git a/src/topology/parser.c b/src/topology/parser.c
+index f56ad97e42b9..98a9f9e9deac 100644
+--- a/src/topology/parser.c
++++ b/src/topology/parser.c
+@@ -393,50 +393,29 @@ static int tplg_build_integ(snd_tplg_t *tplg)
+ return err;
+ }
+
+-int snd_tplg_build_file(snd_tplg_t *tplg, const char *infile,
+- const char *outfile)
++int snd_tplg_build_file(snd_tplg_t *tplg,
++ const char *infile,
++ const char *outfile)
+ {
+ snd_config_t *cfg = NULL;
+ int err = 0;
+
+- tplg->out_fd =
+- open(outfile, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+- if (tplg->out_fd < 0) {
+- SNDERR("error: failed to open %s err %d\n",
+- outfile, -errno);
+- return -errno;
+- }
+-
+ err = tplg_load_config(infile, &cfg);
+ if (err < 0) {
+ SNDERR("error: failed to load topology file %s\n",
+ infile);
+- goto out_close;
++ return err;
+ }
+
+ err = tplg_parse_config(tplg, cfg);
+ if (err < 0) {
+ SNDERR("error: failed to parse topology\n");
+- goto out;
+- }
+-
+- err = tplg_build_integ(tplg);
+- if (err < 0) {
+- SNDERR("error: failed to check topology integrity\n");
+- goto out;
+- }
+-
+- err = tplg_write_data(tplg);
+- if (err < 0) {
+- SNDERR("error: failed to write data %d\n", err);
+- goto out;
++ return err;
+ }
+
+-out:
+ snd_config_delete(cfg);
+-out_close:
+- close(tplg->out_fd);
+- return err;
++
++ return snd_tplg_build(tplg, outfile);
+ }
+
+ int snd_tplg_add_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
+@@ -468,31 +447,38 @@ int snd_tplg_add_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
+
+ int snd_tplg_build(snd_tplg_t *tplg, const char *outfile)
+ {
+- int err;
+-
+- tplg->out_fd =
+- open(outfile, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+- if (tplg->out_fd < 0) {
+- SNDERR("error: failed to open %s err %d\n",
+- outfile, -errno);
+- return -errno;
+- }
++ int fd, err;
++ ssize_t r;
+
+ err = tplg_build_integ(tplg);
+ if (err < 0) {
+ SNDERR("error: failed to check topology integrity\n");
+- goto out;
++ return err;
+ }
+
+ err = tplg_write_data(tplg);
+ if (err < 0) {
+ SNDERR("error: failed to write data %d\n", err);
+- goto out;
++ return err;
+ }
+
+-out:
+- close(tplg->out_fd);
+- return err;
++ fd = open(outfile, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
++ if (fd < 0) {
++ SNDERR("error: failed to open %s err %d\n", outfile, -errno);
++ return -errno;
++ }
++ r = write(fd, tplg->bin, tplg->bin_size);
++ close(fd);
++ if (r < 0) {
++ err = -errno;
++ SNDERR("error: write error: %s\n", strerror(errno));
++ return err;
++ }
++ if ((size_t)r != tplg->bin_size) {
++ SNDERR("error: partial write (%zd != %zd)\n", r, tplg->bin_size);
++ return -EIO;
++ }
++ return 0;
+ }
+
+ int snd_tplg_set_manifest_data(snd_tplg_t *tplg, const void *data, int len)
+@@ -571,8 +557,8 @@ snd_tplg_t *snd_tplg_new(void)
+
+ void snd_tplg_free(snd_tplg_t *tplg)
+ {
+- if (tplg->manifest_pdata)
+- free(tplg->manifest_pdata);
++ free(tplg->bin);
++ free(tplg->manifest_pdata);
+
+ tplg_elem_free_list(&tplg->tlv_list);
+ tplg_elem_free_list(&tplg->widget_list);
+diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h
+index e16c78d49709..87e6c9a517de 100644
+--- a/src/topology/tplg_local.h
++++ b/src/topology/tplg_local.h
+@@ -60,8 +60,9 @@ typedef enum _snd_pcm_rates {
+
+ struct snd_tplg {
+ /* out file */
+- int out_fd;
+- size_t out_pos;
++ unsigned char *bin;
++ size_t bin_pos;
++ size_t bin_size;
+
+ int verbose;
+ unsigned int version;
+--
+2.16.4
+
diff --git a/0038-topology-add-binary-output-from-the-builder.patch b/0038-topology-add-binary-output-from-the-builder.patch
new file mode 100644
index 0000000..6c77496
--- /dev/null
+++ b/0038-topology-add-binary-output-from-the-builder.patch
@@ -0,0 +1,173 @@
+From bee8d4fcaa52d00950d035ec561513c2b9e7cac7 Mon Sep 17 00:00:00 2001
+From: Jaroslav Kysela
+Date: Sat, 14 Dec 2019 19:20:02 +0100
+Subject: [PATCH 38/63] topology: add binary output from the builder
+
+- snd_tplg_build_bin()
+- snd_tplg_build_bin_file()
+
+Signed-off-by: Jaroslav Kysela
+---
+ include/topology.h | 22 +++++++++++++-
+ src/topology/parser.c | 79 ++++++++++++++++++++++++++++++++++++++++++---------
+ 2 files changed, 87 insertions(+), 14 deletions(-)
+
+diff --git a/include/topology.h b/include/topology.h
+index 27da7308dd62..c9ef554a610f 100644
+--- a/include/topology.h
++++ b/include/topology.h
+@@ -791,7 +791,18 @@ void snd_tplg_free(snd_tplg_t *tplg);
+ * \return Zero on success, otherwise a negative error code
+ */
+ int snd_tplg_build_file(snd_tplg_t *tplg, const char *infile,
+- const char *outfile);
++ const char *outfile);
++
++/**
++ * \brief Parse and build topology text file into binary file.
++ * \param tplg Topology instance.
++ * \param infile Topology text input file to be parsed
++ * \param bin Binary topology output buffer (malloc).
++ * \param size Binary topology output buffer size in bytes.
++ * \return Zero on success, otherwise a negative error code
++ */
++int snd_tplg_build_bin_file(snd_tplg_t *tplg, const char *infile,
++ void **bin, size_t *size);
+
+ /**
+ * \brief Enable verbose reporting of binary file output
+@@ -1089,6 +1100,15 @@ int snd_tplg_add_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t);
+ */
+ int snd_tplg_build(snd_tplg_t *tplg, const char *outfile);
+
++/**
++ * \brief Build all registered topology data into memory.
++ * \param tplg Topology instance.
++ * \param bin Binary topology output buffer (malloc).
++ * \param size Binary topology output buffer size in bytes.
++ * \return Zero on success, otherwise a negative error code
++ */
++int snd_tplg_build_bin(snd_tplg_t *tplg, void **bin, size_t *size);
++
+ /**
+ * \brief Attach private data to topology manifest.
+ * \param tplg Topology instance.
+diff --git a/src/topology/parser.c b/src/topology/parser.c
+index 98a9f9e9deac..861565b734ad 100644
+--- a/src/topology/parser.c
++++ b/src/topology/parser.c
+@@ -393,9 +393,7 @@ static int tplg_build_integ(snd_tplg_t *tplg)
+ return err;
+ }
+
+-int snd_tplg_build_file(snd_tplg_t *tplg,
+- const char *infile,
+- const char *outfile)
++static int tplg_load(snd_tplg_t *tplg, const char *infile)
+ {
+ snd_config_t *cfg = NULL;
+ int err = 0;
+@@ -414,10 +412,53 @@ int snd_tplg_build_file(snd_tplg_t *tplg,
+ }
+
+ snd_config_delete(cfg);
++ return 0;
++}
++
++static int tplg_build(snd_tplg_t *tplg)
++{
++ int err;
++
++ err = tplg_build_integ(tplg);
++ if (err < 0) {
++ SNDERR("error: failed to check topology integrity\n");
++ return err;
++ }
++
++ err = tplg_write_data(tplg);
++ if (err < 0) {
++ SNDERR("error: failed to write data %d\n", err);
++ return err;
++ }
++ return 0;
++}
++
++int snd_tplg_build_file(snd_tplg_t *tplg,
++ const char *infile,
++ const char *outfile)
++{
++ int err;
++
++ err = tplg_load(tplg, infile);
++ if (err < 0)
++ return err;
+
+ return snd_tplg_build(tplg, outfile);
+ }
+
++int snd_tplg_build_bin_file(snd_tplg_t *tplg,
++ const char *infile,
++ void **bin, size_t *size)
++{
++ int err;
++
++ err = tplg_load(tplg, infile);
++ if (err < 0)
++ return err;
++
++ return snd_tplg_build_bin(tplg, bin, size);
++}
++
+ int snd_tplg_add_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
+ {
+ switch (t->type) {
+@@ -450,17 +491,9 @@ int snd_tplg_build(snd_tplg_t *tplg, const char *outfile)
+ int fd, err;
+ ssize_t r;
+
+- err = tplg_build_integ(tplg);
+- if (err < 0) {
+- SNDERR("error: failed to check topology integrity\n");
+- return err;
+- }
+-
+- err = tplg_write_data(tplg);
+- if (err < 0) {
+- SNDERR("error: failed to write data %d\n", err);
++ err = tplg_build(tplg);
++ if (err < 0)
+ return err;
+- }
+
+ fd = open(outfile, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+ if (fd < 0) {
+@@ -481,6 +514,26 @@ int snd_tplg_build(snd_tplg_t *tplg, const char *outfile)
+ return 0;
+ }
+
++int snd_tplg_build_bin(snd_tplg_t *tplg,
++ void **bin, size_t *size)
++{
++ int err;
++
++ err = tplg_build(tplg);
++ if (err < 0)
++ return err;
++
++ err = tplg_build(tplg);
++ if (err < 0)
++ return err;
++
++ *bin = tplg->bin;
++ *size = tplg->bin_size;
++ tplg->bin = NULL;
++ tplg->bin_size = tplg->bin_pos = 0;
++ return 0;
++}
++
+ int snd_tplg_set_manifest_data(snd_tplg_t *tplg, const void *data, int len)
+ {
+ if (len <= 0)
+--
+2.16.4
+
diff --git a/0039-topology-parser-recode-tplg_parse_config.patch b/0039-topology-parser-recode-tplg_parse_config.patch
new file mode 100644
index 0000000..1867a91
--- /dev/null
+++ b/0039-topology-parser-recode-tplg_parse_config.patch
@@ -0,0 +1,346 @@
+From 22b66731f3dc0eb5149a99ff547eeb84eaf8d54b Mon Sep 17 00:00:00 2001
+From: Jaroslav Kysela
+Date: Sat, 14 Dec 2019 20:32:24 +0100
+Subject: [PATCH 39/63] topology: parser - recode tplg_parse_config()
+
+Signed-off-by: Jaroslav Kysela
+---
+ src/topology/parser.c | 225 +++++++++++++++++++---------------------------
+ src/topology/tplg_local.h | 67 ++++----------
+ 2 files changed, 108 insertions(+), 184 deletions(-)
+
+diff --git a/src/topology/parser.c b/src/topology/parser.c
+index 861565b734ad..82af7cc518d8 100644
+--- a/src/topology/parser.c
++++ b/src/topology/parser.c
+@@ -142,9 +142,88 @@ int tplg_parse_compound(snd_tplg_t *tplg, snd_config_t *cfg,
+
+ static int tplg_parse_config(snd_tplg_t *tplg, snd_config_t *cfg)
+ {
++ static struct _parser {
++ const char *id;
++ int (*parser)(snd_tplg_t *tplg, snd_config_t *cfg, void *priv);
++ } *p, parsers[] = {
++ {
++ .id = "SectionTLV",
++ .parser = tplg_parse_tlv
++ },
++ {
++ .id = "SectionControlMixer",
++ .parser = tplg_parse_control_mixer
++ },
++ {
++ .id = "SectionControlEnum",
++ .parser = tplg_parse_control_enum
++ },
++ {
++ .id = "SectionControlBytes",
++ .parser = tplg_parse_control_bytes
++ },
++ {
++ .id = "SectionWidget",
++ .parser = tplg_parse_dapm_widget
++ },
++ {
++ .id = "SectionPCMCapabilities",
++ .parser = tplg_parse_stream_caps
++ },
++ {
++ .id = "SectionPCM",
++ .parser = tplg_parse_pcm
++ },
++ {
++ .id = "SectionDAI",
++ .parser = tplg_parse_dai
++ },
++ {
++ .id = "SectionHWConfig",
++ .parser = tplg_parse_hw_config
++ },
++ {
++ .id = "SectionLink",
++ .parser = tplg_parse_link
++ },
++ {
++ .id = "SectionBE",
++ .parser = tplg_parse_link
++ },
++ {
++ .id = "SectionCC",
++ .parser = tplg_parse_cc
++ },
++ {
++ .id = "SectionGraph",
++ .parser = tplg_parse_dapm_graph
++ },
++ {
++ .id = "SectionText",
++ .parser = tplg_parse_text
++ },
++ {
++ .id = "SectionData",
++ .parser = tplg_parse_data
++ },
++ {
++ .id = "SectionVendorTokens",
++ .parser = tplg_parse_tokens
++ },
++ {
++ .id = "SectionVendorTuples",
++ .parser = tplg_parse_tuples
++ },
++ {
++ .id = "SectionManifest",
++ .parser = tplg_parse_manifest_data
++ },
++ };
++ int (*parser)(snd_tplg_t *tplg, snd_config_t *cfg, void *priv);
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id;
++ unsigned int idx;
+ int err;
+
+ if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
+@@ -159,145 +238,23 @@ static int tplg_parse_config(snd_tplg_t *tplg, snd_config_t *cfg)
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+- if (strcmp(id, "SectionTLV") == 0) {
+- err = tplg_parse_compound(tplg, n, tplg_parse_tlv,
+- NULL);
+- if (err < 0)
+- return err;
+- continue;
++ parser = NULL;
++ for (idx = 0; idx < ARRAY_SIZE(parsers); idx++) {
++ p = &parsers[idx];
++ if (strcmp(id, p->id) == 0) {
++ parser = p->parser;
++ break;
++ }
+ }
+
+- if (strcmp(id, "SectionControlMixer") == 0) {
+- err = tplg_parse_compound(tplg, n,
+- tplg_parse_control_mixer, NULL);
+- if (err < 0)
+- return err;
++ if (parser == NULL) {
++ SNDERR("error: unknown section %s\n", id);
+ continue;
+ }
+
+- if (strcmp(id, "SectionControlEnum") == 0) {
+- err = tplg_parse_compound(tplg, n,
+- tplg_parse_control_enum, NULL);
+- if (err < 0)
+- return err;
+- continue;
+- }
+-
+- if (strcmp(id, "SectionControlBytes") == 0) {
+- err = tplg_parse_compound(tplg, n,
+- tplg_parse_control_bytes, NULL);
+- if (err < 0)
+- return err;
+- continue;
+- }
+-
+- if (strcmp(id, "SectionWidget") == 0) {
+- err = tplg_parse_compound(tplg, n,
+- tplg_parse_dapm_widget, NULL);
+- if (err < 0)
+- return err;
+- continue;
+- }
+-
+- if (strcmp(id, "SectionPCMCapabilities") == 0) {
+- err = tplg_parse_compound(tplg, n,
+- tplg_parse_stream_caps, NULL);
+- if (err < 0)
+- return err;
+- continue;
+- }
+-
+- if (strcmp(id, "SectionPCM") == 0) {
+- err = tplg_parse_compound(tplg, n,
+- tplg_parse_pcm, NULL);
+- if (err < 0)
+- return err;
+- continue;
+- }
+-
+- if (strcmp(id, "SectionDAI") == 0) {
+- err = tplg_parse_compound(tplg, n,
+- tplg_parse_dai, NULL);
+- if (err < 0)
+- return err;
+- continue;
+- }
+-
+- if (strcmp(id, "SectionHWConfig") == 0) {
+- err = tplg_parse_compound(tplg, n, tplg_parse_hw_config,
+- NULL);
+- if (err < 0)
+- return err;
+- continue;
+- }
+-
+- if (strcmp(id, "SectionLink") == 0
+- || strcmp(id, "SectionBE") == 0) {
+- err = tplg_parse_compound(tplg, n, tplg_parse_link,
+- NULL);
+- if (err < 0)
+- return err;
+- continue;
+- }
+-
+- if (strcmp(id, "SectionCC") == 0) {
+- err = tplg_parse_compound(tplg, n, tplg_parse_cc,
+- NULL);
+- if (err < 0)
+- return err;
+- continue;
+- }
+-
+- if (strcmp(id, "SectionGraph") == 0) {
+- err = tplg_parse_compound(tplg, n,
+- tplg_parse_dapm_graph, NULL);
+- if (err < 0)
+- return err;
+- continue;
+- }
+-
+- if (strcmp(id, "SectionText") == 0) {
+- err = tplg_parse_compound(tplg, n, tplg_parse_text,
+- NULL);
+- if (err < 0)
+- return err;
+- continue;
+- }
+-
+- if (strcmp(id, "SectionData") == 0) {
+- err = tplg_parse_compound(tplg, n, tplg_parse_data,
+- NULL);
+- if (err < 0)
+- return err;
+- continue;
+- }
+-
+- if (strcmp(id, "SectionVendorTokens") == 0) {
+- err = tplg_parse_compound(tplg, n, tplg_parse_tokens,
+- NULL);
+- if (err < 0)
+- return err;
+- continue;
+- }
+-
+- if (strcmp(id, "SectionVendorTuples") == 0) {
+- err = tplg_parse_compound(tplg, n, tplg_parse_tuples,
+- NULL);
+- if (err < 0)
+- return err;
+- continue;
+- }
+-
+- if (strcmp(id, "SectionManifest") == 0) {
+- err = tplg_parse_compound(tplg, n,
+- tplg_parse_manifest_data,
+- NULL);
+- if (err < 0)
+- return err;
+- continue;
+- }
+-
+- SNDERR("error: unknown section %s\n", id);
++ err = tplg_parse_compound(tplg, n, parser, NULL);
++ if (err < 0)
++ return err;
+ }
+ return 0;
+ }
+diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h
+index 87e6c9a517de..77a681897a85 100644
+--- a/src/topology/tplg_local.h
++++ b/src/topology/tplg_local.h
+@@ -202,59 +202,26 @@ int tplg_parse_compound(snd_tplg_t *tplg, snd_config_t *cfg,
+
+ int tplg_write_data(snd_tplg_t *tplg);
+
+-int tplg_parse_tlv(snd_tplg_t *tplg, snd_config_t *cfg,
+- void *private ATTRIBUTE_UNUSED);
+-
+-int tplg_parse_text(snd_tplg_t *tplg, snd_config_t *cfg,
+- void *private ATTRIBUTE_UNUSED);
+-
+-int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg,
+- void *private ATTRIBUTE_UNUSED);
+-
+-int tplg_parse_tokens(snd_tplg_t *tplg, snd_config_t *cfg,
+- void *private ATTRIBUTE_UNUSED);
+-
+-int tplg_parse_tuples(snd_tplg_t *tplg, snd_config_t *cfg,
+- void *private ATTRIBUTE_UNUSED);
++int tplg_parse_tlv(snd_tplg_t *tplg, snd_config_t *cfg, void *priv);
++int tplg_parse_text(snd_tplg_t *tplg, snd_config_t *cfg, void *priv);
++int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg, void *priv);
++int tplg_parse_tokens(snd_tplg_t *tplg, snd_config_t *cfg, void *priv);
++int tplg_parse_tuples(snd_tplg_t *tplg, snd_config_t *cfg, void *priv);
++int tplg_parse_manifest_data(snd_tplg_t *tplg, snd_config_t *cfg, void *priv);
++int tplg_parse_control_bytes(snd_tplg_t *tplg, snd_config_t *cfg, void *priv);
++int tplg_parse_control_enum(snd_tplg_t *tplg, snd_config_t *cfg, void *priv);
++int tplg_parse_control_mixer(snd_tplg_t *tplg, snd_config_t *cfg, void *priv);
++int tplg_parse_dapm_graph(snd_tplg_t *tplg, snd_config_t *cfg, void *priv);
++int tplg_parse_dapm_widget(snd_tplg_t *tplg, snd_config_t *cfg, void *priv);
++int tplg_parse_stream_caps(snd_tplg_t *tplg, snd_config_t *cfg, void *priv);
++int tplg_parse_pcm(snd_tplg_t *tplg, snd_config_t *cfg, void *priv);
++int tplg_parse_dai(snd_tplg_t *tplg, snd_config_t *cfg, void *priv);
++int tplg_parse_link(snd_tplg_t *tplg, snd_config_t *cfg, void *priv);
++int tplg_parse_cc(snd_tplg_t *tplg, snd_config_t *cfg, void *priv);
++int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg, void *priv);
+
+ void tplg_free_tuples(void *obj);
+
+-int tplg_parse_manifest_data(snd_tplg_t *tplg, snd_config_t *cfg,
+- void *private ATTRIBUTE_UNUSED);
+-
+-int tplg_parse_control_bytes(snd_tplg_t *tplg,
+- snd_config_t *cfg, void *private ATTRIBUTE_UNUSED);
+-
+-int tplg_parse_control_enum(snd_tplg_t *tplg, snd_config_t *cfg,
+- void *private ATTRIBUTE_UNUSED);
+-
+-int tplg_parse_control_mixer(snd_tplg_t *tplg,
+- snd_config_t *cfg, void *private ATTRIBUTE_UNUSED);
+-
+-int tplg_parse_dapm_graph(snd_tplg_t *tplg, snd_config_t *cfg,
+- void *private ATTRIBUTE_UNUSED);
+-
+-int tplg_parse_dapm_widget(snd_tplg_t *tplg,
+- snd_config_t *cfg, void *private ATTRIBUTE_UNUSED);
+-
+-int tplg_parse_stream_caps(snd_tplg_t *tplg,
+- snd_config_t *cfg, void *private ATTRIBUTE_UNUSED);
+-
+-int tplg_parse_pcm(snd_tplg_t *tplg,
+- snd_config_t *cfg, void *private ATTRIBUTE_UNUSED);
+-
+-int tplg_parse_dai(snd_tplg_t *tplg, snd_config_t *cfg,
+- void *private ATTRIBUTE_UNUSED);
+-
+-int tplg_parse_link(snd_tplg_t *tplg,
+- snd_config_t *cfg, void *private ATTRIBUTE_UNUSED);
+-
+-int tplg_parse_cc(snd_tplg_t *tplg,
+- snd_config_t *cfg, void *private ATTRIBUTE_UNUSED);
+-
+-int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg,
+- void *private ATTRIBUTE_UNUSED);
+-
+ int tplg_build_data(snd_tplg_t *tplg);
+ int tplg_build_manifest_data(snd_tplg_t *tplg);
+ int tplg_build_controls(snd_tplg_t *tplg);
+--
+2.16.4
+
diff --git a/0040-topology-add-snd_tplg_load-remove-snd_tplg_build_bin.patch b/0040-topology-add-snd_tplg_load-remove-snd_tplg_build_bin.patch
new file mode 100644
index 0000000..1b18fe3
--- /dev/null
+++ b/0040-topology-add-snd_tplg_load-remove-snd_tplg_build_bin.patch
@@ -0,0 +1,208 @@
+From d52eaba63dfe1d845663a4cd1bf676fafc43874a Mon Sep 17 00:00:00 2001
+From: Jaroslav Kysela
+Date: Sun, 15 Dec 2019 16:03:29 +0100
+Subject: [PATCH 40/63] topology: add snd_tplg_load() remove
+ snd_tplg_build_bin_file()
+
+Signed-off-by: Jaroslav Kysela
+---
+ include/topology.h | 16 ++++-----
+ src/topology/parser.c | 96 ++++++++++++++++++---------------------------------
+ 2 files changed, 41 insertions(+), 71 deletions(-)
+
+diff --git a/include/topology.h b/include/topology.h
+index c9ef554a610f..c9f4ffea27de 100644
+--- a/include/topology.h
++++ b/include/topology.h
+@@ -784,25 +784,23 @@ snd_tplg_t *snd_tplg_new(void);
+ void snd_tplg_free(snd_tplg_t *tplg);
+
+ /**
+- * \brief Parse and build topology text file into binary file.
++ * \brief Load topology from the text buffer.
+ * \param tplg Topology instance.
+- * \param infile Topology text input file to be parsed
+- * \param outfile Binary topology output file.
++ * \param buf Text buffer.
++ * \param size Text buffer size in bytes.
+ * \return Zero on success, otherwise a negative error code
+ */
+-int snd_tplg_build_file(snd_tplg_t *tplg, const char *infile,
+- const char *outfile);
++int snd_tplg_load(snd_tplg_t *tplg, const char *buf, size_t size);
+
+ /**
+ * \brief Parse and build topology text file into binary file.
+ * \param tplg Topology instance.
+ * \param infile Topology text input file to be parsed
+- * \param bin Binary topology output buffer (malloc).
+- * \param size Binary topology output buffer size in bytes.
++ * \param outfile Binary topology output file.
+ * \return Zero on success, otherwise a negative error code
+ */
+-int snd_tplg_build_bin_file(snd_tplg_t *tplg, const char *infile,
+- void **bin, size_t *size);
++int snd_tplg_build_file(snd_tplg_t *tplg, const char *infile,
++ const char *outfile);
+
+ /**
+ * \brief Enable verbose reporting of binary file output
+diff --git a/src/topology/parser.c b/src/topology/parser.c
+index 82af7cc518d8..ed864d3223c4 100644
+--- a/src/topology/parser.c
++++ b/src/topology/parser.c
+@@ -259,52 +259,30 @@ static int tplg_parse_config(snd_tplg_t *tplg, snd_config_t *cfg)
+ return 0;
+ }
+
+-static int tplg_load_config(const char *file, snd_config_t **cfg)
++static int tplg_load_config(snd_tplg_t *tplg, snd_input_t *in)
+ {
+- FILE *fp;
+- snd_input_t *in;
+ snd_config_t *top;
+ int ret;
+
+- fp = fopen(file, "r");
+- if (fp == NULL) {
+- SNDERR("error: could not open configuration file %s",
+- file);
+- return -errno;
+- }
+-
+- ret = snd_input_stdio_attach(&in, fp, 1);
+- if (ret < 0) {
+- fclose(fp);
+- SNDERR("error: could not attach stdio %s", file);
+- return ret;
+- }
+ ret = snd_config_top(&top);
+ if (ret < 0)
+- goto err;
++ return ret;
+
+ ret = snd_config_load(top, in);
+ if (ret < 0) {
+- SNDERR("error: could not load configuration file %s",
+- file);
+- goto err_load;
++ SNDERR("error: could not load configuration");
++ snd_config_delete(top);
++ return ret;
+ }
+
+- ret = snd_input_close(in);
++ ret = tplg_parse_config(tplg, top);
++ snd_config_delete(top);
+ if (ret < 0) {
+- in = NULL;
+- goto err_load;
++ SNDERR("error: failed to parse topology");
++ return ret;
+ }
+
+- *cfg = top;
+ return 0;
+-
+-err_load:
+- snd_config_delete(top);
+-err:
+- if (in)
+- snd_input_close(in);
+- return ret;
+ }
+
+ static int tplg_build_integ(snd_tplg_t *tplg)
+@@ -350,26 +328,20 @@ static int tplg_build_integ(snd_tplg_t *tplg)
+ return err;
+ }
+
+-static int tplg_load(snd_tplg_t *tplg, const char *infile)
++int snd_tplg_load(snd_tplg_t *tplg, const char *buf, size_t size)
+ {
+- snd_config_t *cfg = NULL;
+- int err = 0;
+-
+- err = tplg_load_config(infile, &cfg);
+- if (err < 0) {
+- SNDERR("error: failed to load topology file %s\n",
+- infile);
+- return err;
+- }
++ snd_input_t *in;
++ int err;
+
+- err = tplg_parse_config(tplg, cfg);
++ err = snd_input_buffer_open(&in, buf, size);
+ if (err < 0) {
+- SNDERR("error: failed to parse topology\n");
++ SNDERR("error: could not create input buffer");
+ return err;
+ }
+
+- snd_config_delete(cfg);
+- return 0;
++ err = tplg_load_config(tplg, in);
++ snd_input_close(in);
++ return err;
+ }
+
+ static int tplg_build(snd_tplg_t *tplg)
+@@ -394,26 +366,30 @@ int snd_tplg_build_file(snd_tplg_t *tplg,
+ const char *infile,
+ const char *outfile)
+ {
++ FILE *fp;
++ snd_input_t *in;
+ int err;
+
+- err = tplg_load(tplg, infile);
+- if (err < 0)
+- return err;
+-
+- return snd_tplg_build(tplg, outfile);
+-}
++ fp = fopen(infile, "r");
++ if (fp == NULL) {
++ SNDERR("error: could not open configuration file %s",
++ infile);
++ return -errno;
++ }
+
+-int snd_tplg_build_bin_file(snd_tplg_t *tplg,
+- const char *infile,
+- void **bin, size_t *size)
+-{
+- int err;
++ err = snd_input_stdio_attach(&in, fp, 1);
++ if (err < 0) {
++ fclose(fp);
++ SNDERR("error: could not attach stdio %s", infile);
++ return err;
++ }
+
+- err = tplg_load(tplg, infile);
++ err = tplg_load_config(tplg, in);
++ snd_input_close(in);
+ if (err < 0)
+ return err;
+
+- return snd_tplg_build_bin(tplg, bin, size);
++ return snd_tplg_build(tplg, outfile);
+ }
+
+ int snd_tplg_add_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
+@@ -476,10 +452,6 @@ int snd_tplg_build_bin(snd_tplg_t *tplg,
+ {
+ int err;
+
+- err = tplg_build(tplg);
+- if (err < 0)
+- return err;
+-
+ err = tplg_build(tplg);
+ if (err < 0)
+ return err;
+--
+2.16.4
+
diff --git a/0041-topology-move-the-topology-element-table-from-builde.patch b/0041-topology-move-the-topology-element-table-from-builde.patch
new file mode 100644
index 0000000..721162e
--- /dev/null
+++ b/0041-topology-move-the-topology-element-table-from-builde.patch
@@ -0,0 +1,464 @@
+From 4f076f5b69a873418ecb826a4198e4d95a3a2a6f Mon Sep 17 00:00:00 2001
+From: Jaroslav Kysela
+Date: Sun, 15 Dec 2019 16:15:29 +0100
+Subject: [PATCH 41/63] topology: move the topology element table from builder
+ to elem
+
+- use offsetof() for the lists
+- add other info to describe the elements
+- use the table in the element constructor
+
+Signed-off-by: Jaroslav Kysela
+---
+ src/topology/builder.c | 98 ++++----------------
+ src/topology/ctl.c | 7 +-
+ src/topology/elem.c | 232 ++++++++++++++++++++++++++++++++--------------
+ src/topology/tplg_local.h | 15 +++
+ 4 files changed, 199 insertions(+), 153 deletions(-)
+
+diff --git a/src/topology/builder.c b/src/topology/builder.c
+index 40943b56e4aa..cadb55830809 100644
+--- a/src/topology/builder.c
++++ b/src/topology/builder.c
+@@ -236,83 +236,20 @@ static ssize_t write_manifest_data(snd_tplg_t *tplg)
+
+ int tplg_write_data(snd_tplg_t *tplg)
+ {
+- struct wtable {
+- const char *name;
+- struct list_head *list;
+- int type;
+- int tsoc;
+- } *wptr, wtable[] = {
+- {
+- .name = "control mixer",
+- .list = &tplg->mixer_list,
+- .type = SND_TPLG_TYPE_MIXER,
+- .tsoc = SND_SOC_TPLG_TYPE_MIXER,
+- },
+- {
+- .name = "control enum",
+- .list = &tplg->enum_list,
+- .type = SND_TPLG_TYPE_ENUM,
+- .tsoc = SND_SOC_TPLG_TYPE_ENUM,
+- },
+- {
+- .name = "control extended (bytes)",
+- .list = &tplg->bytes_ext_list,
+- .type = SND_TPLG_TYPE_BYTES,
+- .tsoc = SND_SOC_TPLG_TYPE_BYTES,
+- },
+- {
+- .name = "dapm widget",
+- .list = &tplg->widget_list,
+- .type = SND_TPLG_TYPE_DAPM_WIDGET,
+- .tsoc = SND_SOC_TPLG_TYPE_DAPM_WIDGET,
+- },
+- {
+- .name = "pcm",
+- .list = &tplg->pcm_list,
+- .type = SND_TPLG_TYPE_PCM,
+- .tsoc = SND_SOC_TPLG_TYPE_PCM,
+- },
+- {
+- .name = "physical dai",
+- .list = &tplg->dai_list,
+- .type = SND_TPLG_TYPE_DAI,
+- .tsoc = SND_SOC_TPLG_TYPE_DAI,
+- },
+- {
+- .name = "be",
+- .list = &tplg->be_list,
+- .type = SND_TPLG_TYPE_BE,
+- .tsoc = SND_SOC_TPLG_TYPE_BACKEND_LINK,
+- },
+- {
+- .name = "cc",
+- .list = &tplg->cc_list,
+- .type = SND_TPLG_TYPE_CC,
+- .tsoc = SND_SOC_TPLG_TYPE_CODEC_LINK,
+- },
+- {
+- .name = "route (dapm graph)",
+- .list = &tplg->route_list,
+- .type = SND_TPLG_TYPE_DAPM_GRAPH,
+- .tsoc = SND_SOC_TPLG_TYPE_DAPM_GRAPH,
+- },
+- {
+- .name = "private data",
+- .list = &tplg->pdata_list,
+- .type = SND_TPLG_TYPE_DATA,
+- .tsoc = SND_SOC_TPLG_TYPE_PDATA,
+- },
+- };
+-
++ struct tplg_table *tptr;
++ struct list_head *list;
+ ssize_t ret;
+ size_t total_size, size;
+ unsigned int index;
+
+ /* calculate total size */
+ total_size = calc_manifest_size(tplg);
+- for (index = 0; index < ARRAY_SIZE(wtable); index++) {
+- wptr = &wtable[index];
+- size = calc_real_size(wptr->list);
++ for (index = 0; index < tplg_table_items; index++) {
++ tptr = &tplg_table[index];
++ if (!tptr->build)
++ continue;
++ list = (struct list_head *)((void *)tplg + tptr->loff);
++ size = calc_real_size(list);
+ total_size += size;
+ }
+
+@@ -334,20 +271,23 @@ int tplg_write_data(snd_tplg_t *tplg)
+ }
+
+ /* write all blocks */
+- for (index = 0; index < ARRAY_SIZE(wtable); index++) {
+- wptr = &wtable[index];
++ for (index = 0; index < tplg_table_items; index++) {
++ tptr = &tplg_table[index];
++ if (!tptr->build)
++ continue;
++ list = (struct list_head *)((void *)tplg + tptr->loff);
+ /* calculate the block size in bytes for all elems in this list */
+- size = calc_block_size(wptr->list);
++ size = calc_block_size(list);
+ if (size == 0)
+ continue;
+ verbose(tplg, "block size for type %s (%d:%d) is 0x%zx/%zd\n",
+- wptr->name, wptr->type,
+- wptr->tsoc, size, size);
+- ret = write_elem_block(tplg, wptr->list, size,
+- wptr->tsoc, wptr->name);
++ tptr->name, tptr->type,
++ tptr->tsoc, size, size);
++ ret = write_elem_block(tplg, list, size,
++ tptr->tsoc, tptr->name);
+ if (ret < 0) {
+ SNDERR("failed to write %s elements: %s\n",
+- wptr->name, snd_strerror(-ret));
++ tptr->name, snd_strerror(-ret));
+ return ret;
+ }
+ }
+diff --git a/src/topology/ctl.c b/src/topology/ctl.c
+index 9190efefb575..539329cd661f 100644
+--- a/src/topology/ctl.c
++++ b/src/topology/ctl.c
+@@ -284,18 +284,13 @@ static int tplg_parse_tlv_dbscale(snd_config_t *cfg, struct tplg_elem *elem)
+ {
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+- struct snd_soc_tplg_ctl_tlv *tplg_tlv;
++ struct snd_soc_tplg_ctl_tlv *tplg_tlv = elem->tlv;
+ struct snd_soc_tplg_tlv_dbscale *scale;
+ const char *id = NULL;
+ int val;
+
+ tplg_dbg(" scale: %s\n", elem->id);
+
+- tplg_tlv = calloc(1, sizeof(*tplg_tlv));
+- if (!tplg_tlv)
+- return -ENOMEM;
+-
+- elem->tlv = tplg_tlv;
+ tplg_tlv->size = sizeof(struct snd_soc_tplg_ctl_tlv);
+ tplg_tlv->type = SNDRV_CTL_TLVT_DB_SCALE;
+ scale = &tplg_tlv->scale;
+diff --git a/src/topology/elem.c b/src/topology/elem.c
+index f2076f7958aa..92ca7da4c4aa 100644
+--- a/src/topology/elem.c
++++ b/src/topology/elem.c
+@@ -20,6 +20,154 @@
+ #include "list.h"
+ #include "tplg_local.h"
+
++struct tplg_table tplg_table[] = {
++ {
++ .name = "manifest",
++ .loff = offsetof(snd_tplg_t, manifest_list),
++ .type = SND_TPLG_TYPE_MANIFEST,
++ .tsoc = SND_SOC_TPLG_TYPE_MANIFEST,
++ .size = sizeof(struct snd_soc_tplg_manifest),
++ .enew = 1,
++ },
++ {
++ .name = "control mixer",
++ .loff = offsetof(snd_tplg_t, mixer_list),
++ .type = SND_TPLG_TYPE_MIXER,
++ .tsoc = SND_SOC_TPLG_TYPE_MIXER,
++ .size = sizeof(struct snd_soc_tplg_mixer_control),
++ .build = 1,
++ .enew = 1,
++ },
++ {
++ .name = "control enum",
++ .loff = offsetof(snd_tplg_t, enum_list),
++ .type = SND_TPLG_TYPE_ENUM,
++ .tsoc = SND_SOC_TPLG_TYPE_ENUM,
++ .size = sizeof(struct snd_soc_tplg_enum_control),
++ .build = 1,
++ .enew = 1,
++ },
++ {
++ .name = "control extended (bytes)",
++ .loff = offsetof(snd_tplg_t, bytes_ext_list),
++ .type = SND_TPLG_TYPE_BYTES,
++ .tsoc = SND_SOC_TPLG_TYPE_BYTES,
++ .size = sizeof(struct snd_soc_tplg_bytes_control),
++ .build = 1,
++ .enew = 1,
++ },
++ {
++ .name = "dapm widget",
++ .loff = offsetof(snd_tplg_t, widget_list),
++ .type = SND_TPLG_TYPE_DAPM_WIDGET,
++ .tsoc = SND_SOC_TPLG_TYPE_DAPM_WIDGET,
++ .size = sizeof(struct snd_soc_tplg_dapm_widget),
++ .build = 1,
++ .enew = 1,
++ },
++ {
++ .name = "pcm",
++ .loff = offsetof(snd_tplg_t, pcm_list),
++ .type = SND_TPLG_TYPE_PCM,
++ .tsoc = SND_SOC_TPLG_TYPE_PCM,
++ .size = sizeof(struct snd_soc_tplg_pcm),
++ .build = 1,
++ .enew = 1,
++ },
++ {
++ .name = "physical dai",
++ .loff = offsetof(snd_tplg_t, dai_list),
++ .type = SND_TPLG_TYPE_DAI,
++ .tsoc = SND_SOC_TPLG_TYPE_DAI,
++ .size = sizeof(struct snd_soc_tplg_dai),
++ .build = 1,
++ .enew = 1,
++ },
++ {
++ .name = "be",
++ .loff = offsetof(snd_tplg_t, be_list),
++ .type = SND_TPLG_TYPE_BE,
++ .tsoc = SND_SOC_TPLG_TYPE_BACKEND_LINK,
++ .size = sizeof(struct snd_soc_tplg_link_config),
++ .build = 1,
++ .enew = 1,
++ },
++ {
++ .name = "cc",
++ .loff = offsetof(snd_tplg_t, cc_list),
++ .type = SND_TPLG_TYPE_CC,
++ .tsoc = SND_SOC_TPLG_TYPE_CODEC_LINK,
++ .size = sizeof(struct snd_soc_tplg_link_config),
++ .build = 1,
++ .enew = 1,
++ },
++ {
++ .name = "route (dapm graph)",
++ .loff = offsetof(snd_tplg_t, route_list),
++ .type = SND_TPLG_TYPE_DAPM_GRAPH,
++ .tsoc = SND_SOC_TPLG_TYPE_DAPM_GRAPH,
++ .build = 1,
++ },
++ {
++ .name = "private data",
++ .loff = offsetof(snd_tplg_t, pdata_list),
++ .type = SND_TPLG_TYPE_DATA,
++ .tsoc = SND_SOC_TPLG_TYPE_PDATA,
++ .build = 1,
++ .enew = 1,
++ },
++ {
++ .name = "text",
++ .loff = offsetof(snd_tplg_t, text_list),
++ .type = SND_TPLG_TYPE_TEXT,
++ .size = sizeof(struct tplg_texts),
++ .enew = 1,
++ },
++ {
++ .name = "tlv",
++ .loff = offsetof(snd_tplg_t, tlv_list),
++ .type = SND_TPLG_TYPE_TLV,
++ .size = sizeof(struct snd_soc_tplg_ctl_tlv),
++ .enew = 1,
++ },
++ {
++ .name = "stream config",
++ .loff = offsetof(snd_tplg_t, pcm_config_list),
++ .type = SND_TPLG_TYPE_STREAM_CONFIG,
++ .size = sizeof(struct snd_soc_tplg_stream),
++ .enew = 1,
++ },
++ {
++ .name = "stream capabilities",
++ .loff = offsetof(snd_tplg_t, pcm_caps_list),
++ .type = SND_TPLG_TYPE_STREAM_CAPS,
++ .size = sizeof(struct snd_soc_tplg_stream_caps),
++ .enew = 1,
++ },
++ {
++ .name = "token",
++ .loff = offsetof(snd_tplg_t, token_list),
++ .type = SND_TPLG_TYPE_TOKEN,
++ .enew = 1,
++ },
++ {
++ .name = "tuple",
++ .loff = offsetof(snd_tplg_t, tuple_list),
++ .type = SND_TPLG_TYPE_TUPLE,
++ .free = tplg_free_tuples,
++ .enew = 1,
++ },
++ {
++ .name = "hw config",
++ .loff = offsetof(snd_tplg_t, hw_cfg_list),
++ .type = SND_TPLG_TYPE_HW_CONFIG,
++ .size = sizeof(struct snd_soc_tplg_hw_config),
++ .enew = 1,
++ }
++};
++
++unsigned int tplg_table_items = ARRAY_SIZE(tplg_table);
++
+ int tplg_ref_add(struct tplg_elem *elem, int type, const char* id)
+ {
+ struct tplg_ref *ref;
+@@ -152,9 +300,12 @@ struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg,
+ const char *name,
+ enum snd_tplg_type type)
+ {
++ struct tplg_table *tptr;
+ struct tplg_elem *elem;
++ struct list_head *list;
+ const char *id;
+ int obj_size = 0;
++ unsigned index;
+ void *obj;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+@@ -191,79 +342,24 @@ struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg,
+ } else if (name != NULL)
+ snd_strlcpy(elem->id, name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+
+- switch (type) {
+- case SND_TPLG_TYPE_DATA:
+- tplg_elem_insert(elem, &tplg->pdata_list);
+- break;
+- case SND_TPLG_TYPE_MANIFEST:
+- tplg_elem_insert(elem, &tplg->manifest_list);
+- obj_size = sizeof(struct snd_soc_tplg_manifest);
+- break;
+- case SND_TPLG_TYPE_TEXT:
+- tplg_elem_insert(elem, &tplg->text_list);
+- obj_size = sizeof(struct tplg_texts);
+- break;
+- case SND_TPLG_TYPE_TLV:
+- tplg_elem_insert(elem, &tplg->tlv_list);
+- elem->size = sizeof(struct snd_soc_tplg_ctl_tlv);
+- break;
+- case SND_TPLG_TYPE_BYTES:
+- tplg_elem_insert(elem, &tplg->bytes_ext_list);
+- obj_size = sizeof(struct snd_soc_tplg_bytes_control);
+- break;
+- case SND_TPLG_TYPE_ENUM:
+- tplg_elem_insert(elem, &tplg->enum_list);
+- obj_size = sizeof(struct snd_soc_tplg_enum_control);
+- break;
+- case SND_TPLG_TYPE_MIXER:
+- tplg_elem_insert(elem, &tplg->mixer_list);
+- obj_size = sizeof(struct snd_soc_tplg_mixer_control);
+- break;
+- case SND_TPLG_TYPE_DAPM_WIDGET:
+- tplg_elem_insert(elem, &tplg->widget_list);
+- obj_size = sizeof(struct snd_soc_tplg_dapm_widget);
+- break;
+- case SND_TPLG_TYPE_STREAM_CONFIG:
+- tplg_elem_insert(elem, &tplg->pcm_config_list);
+- obj_size = sizeof(struct snd_soc_tplg_stream);
++ for (index = 0; index < tplg_table_items; index++) {
++ tptr = &tplg_table[index];
++ if (!tptr->enew)
++ continue;
++ if ((int)type != tptr->type)
++ continue;
+ break;
+- case SND_TPLG_TYPE_STREAM_CAPS:
+- tplg_elem_insert(elem, &tplg->pcm_caps_list);
+- obj_size = sizeof(struct snd_soc_tplg_stream_caps);
+- break;
+- case SND_TPLG_TYPE_PCM:
+- tplg_elem_insert(elem, &tplg->pcm_list);
+- obj_size = sizeof(struct snd_soc_tplg_pcm);
+- break;
+- case SND_TPLG_TYPE_DAI:
+- tplg_elem_insert(elem, &tplg->dai_list);
+- obj_size = sizeof(struct snd_soc_tplg_dai);
+- break;
+- case SND_TPLG_TYPE_BE:
+- case SND_TPLG_TYPE_LINK:
+- tplg_elem_insert(elem, &tplg->be_list);
+- obj_size = sizeof(struct snd_soc_tplg_link_config);
+- break;
+- case SND_TPLG_TYPE_CC:
+- tplg_elem_insert(elem, &tplg->cc_list);
+- obj_size = sizeof(struct snd_soc_tplg_link_config);
+- break;
+- case SND_TPLG_TYPE_TOKEN:
+- tplg_elem_insert(elem, &tplg->token_list);
+- break;
+- case SND_TPLG_TYPE_TUPLE:
+- tplg_elem_insert(elem, &tplg->tuple_list);
+- elem->free = tplg_free_tuples;
+- break;
+- case SND_TPLG_TYPE_HW_CONFIG:
+- tplg_elem_insert(elem, &tplg->hw_cfg_list);
+- obj_size = sizeof(struct snd_soc_tplg_hw_config);
+- break;
+- default:
++ }
++ if (index >= tplg_table_items) {
+ free(elem);
+ return NULL;
+ }
+
++ list = (struct list_head *)((void *)tplg + tptr->loff);
++ tplg_elem_insert(elem, list);
++ obj_size = tptr->size;
++ elem->free = tptr->free;
++
+ /* create new object too if required */
+ if (obj_size > 0) {
+ obj = calloc(1, obj_size);
+diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h
+index 77a681897a85..0987898f8336 100644
+--- a/src/topology/tplg_local.h
++++ b/src/topology/tplg_local.h
+@@ -196,6 +196,21 @@ struct map_elem {
+ int id;
+ };
+
++/* mapping table */
++struct tplg_table {
++ const char *name;
++ off_t loff;
++ size_t size;
++ int type;
++ int tsoc;
++ unsigned build: 1;
++ unsigned enew: 1;
++ void (*free)(void *);
++};
++
++extern struct tplg_table tplg_table[];
++extern unsigned int tplg_table_items;
++
+ int tplg_parse_compound(snd_tplg_t *tplg, snd_config_t *cfg,
+ int (*fcn)(snd_tplg_t *, snd_config_t *, void *),
+ void *private);
+--
+2.16.4
+
diff --git a/0042-topology-add-parser-to-the-tplg_table.patch b/0042-topology-add-parser-to-the-tplg_table.patch
new file mode 100644
index 0000000..2637593
--- /dev/null
+++ b/0042-topology-add-parser-to-the-tplg_table.patch
@@ -0,0 +1,331 @@
+From 4a0efdc87355d5789876e20b9530dc85224ad281 Mon Sep 17 00:00:00 2001
+From: Jaroslav Kysela
+Date: Sun, 15 Dec 2019 17:24:50 +0100
+Subject: [PATCH 42/63] topology: add parser to the tplg_table
+
+Signed-off-by: Jaroslav Kysela
+---
+ src/topology/elem.c | 35 ++++++++++++++++++
+ src/topology/parser.c | 90 +++++------------------------------------------
+ src/topology/tplg_local.h | 3 ++
+ 3 files changed, 47 insertions(+), 81 deletions(-)
+
+diff --git a/src/topology/elem.c b/src/topology/elem.c
+index 92ca7da4c4aa..e79a68b71a91 100644
+--- a/src/topology/elem.c
++++ b/src/topology/elem.c
+@@ -23,112 +23,139 @@
+ struct tplg_table tplg_table[] = {
+ {
+ .name = "manifest",
++ .id = "SectionManifest",
+ .loff = offsetof(snd_tplg_t, manifest_list),
+ .type = SND_TPLG_TYPE_MANIFEST,
+ .tsoc = SND_SOC_TPLG_TYPE_MANIFEST,
+ .size = sizeof(struct snd_soc_tplg_manifest),
+ .enew = 1,
++ .parse = tplg_parse_manifest_data,
+ },
+ {
+ .name = "control mixer",
++ .id = "SectionControlMixer",
+ .loff = offsetof(snd_tplg_t, mixer_list),
+ .type = SND_TPLG_TYPE_MIXER,
+ .tsoc = SND_SOC_TPLG_TYPE_MIXER,
+ .size = sizeof(struct snd_soc_tplg_mixer_control),
+ .build = 1,
+ .enew = 1,
++ .parse = tplg_parse_control_mixer,
+ },
+ {
+ .name = "control enum",
++ .id = "SectionControlEnum",
+ .loff = offsetof(snd_tplg_t, enum_list),
+ .type = SND_TPLG_TYPE_ENUM,
+ .tsoc = SND_SOC_TPLG_TYPE_ENUM,
+ .size = sizeof(struct snd_soc_tplg_enum_control),
+ .build = 1,
+ .enew = 1,
++ .parse = tplg_parse_control_enum,
+ },
+ {
+ .name = "control extended (bytes)",
++ .id = "SectionControlBytes",
+ .loff = offsetof(snd_tplg_t, bytes_ext_list),
+ .type = SND_TPLG_TYPE_BYTES,
+ .tsoc = SND_SOC_TPLG_TYPE_BYTES,
+ .size = sizeof(struct snd_soc_tplg_bytes_control),
+ .build = 1,
+ .enew = 1,
++ .parse = tplg_parse_control_bytes,
+ },
+ {
+ .name = "dapm widget",
++ .id = "SectionWidget",
+ .loff = offsetof(snd_tplg_t, widget_list),
+ .type = SND_TPLG_TYPE_DAPM_WIDGET,
+ .tsoc = SND_SOC_TPLG_TYPE_DAPM_WIDGET,
+ .size = sizeof(struct snd_soc_tplg_dapm_widget),
+ .build = 1,
+ .enew = 1,
++ .parse = tplg_parse_dapm_widget,
+ },
+ {
+ .name = "pcm",
++ .id = "SectionPCM",
+ .loff = offsetof(snd_tplg_t, pcm_list),
+ .type = SND_TPLG_TYPE_PCM,
+ .tsoc = SND_SOC_TPLG_TYPE_PCM,
+ .size = sizeof(struct snd_soc_tplg_pcm),
+ .build = 1,
+ .enew = 1,
++ .parse = tplg_parse_pcm,
+ },
+ {
+ .name = "physical dai",
++ .id = "SectionDAI",
+ .loff = offsetof(snd_tplg_t, dai_list),
+ .type = SND_TPLG_TYPE_DAI,
+ .tsoc = SND_SOC_TPLG_TYPE_DAI,
+ .size = sizeof(struct snd_soc_tplg_dai),
+ .build = 1,
+ .enew = 1,
++ .parse = tplg_parse_dai,
+ },
+ {
+ .name = "be",
++ .id = "SectionBE",
++ .id2 = "SectionLink",
+ .loff = offsetof(snd_tplg_t, be_list),
+ .type = SND_TPLG_TYPE_BE,
+ .tsoc = SND_SOC_TPLG_TYPE_BACKEND_LINK,
+ .size = sizeof(struct snd_soc_tplg_link_config),
+ .build = 1,
+ .enew = 1,
++ .parse = tplg_parse_link,
+ },
+ {
+ .name = "cc",
++ .id = "SectionCC",
+ .loff = offsetof(snd_tplg_t, cc_list),
+ .type = SND_TPLG_TYPE_CC,
+ .tsoc = SND_SOC_TPLG_TYPE_CODEC_LINK,
+ .size = sizeof(struct snd_soc_tplg_link_config),
+ .build = 1,
+ .enew = 1,
++ .parse = tplg_parse_cc,
+ },
+ {
+ .name = "route (dapm graph)",
++ .id = "SectionGraph",
+ .loff = offsetof(snd_tplg_t, route_list),
+ .type = SND_TPLG_TYPE_DAPM_GRAPH,
+ .tsoc = SND_SOC_TPLG_TYPE_DAPM_GRAPH,
+ .build = 1,
++ .parse = tplg_parse_dapm_graph,
+ },
+ {
+ .name = "private data",
++ .id = "SectionData",
+ .loff = offsetof(snd_tplg_t, pdata_list),
+ .type = SND_TPLG_TYPE_DATA,
+ .tsoc = SND_SOC_TPLG_TYPE_PDATA,
+ .build = 1,
+ .enew = 1,
++ .parse = tplg_parse_data,
+ },
+ {
+ .name = "text",
++ .id = "SectionText",
+ .loff = offsetof(snd_tplg_t, text_list),
+ .type = SND_TPLG_TYPE_TEXT,
+ .size = sizeof(struct tplg_texts),
+ .enew = 1,
++ .parse = tplg_parse_text,
+ },
+ {
+ .name = "tlv",
++ .id = "SectionTLV",
+ .loff = offsetof(snd_tplg_t, tlv_list),
+ .type = SND_TPLG_TYPE_TLV,
+ .size = sizeof(struct snd_soc_tplg_ctl_tlv),
+ .enew = 1,
++ .parse = tplg_parse_tlv,
+ },
+ {
+ .name = "stream config",
+@@ -139,30 +166,38 @@ struct tplg_table tplg_table[] = {
+ },
+ {
+ .name = "stream capabilities",
++ .id = "SectionPCMCapabilities",
+ .loff = offsetof(snd_tplg_t, pcm_caps_list),
+ .type = SND_TPLG_TYPE_STREAM_CAPS,
+ .size = sizeof(struct snd_soc_tplg_stream_caps),
+ .enew = 1,
++ .parse = tplg_parse_stream_caps,
+ },
+ {
+ .name = "token",
++ .id = "SectionVendorTokens",
+ .loff = offsetof(snd_tplg_t, token_list),
+ .type = SND_TPLG_TYPE_TOKEN,
+ .enew = 1,
++ .parse = tplg_parse_tokens,
+ },
+ {
+ .name = "tuple",
++ .id = "SectionVendorTuples",
+ .loff = offsetof(snd_tplg_t, tuple_list),
+ .type = SND_TPLG_TYPE_TUPLE,
+ .free = tplg_free_tuples,
+ .enew = 1,
++ .parse = tplg_parse_tuples,
+ },
+ {
+ .name = "hw config",
++ .id = "SectionHWConfig",
+ .loff = offsetof(snd_tplg_t, hw_cfg_list),
+ .type = SND_TPLG_TYPE_HW_CONFIG,
+ .size = sizeof(struct snd_soc_tplg_hw_config),
+ .enew = 1,
++ .parse = tplg_parse_hw_config,
+ }
+ };
+
+diff --git a/src/topology/parser.c b/src/topology/parser.c
+index ed864d3223c4..11202769391c 100644
+--- a/src/topology/parser.c
++++ b/src/topology/parser.c
+@@ -142,87 +142,11 @@ int tplg_parse_compound(snd_tplg_t *tplg, snd_config_t *cfg,
+
+ static int tplg_parse_config(snd_tplg_t *tplg, snd_config_t *cfg)
+ {
+- static struct _parser {
+- const char *id;
+- int (*parser)(snd_tplg_t *tplg, snd_config_t *cfg, void *priv);
+- } *p, parsers[] = {
+- {
+- .id = "SectionTLV",
+- .parser = tplg_parse_tlv
+- },
+- {
+- .id = "SectionControlMixer",
+- .parser = tplg_parse_control_mixer
+- },
+- {
+- .id = "SectionControlEnum",
+- .parser = tplg_parse_control_enum
+- },
+- {
+- .id = "SectionControlBytes",
+- .parser = tplg_parse_control_bytes
+- },
+- {
+- .id = "SectionWidget",
+- .parser = tplg_parse_dapm_widget
+- },
+- {
+- .id = "SectionPCMCapabilities",
+- .parser = tplg_parse_stream_caps
+- },
+- {
+- .id = "SectionPCM",
+- .parser = tplg_parse_pcm
+- },
+- {
+- .id = "SectionDAI",
+- .parser = tplg_parse_dai
+- },
+- {
+- .id = "SectionHWConfig",
+- .parser = tplg_parse_hw_config
+- },
+- {
+- .id = "SectionLink",
+- .parser = tplg_parse_link
+- },
+- {
+- .id = "SectionBE",
+- .parser = tplg_parse_link
+- },
+- {
+- .id = "SectionCC",
+- .parser = tplg_parse_cc
+- },
+- {
+- .id = "SectionGraph",
+- .parser = tplg_parse_dapm_graph
+- },
+- {
+- .id = "SectionText",
+- .parser = tplg_parse_text
+- },
+- {
+- .id = "SectionData",
+- .parser = tplg_parse_data
+- },
+- {
+- .id = "SectionVendorTokens",
+- .parser = tplg_parse_tokens
+- },
+- {
+- .id = "SectionVendorTuples",
+- .parser = tplg_parse_tuples
+- },
+- {
+- .id = "SectionManifest",
+- .parser = tplg_parse_manifest_data
+- },
+- };
+ int (*parser)(snd_tplg_t *tplg, snd_config_t *cfg, void *priv);
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id;
++ struct tplg_table *p;
+ unsigned int idx;
+ int err;
+
+@@ -239,10 +163,14 @@ static int tplg_parse_config(snd_tplg_t *tplg, snd_config_t *cfg)
+ continue;
+
+ parser = NULL;
+- for (idx = 0; idx < ARRAY_SIZE(parsers); idx++) {
+- p = &parsers[idx];
+- if (strcmp(id, p->id) == 0) {
+- parser = p->parser;
++ for (idx = 0; idx < tplg_table_items; idx++) {
++ p = &tplg_table[idx];
++ if (p->id && strcmp(id, p->id) == 0) {
++ parser = p->parse;
++ break;
++ }
++ if (p->id2 && strcmp(id, p->id2) == 0) {
++ parser = p->parse;
+ break;
+ }
+ }
+diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h
+index 0987898f8336..bea88ba35608 100644
+--- a/src/topology/tplg_local.h
++++ b/src/topology/tplg_local.h
+@@ -199,6 +199,8 @@ struct map_elem {
+ /* mapping table */
+ struct tplg_table {
+ const char *name;
++ const char *id;
++ const char *id2;
+ off_t loff;
+ size_t size;
+ int type;
+@@ -206,6 +208,7 @@ struct tplg_table {
+ unsigned build: 1;
+ unsigned enew: 1;
+ void (*free)(void *);
++ int (*parse)(snd_tplg_t *tplg, snd_config_t *cfg, void *priv);
+ };
+
+ extern struct tplg_table tplg_table[];
+--
+2.16.4
+
diff --git a/0043-topology-add-snd_tplg_save.patch b/0043-topology-add-snd_tplg_save.patch
new file mode 100644
index 0000000..5b8eeeb
--- /dev/null
+++ b/0043-topology-add-snd_tplg_save.patch
@@ -0,0 +1,3141 @@
+From aa1bac2d04bd1fb4ccae96a1136e60454298a710 Mon Sep 17 00:00:00 2001
+From: Jaroslav Kysela
+Date: Mon, 16 Dec 2019 14:26:31 +0100
+Subject: [PATCH 43/63] topology: add snd_tplg_save()
+
+Signed-off-by: Jaroslav Kysela
+---
+ include/topology.h | 15 ++
+ src/conf.c | 17 ++
+ src/topology/Makefile.am | 3 +-
+ src/topology/channel.c | 45 ++++
+ src/topology/ctl.c | 201 ++++++++++++++-
+ src/topology/dapm.c | 209 ++++++++++-----
+ src/topology/data.c | 601 ++++++++++++++++++++++++++++++++++---------
+ src/topology/elem.c | 18 ++
+ src/topology/ops.c | 92 +++++++
+ src/topology/parser.c | 4 +
+ src/topology/pcm.c | 505 +++++++++++++++++++++++++++++++-----
+ src/topology/save.c | 632 ++++++++++++++++++++++++++++++++++++++++++++++
+ src/topology/text.c | 19 ++
+ src/topology/tplg_local.h | 58 ++++-
+ 14 files changed, 2162 insertions(+), 257 deletions(-)
+ create mode 100644 src/topology/save.c
+
+diff --git a/include/topology.h b/include/topology.h
+index c9f4ffea27de..69aa5ed733e3 100644
+--- a/include/topology.h
++++ b/include/topology.h
+@@ -1124,6 +1124,21 @@ int snd_tplg_set_manifest_data(snd_tplg_t *tplg, const void *data, int len);
+ */
+ int snd_tplg_set_version(snd_tplg_t *tplg, unsigned int version);
+
++/*
++ * Flags for the snd_tplg_save()
++ */
++#define SND_TPLG_SAVE_SORT (1<<0) /*!< sort identifiers */
++#define SND_TPLG_SAVE_GROUPS (1<<1) /*!< create the structure by group index */
++#define SND_TPLG_SAVE_NOCHECK (1<<16) /*!< unchecked output for debugging */
++
++/**
++ * \brief Save the topology to the text configuration string.
++ * \param tplg Topology instance.
++ * \param dst A pointer to string with result (malloc).
++ * \return Zero on success, otherwise a negative error code
++ */
++int snd_tplg_save(snd_tplg_t *tplg, char **dst, int flags);
++
+ /* \} */
+
+ #ifdef __cplusplus
+diff --git a/src/conf.c b/src/conf.c
+index 3e753b266b8d..c4db9f21a15e 100644
+--- a/src/conf.c
++++ b/src/conf.c
+@@ -874,6 +874,21 @@ static int get_nonwhite(input_t *input)
+ }
+ }
+
++static inline int get_hexachar(input_t *input)
++{
++ int c, num = 0;
++
++ c = get_char(input);
++ if (c >= '0' && c <= '9') num |= (c - '0') << 4;
++ else if (c >= 'a' && c <= 'f') num |= (c - 'a') << 4;
++ else if (c >= 'A' && c <= 'F') num |= (c - 'A') << 4;
++ c = get_char(input);
++ if (c >= '0' && c <= '9') num |= (c - '0') << 0;
++ else if (c >= 'a' && c <= 'f') num |= (c - 'a') << 0;
++ else if (c >= 'A' && c <= 'F') num |= (c - 'A') << 0;
++ return c;
++}
++
+ static int get_quotedchar(input_t *input)
+ {
+ int c;
+@@ -891,6 +906,8 @@ static int get_quotedchar(input_t *input)
+ return '\r';
+ case 'f':
+ return '\f';
++ case 'x':
++ return get_hexachar(input);
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ {
+diff --git a/src/topology/Makefile.am b/src/topology/Makefile.am
+index 9dc472d62fac..a850ec4c195c 100644
+--- a/src/topology/Makefile.am
++++ b/src/topology/Makefile.am
+@@ -27,7 +27,8 @@ libatopology_la_SOURCES =\
+ text.c \
+ channel.c \
+ ops.c \
+- elem.c
++ elem.c \
++ save.c
+
+ noinst_HEADERS = tplg_local.h
+
+diff --git a/src/topology/channel.c b/src/topology/channel.c
+index 4569eb31e0c5..b54a10c89379 100644
+--- a/src/topology/channel.c
++++ b/src/topology/channel.c
+@@ -73,6 +73,18 @@ static int lookup_channel(const char *c)
+ return -EINVAL;
+ }
+
++const char *tplg_channel_name(int type)
++{
++ unsigned int i;
++
++ for (i = 0; i < ARRAY_SIZE(channel_map); i++) {
++ if (channel_map[i].id == type)
++ return channel_map[i].name;
++ }
++
++ return NULL;
++}
++
+ /* Parse a channel mapping. */
+ int tplg_parse_channel(snd_tplg_t *tplg, snd_config_t *cfg,
+ void *private)
+@@ -123,3 +135,36 @@ int tplg_parse_channel(snd_tplg_t *tplg, snd_config_t *cfg,
+ tplg->channel_idx++;
+ return 0;
+ }
++
++int tplg_save_channels(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
++ struct snd_soc_tplg_channel *channel,
++ unsigned int count, char **dst, const char *pfx)
++{
++ struct snd_soc_tplg_channel *c;
++ const char *s;
++ unsigned int index;
++ int err;
++
++ if (count == 0)
++ return 0;
++ err = tplg_save_printf(dst, pfx, "channel {\n");
++ for (index = 0; err >= 0 && index < count; index++) {
++ c = channel + index;
++ s = tplg_channel_name(c->id);
++ if (s == NULL)
++ err = tplg_save_printf(dst, pfx, "\t%u", c->id);
++ else
++ err = tplg_save_printf(dst, pfx, "\t%s", s);
++ if (err >= 0)
++ err = tplg_save_printf(dst, NULL, " {\n");
++ if (err >= 0)
++ err = tplg_save_printf(dst, pfx, "\t\treg %d\n", c->reg);
++ if (err >= 0 && c->shift > 0)
++ err = tplg_save_printf(dst, pfx, "\t\tshift %u\n", c->shift);
++ if (err >= 0)
++ err = tplg_save_printf(dst, pfx, "\t}\n");
++ }
++ if (err >= 0)
++ err = tplg_save_printf(dst, pfx, "}\n");
++ return err;
++}
+diff --git a/src/topology/ctl.c b/src/topology/ctl.c
+index 539329cd661f..979cc1b035f1 100644
+--- a/src/topology/ctl.c
++++ b/src/topology/ctl.c
+@@ -28,15 +28,16 @@ struct ctl_access_elem {
+ };
+
+ /* CTL access strings and codes */
++/* place the multi-bit values on top - like read_write - for save */
+ static const struct ctl_access_elem ctl_access[] = {
++ {"read_write", SNDRV_CTL_ELEM_ACCESS_READWRITE},
++ {"tlv_read_write", SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE},
+ {"read", SNDRV_CTL_ELEM_ACCESS_READ},
+ {"write", SNDRV_CTL_ELEM_ACCESS_WRITE},
+- {"read_write", SNDRV_CTL_ELEM_ACCESS_READWRITE},
+ {"volatile", SNDRV_CTL_ELEM_ACCESS_VOLATILE},
+ {"timestamp", SNDRV_CTL_ELEM_ACCESS_TIMESTAMP},
+ {"tlv_read", SNDRV_CTL_ELEM_ACCESS_TLV_READ},
+ {"tlv_write", SNDRV_CTL_ELEM_ACCESS_TLV_WRITE},
+- {"tlv_read_write", SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE},
+ {"tlv_command", SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND},
+ {"inactive", SNDRV_CTL_ELEM_ACCESS_INACTIVE},
+ {"lock", SNDRV_CTL_ELEM_ACCESS_LOCK},
+@@ -103,6 +104,46 @@ int parse_access(snd_config_t *cfg,
+ return err;
+ }
+
++/* Save Access */
++static int tplg_save_access(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
++ struct snd_soc_tplg_ctl_hdr *hdr, char **dst,
++ const char *pfx)
++{
++ const char *last;
++ unsigned int j, count, access, cval;
++ int err;
++
++ if (hdr->access == 0)
++ return 0;
++
++ access = hdr->access;
++ for (j = 0, count = 0, last = NULL; j < ARRAY_SIZE(ctl_access); j++) {
++ cval = ctl_access[j].value;
++ if ((access & cval) == cval) {
++ access &= ~cval;
++ last = ctl_access[j].name;
++ count++;
++ }
++ }
++ if (count == 1)
++ return tplg_save_printf(dst, pfx, "access.0 %s\n", last);
++ err = tplg_save_printf(dst, pfx, "access [\n");
++ if (err < 0)
++ return err;
++ access = hdr->access;
++ for (j = 0; j < ARRAY_SIZE(ctl_access); j++) {
++ cval = ctl_access[j].value;
++ if ((access & cval) == cval) {
++ err = tplg_save_printf(dst, pfx, "\t%s\n",
++ ctl_access[j].name);
++ if (err < 0)
++ return err;
++ access &= ~cval;
++ }
++ }
++ return tplg_save_printf(dst, pfx, "]\n");
++}
++
+ /* copy referenced TLV to the mixer control */
+ static int copy_tlv(struct tplg_elem *elem, struct tplg_elem *ref)
+ {
+@@ -358,6 +399,37 @@ int tplg_parse_tlv(snd_tplg_t *tplg, snd_config_t *cfg,
+ return err;
+ }
+
++/* save TLV data */
++int tplg_save_tlv(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
++ struct tplg_elem *elem,
++ char **dst, const char *pfx)
++{
++ struct snd_soc_tplg_ctl_tlv *tlv = elem->tlv;
++ struct snd_soc_tplg_tlv_dbscale *scale;
++ int err;
++
++ if (tlv->type != SNDRV_CTL_TLVT_DB_SCALE) {
++ SNDERR("unknown TLV type");
++ return -EINVAL;
++ }
++
++ scale = &tlv->scale;
++ err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
++ if (err >= 0)
++ err = tplg_save_printf(dst, pfx, "\tscale {\n");
++ if (err >= 0 && scale->min)
++ err = tplg_save_printf(dst, pfx, "\t\tmin %i\n", scale->min);
++ if (err >= 0 && scale->step > 0)
++ err = tplg_save_printf(dst, pfx, "\t\tstep %i\n", scale->step);
++ if (err >= 0 && scale->mute > 0)
++ err = tplg_save_printf(dst, pfx, "\t\tmute %i\n", scale->mute);
++ if (err >= 0)
++ err = tplg_save_printf(dst, pfx, "\t}\n");
++ if (err >= 0)
++ err = tplg_save_printf(dst, pfx, "}\n");
++ return err;
++}
++
+ /* Parse Control Bytes */
+ int tplg_parse_control_bytes(snd_tplg_t *tplg,
+ snd_config_t *cfg,
+@@ -430,7 +502,7 @@ int tplg_parse_control_bytes(snd_tplg_t *tplg,
+ }
+
+ if (strcmp(id, "data") == 0) {
+- err = tplg_parse_data_refs(n, elem);
++ err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA);
+ if (err < 0)
+ return err;
+ continue;
+@@ -485,6 +557,49 @@ int tplg_parse_control_bytes(snd_tplg_t *tplg,
+ return 0;
+ }
+
++/* save control bytes */
++int tplg_save_control_bytes(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
++ struct tplg_elem *elem,
++ char **dst, const char *pfx)
++{
++ struct snd_soc_tplg_bytes_control *be = elem->bytes_ext;
++ char pfx2[16];
++ int err;
++
++ if (!be)
++ return 0;
++
++ snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: "");
++ err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
++ if (err < 0)
++ return err;
++ if (err >= 0 && elem->index > 0)
++ err = tplg_save_printf(dst, pfx, "\tindex %u\n", elem->index);
++ if (err >= 0 && be->base > 0)
++ err = tplg_save_printf(dst, pfx, "\tbase %u\n", be->base);
++ if (err >= 0 && be->num_regs > 0)
++ err = tplg_save_printf(dst, pfx, "\tnum_regs %u\n", be->num_regs);
++ if (err >= 0 && be->max > 0)
++ err = tplg_save_printf(dst, pfx, "\tmax %u\n", be->max);
++ if (err >= 0 && be->mask > 0)
++ err = tplg_save_printf(dst, pfx, "\tmask %u\n", be->mask);
++ if (err >= 0)
++ err = tplg_save_ops(tplg, &be->hdr, dst, pfx2);
++ if (err >= 0)
++ err = tplg_save_ext_ops(tplg, be, dst, pfx2);
++ if (err >= 0)
++ err = tplg_save_access(tplg, &be->hdr, dst, pfx2);
++ if (err >= 0)
++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_TLV,
++ "tlv", dst, pfx2);
++ if (err >= 0)
++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_DATA,
++ "data", dst, pfx2);
++ if (err >= 0)
++ err = tplg_save_printf(dst, pfx, "}\n");
++ return err;
++}
++
+ /* Parse Control Enums. */
+ int tplg_parse_control_enum(snd_tplg_t *tplg, snd_config_t *cfg,
+ void *private ATTRIBUTE_UNUSED)
+@@ -559,7 +674,7 @@ int tplg_parse_control_enum(snd_tplg_t *tplg, snd_config_t *cfg,
+ }
+
+ if (strcmp(id, "data") == 0) {
+- err = tplg_parse_data_refs(n, elem);
++ err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA);
+ if (err < 0)
+ return err;
+ continue;
+@@ -582,6 +697,42 @@ int tplg_parse_control_enum(snd_tplg_t *tplg, snd_config_t *cfg,
+ return 0;
+ }
+
++/* save control eunm */
++int tplg_save_control_enum(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
++ struct tplg_elem *elem,
++ char **dst, const char *pfx)
++{
++ struct snd_soc_tplg_enum_control *ec = elem->enum_ctrl;
++ char pfx2[16];
++ int err;
++
++ if (!ec)
++ return 0;
++
++ snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: "");
++ err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
++ if (err < 0)
++ return err;
++ if (err >= 0 && elem->index > 0)
++ err = tplg_save_printf(dst, pfx, "\tindex %u\n", elem->index);
++ if (err >= 0)
++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_TEXT,
++ "texts", dst, pfx2);
++ if (err >= 0)
++ err = tplg_save_channels(tplg, ec->channel, ec->num_channels,
++ dst, pfx2);
++ if (err >= 0)
++ err = tplg_save_ops(tplg, &ec->hdr, dst, pfx2);
++ if (err >= 0)
++ err = tplg_save_access(tplg, &ec->hdr, dst, pfx2);
++ if (err >= 0)
++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_DATA,
++ "data", dst, pfx2);
++ if (err >= 0)
++ err = tplg_save_printf(dst, pfx, "}\n");
++ return err;
++}
++
+ /* Parse Controls.
+ *
+ * Mixer control. Supports multiple channels.
+@@ -683,7 +834,7 @@ int tplg_parse_control_mixer(snd_tplg_t *tplg,
+ }
+
+ if (strcmp(id, "data") == 0) {
+- err = tplg_parse_data_refs(n, elem);
++ err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA);
+ if (err < 0)
+ return err;
+ continue;
+@@ -709,6 +860,46 @@ int tplg_parse_control_mixer(snd_tplg_t *tplg,
+ return 0;
+ }
+
++int tplg_save_control_mixer(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
++ struct tplg_elem *elem, char **dst,
++ const char *pfx)
++{
++ struct snd_soc_tplg_mixer_control *mc = elem->mixer_ctrl;
++ char pfx2[16];
++ int err;
++
++ if (!mc)
++ return 0;
++ err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
++ if (err < 0)
++ return err;
++ snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: "");
++ if (err >= 0 && elem->index > 0)
++ err = tplg_save_printf(dst, pfx, "\tindex %u\n", elem->index);
++ if (err >= 0)
++ err = tplg_save_channels(tplg, mc->channel, mc->num_channels,
++ dst, pfx2);
++ if (err >= 0 && mc->max > 0)
++ err = tplg_save_printf(dst, pfx, "\tmax %u\n", mc->max);
++ if (err >= 0 && mc->invert > 0)
++ err = tplg_save_printf(dst, pfx, "\tinvert 1\n", mc->max);
++ if (err >= 0 && mc->invert > 0)
++ err = tplg_save_printf(dst, pfx, "\tinvert 1\n", mc->max);
++ if (err >= 0)
++ err = tplg_save_ops(tplg, &mc->hdr, dst, pfx2);
++ if (err >= 0)
++ err = tplg_save_access(tplg, &mc->hdr, dst, pfx2);
++ if (err >= 0)
++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_TLV,
++ "tlv", dst, pfx2);
++ if (err >= 0)
++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_DATA,
++ "data", dst, pfx2);
++ if (err >= 0)
++ err = tplg_save_printf(dst, pfx, "}\n");
++ return err;
++}
++
+ static int init_ctl_hdr(struct snd_soc_tplg_ctl_hdr *hdr,
+ struct snd_tplg_ctl_template *t)
+ {
+diff --git a/src/topology/dapm.c b/src/topology/dapm.c
+index ad7092107896..2bdacedca125 100644
+--- a/src/topology/dapm.c
++++ b/src/topology/dapm.c
+@@ -43,7 +43,6 @@ static const struct map_elem widget_map[] = {
+ {"effect", SND_SOC_TPLG_DAPM_EFFECT},
+ {"siggen", SND_SOC_TPLG_DAPM_SIGGEN},
+ {"src", SND_SOC_TPLG_DAPM_SRC},
+- {"asrc", SND_SOC_TPLG_DAPM_ASRC},
+ {"encoder", SND_SOC_TPLG_DAPM_ENCODER},
+ {"decoder", SND_SOC_TPLG_DAPM_DECODER},
+ };
+@@ -60,70 +59,16 @@ static int lookup_widget(const char *w)
+ return -EINVAL;
+ }
+
+-static int tplg_parse_dapm_mixers(snd_config_t *cfg, struct tplg_elem *elem)
++static const char *get_widget_name(unsigned int type)
+ {
+- snd_config_iterator_t i, next;
+- snd_config_t *n;
+- const char *value = NULL;
+-
+- tplg_dbg(" DAPM Mixer Controls: %s\n", elem->id);
+-
+- snd_config_for_each(i, next, cfg) {
+- n = snd_config_iterator_entry(i);
+-
+- /* get value */
+- if (snd_config_get_string(n, &value) < 0)
+- continue;
+-
+- tplg_ref_add(elem, SND_TPLG_TYPE_MIXER, value);
+- tplg_dbg("\t\t %s\n", value);
+- }
+-
+- return 0;
+-}
+-
+-static int tplg_parse_dapm_enums(snd_config_t *cfg, struct tplg_elem *elem)
+-{
+- snd_config_iterator_t i, next;
+- snd_config_t *n;
+- const char *value = NULL;
+-
+- tplg_dbg(" DAPM Enum Controls: %s\n", elem->id);
+-
+- snd_config_for_each(i, next, cfg) {
+- n = snd_config_iterator_entry(i);
+-
+- /* get value */
+- if (snd_config_get_string(n, &value) < 0)
+- continue;
+-
+- tplg_ref_add(elem, SND_TPLG_TYPE_ENUM, value);
+- tplg_dbg("\t\t %s\n", value);
+- }
+-
+- return 0;
+-}
+-
+-static int tplg_parse_dapm_bytes(snd_config_t *cfg, struct tplg_elem *elem)
+-{
+- snd_config_iterator_t i, next;
+- snd_config_t *n;
+- const char *value = NULL;
+-
+- tplg_dbg(" DAPM Bytes Controls: %s\n", elem->id);
+-
+- snd_config_for_each(i, next, cfg) {
+- n = snd_config_iterator_entry(i);
+-
+- /* get value */
+- if (snd_config_get_string(n, &value) < 0)
+- continue;
++ unsigned int i;
+
+- tplg_ref_add(elem, SND_TPLG_TYPE_BYTES, value);
+- tplg_dbg("\t\t %s\n", value);
++ for (i = 0; i < ARRAY_SIZE(widget_map); i++) {
++ if ((unsigned int)widget_map[i].id == type)
++ return widget_map[i].name;
+ }
+
+- return 0;
++ return NULL;
+ }
+
+ /* move referenced controls to the widget */
+@@ -340,7 +285,7 @@ struct tplg_elem *tplg_elem_new_route(snd_tplg_t *tplg, int index)
+
+ #define LINE_SIZE 1024
+
+-/* line is defined as '"source, control, sink"' */
++/* line is defined as '"sink, control, source"' */
+ static int tplg_parse_line(const char *text,
+ struct snd_soc_tplg_dapm_graph_elem *line)
+ {
+@@ -470,6 +415,77 @@ int tplg_parse_dapm_graph(snd_tplg_t *tplg, snd_config_t *cfg,
+ return 0;
+ }
+
++/* save DAPM graph */
++int tplg_save_dapm_graph(snd_tplg_t *tplg, int index, char **dst, const char *pfx)
++{
++ struct snd_soc_tplg_dapm_graph_elem *route;
++ struct list_head *pos;
++ struct tplg_elem *elem;
++ int err, first = 1, old_index = -1;
++ unsigned block = -1, count = 0;
++
++ list_for_each(pos, &tplg->route_list) {
++ elem = list_entry(pos, struct tplg_elem, list);
++ if (!elem->route || elem->type != SND_TPLG_TYPE_DAPM_GRAPH)
++ continue;
++ if (index >= 0 && elem->index != index)
++ continue;
++ count++;
++ }
++ if (count == 0)
++ return 0;
++ err = tplg_save_printf(dst, pfx, "SectionGraph {\n");
++ list_for_each(pos, &tplg->route_list) {
++ elem = list_entry(pos, struct tplg_elem, list);
++ if (!elem->route || elem->type != SND_TPLG_TYPE_DAPM_GRAPH)
++ continue;
++ if (index >= 0 && elem->index != index)
++ continue;
++ if (old_index != elem->index) {
++ if (old_index >= 0) {
++ err = tplg_save_printf(dst, pfx, "\t\t]\n");
++ if (err < 0)
++ return err;
++ err = tplg_save_printf(dst, pfx, "\t}\n");
++ if (err < 0)
++ return err;
++ }
++ old_index = elem->index;
++ block++;
++ first = 1;
++ err = tplg_save_printf(dst, pfx, "\tset%u {\n", block);
++ if (err >= 0)
++ err = tplg_save_printf(dst, pfx, "\t\tindex %u\n",
++ elem->index);
++ if (err < 0)
++ return err;
++ }
++ if (first) {
++ first = 0;
++ err = tplg_save_printf(dst, pfx, "\t\tlines [\n", elem->index);
++ if (err < 0)
++ return err;
++ }
++ route = elem->route;
++ err = tplg_save_printf(dst, pfx, "\t\t\t'%s, %s, %s'\n",
++ route->sink, route->control,
++ route->source);
++ if (err < 0)
++ return err;
++ }
++
++ if (!first) {
++ if (err >= 0)
++ err = tplg_save_printf(dst, pfx, "\t\t]\n");
++ if (err >= 0)
++ err = tplg_save_printf(dst, pfx, "\t}\n");
++ }
++
++ if (err >= 0)
++ err = tplg_save_printf(dst, pfx, "}\n");
++ return err;
++}
++
+ /* DAPM Widget */
+ int tplg_parse_dapm_widget(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
+@@ -595,7 +611,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg,
+ }
+
+ if (strcmp(id, "enum") == 0) {
+- err = tplg_parse_dapm_enums(n, elem);
++ err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_ENUM);
+ if (err < 0)
+ return err;
+
+@@ -603,7 +619,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg,
+ }
+
+ if (strcmp(id, "mixer") == 0) {
+- err = tplg_parse_dapm_mixers(n, elem);
++ err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_MIXER);
+ if (err < 0)
+ return err;
+
+@@ -611,7 +627,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg,
+ }
+
+ if (strcmp(id, "bytes") == 0) {
+- err = tplg_parse_dapm_bytes(n, elem);
++ err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_BYTES);
+ if (err < 0)
+ return err;
+
+@@ -619,7 +635,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg,
+ }
+
+ if (strcmp(id, "data") == 0) {
+- err = tplg_parse_data_refs(n, elem);
++ err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA);
+ if (err < 0)
+ return err;
+ continue;
+@@ -629,6 +645,66 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg,
+ return 0;
+ }
+
++/* save DAPM widget */
++int tplg_save_dapm_widget(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
++ struct tplg_elem *elem,
++ char **dst, const char *pfx)
++{
++ struct snd_soc_tplg_dapm_widget *widget = elem->widget;
++ const char *s;
++ char pfx2[16];
++ int err;
++
++ err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
++ if (err >= 0 && elem->index)
++ err = tplg_save_printf(dst, pfx, "\tindex %u\n",
++ elem->index);
++ if (err >= 0) {
++ s = get_widget_name(widget->id);
++ if (s)
++ err = tplg_save_printf(dst, pfx, "\ttype %s\n", s);
++ else
++ err = tplg_save_printf(dst, pfx, "\ttype %u\n",
++ widget->id);
++ }
++ if (err >= 0 && widget->sname[0])
++ err = tplg_save_printf(dst, pfx, "\tstream_name '%s'\n",
++ widget->sname);
++ if (err >= 0 && widget->reg)
++ err = tplg_save_printf(dst, pfx, "\tno_pm 1\n");
++ if (err >= 0 && widget->shift)
++ err = tplg_save_printf(dst, pfx, "\tshift %u\n",
++ widget->shift);
++ if (err >= 0 && widget->invert)
++ err = tplg_save_printf(dst, pfx, "\tinvert %u\n",
++ widget->invert);
++ if (err >= 0 && widget->subseq)
++ err = tplg_save_printf(dst, pfx, "\tsubseq %u\n",
++ widget->subseq);
++ if (err >= 0 && widget->event_type)
++ err = tplg_save_printf(dst, pfx, "\tevent_type %u\n",
++ widget->event_type);
++ if (err >= 0 && widget->event_flags)
++ err = tplg_save_printf(dst, pfx, "\tevent_flags %u\n",
++ widget->event_flags);
++ snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: "");
++ if (err >= 0)
++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_ENUM,
++ "enum", dst, pfx2);
++ if (err >= 0)
++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_MIXER,
++ "mixer", dst, pfx2);
++ if (err >= 0)
++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_BYTES,
++ "bytes", dst, pfx2);
++ if (err >= 0)
++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_DATA,
++ "data", dst, pfx2);
++ if (err >= 0)
++ err = tplg_save_printf(dst, pfx, "}\n");
++ return err;
++}
++
+ int tplg_add_route(snd_tplg_t *tplg, struct snd_tplg_graph_elem *t, int index)
+ {
+ struct tplg_elem *elem;
+@@ -744,7 +820,6 @@ int tplg_add_widget_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
+ default:
+ SNDERR("error: widget %s: invalid type %d for ctl %d\n",
+ wt->name, ct->type, i);
+- ret = -EINVAL;
+ break;
+ }
+
+diff --git a/src/topology/data.c b/src/topology/data.c
+index 9807445e8c37..11cd73f5ed6e 100644
+--- a/src/topology/data.c
++++ b/src/topology/data.c
+@@ -21,6 +21,10 @@
+ #include "tplg_local.h"
+ #include
+
++#define UUID_FORMAT "\
++0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, \
++0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x"
++
+ /* Get private data buffer of an element */
+ struct snd_soc_tplg_private *get_priv_data(struct tplg_elem *elem)
+ {
+@@ -64,6 +68,96 @@ struct snd_soc_tplg_private *get_priv_data(struct tplg_elem *elem)
+ return priv;
+ }
+
++/* Parse references for the element, either a single data section
++ * or a list of data sections.
++ */
++int tplg_parse_refs(snd_config_t *cfg, struct tplg_elem *elem,
++ unsigned int type)
++{
++ snd_config_type_t cfg_type;
++ snd_config_iterator_t i, next;
++ snd_config_t *n;
++ const char *val = NULL;
++ int err, count;
++
++ cfg_type = snd_config_get_type(cfg);
++
++ /* refer to a single data section */
++ if (cfg_type == SND_CONFIG_TYPE_STRING) {
++ if (snd_config_get_string(cfg, &val) < 0)
++ return -EINVAL;
++
++ tplg_dbg("\tref data: %s\n", val);
++ err = tplg_ref_add(elem, type, val);
++ if (err < 0)
++ return err;
++ return 1;
++ }
++
++ if (cfg_type != SND_CONFIG_TYPE_COMPOUND) {
++ SNDERR("error: compound type expected for %s", elem->id);
++ return -EINVAL;
++ }
++
++ /* refer to a list of data sections */
++ count = 0;
++ snd_config_for_each(i, next, cfg) {
++ const char *val;
++
++ n = snd_config_iterator_entry(i);
++ if (snd_config_get_string(n, &val) < 0)
++ continue;
++
++ tplg_dbg("\tref data: %s\n", val);
++ err = tplg_ref_add(elem, type, val);
++ if (err < 0)
++ return err;
++ count++;
++ }
++
++ return count;
++}
++
++/* save references */
++int tplg_save_refs(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
++ struct tplg_elem *elem, unsigned int type,
++ const char *id, char **dst, const char *pfx)
++{
++ struct tplg_ref *ref, *last;
++ struct list_head *pos;
++ int err, count;
++
++ count = 0;
++ last = NULL;
++ list_for_each(pos, &elem->ref_list) {
++ ref = list_entry(pos, struct tplg_ref, list);
++ if (ref->type == type) {
++ last = ref;
++ count++;
++ }
++ }
++
++ if (count == 0)
++ return 0;
++
++ if (count == 1)
++ return tplg_save_printf(dst, pfx, "%s '%s'\n", id, last->id);
++
++ err = tplg_save_printf(dst, pfx, "%s [\n", id);
++ if (err < 0)
++ return err;
++ list_for_each(pos, &elem->ref_list) {
++ ref = list_entry(pos, struct tplg_ref, list);
++ if (ref->type == type) {
++ err = tplg_save_printf(dst, pfx, "\t'%s'\n", ref->id);
++ if (err < 0)
++ return err;
++ }
++ }
++
++ return tplg_save_printf(dst, pfx, "]\n");
++}
++
+ /* Get Private data from a file. */
+ static int tplg_parse_data_file(snd_config_t *cfg, struct tplg_elem *elem)
+ {
+@@ -140,58 +234,98 @@ err:
+ static void dump_priv_data(struct tplg_elem *elem)
+ {
+ struct snd_soc_tplg_private *priv = elem->data;
+- unsigned int i, j = 0;
++ unsigned int i;
+
+ tplg_dbg(" elem size = %d, priv data size = %d\n",
+ elem->size, priv->size);
+
+ for (i = 0; i < priv->size; i++) {
+- if (j++ % 8 == 0)
++ if (i > 0 && (i % 16) == 0)
+ tplg_dbg("\n");
+
+- tplg_dbg(" 0x%x", *p++);
++ tplg_dbg(" %02x:", *p++);
+ }
+
+ tplg_dbg("\n\n");
+ }
+
++static inline int check_nibble(unsigned char c)
++{
++ return (c >= '0' && c <= '9') ||
++ (c >= 'a' && c <= 'f') ||
++ (c >= 'A' && c <= 'F');
++}
++
+ /* get number of hex value elements in CSV list */
+ static int get_hex_num(const char *str)
+ {
+- int commas = 0, values = 0, len = strlen(str);
+- const char *end = str + len;
++ int delims, values, len = strlen(str);
++ const char *s, *end = str + len;
++
++ /* check "aa:bb:00" syntax */
++ s = str;
++ delims = values = 0;
++ while (s < end) {
++ /* skip white space */
++ if (isspace(*s)) {
++ s++;
++ continue;
++ }
++ /* find delimeters */
++ if (*s == ':') {
++ delims++;
++ s++;
++ continue;
++ }
++ /* check 00 hexadecimal value */
++ if (s + 1 <= end) {
++ if (check_nibble(s[0]) && check_nibble(s[1])) {
++ values++;
++ } else {
++ goto format2;
++ }
++ s++;
++ }
++ s++;
++ }
++ goto end;
+
++format2:
+ /* we expect "0x0, 0x0, 0x0" */
+- while (str < end) {
++ s = str;
++ delims = values = 0;
++ while (s < end) {
+
+ /* skip white space */
+- if (isspace(*str)) {
+- str++;
++ if (isspace(*s)) {
++ s++;
+ continue;
+ }
+
+ /* find delimeters */
+- if (*str == ',') {
+- commas++;
+- str++;
++ if (*s == ',') {
++ delims++;
++ s++;
+ continue;
+ }
+
+ /* find 0x[0-9] values */
+- if (*str == '0' && str + 2 <= end) {
+- if (str[1] == 'x' && str[2] >= '0' && str[2] <= 'f') {
++ if (*s == '0' && s + 2 <= end) {
++ if (s[1] == 'x' && check_nibble(s[2])) {
++ if (check_nibble(s[3]))
++ s++;
+ values++;
+- str += 3;
+- } else {
+- str++;
++ s += 2;
+ }
++ s++;
+ }
+
+- str++;
++ s++;
+ }
+
++end:
+ /* there should always be one less comma than value */
+- if (values -1 != commas)
++ if (values - 1 != delims)
+ return -EINVAL;
+
+ return values;
+@@ -547,6 +681,71 @@ static int build_tuples(snd_tplg_t *tplg, struct tplg_elem *elem)
+ return 0;
+ }
+
++struct tuple_type {
++ unsigned int type;
++ const char *name;
++ unsigned int size;
++};
++
++static struct tuple_type tuple_types[] = {
++ {
++ .type = SND_SOC_TPLG_TUPLE_TYPE_UUID,
++ .name = "uuid",
++ .size = 4,
++ },
++ {
++ .type = SND_SOC_TPLG_TUPLE_TYPE_STRING,
++ .name = "string",
++ .size = 6,
++ },
++ {
++ .type = SND_SOC_TPLG_TUPLE_TYPE_BOOL,
++ .name = "bool",
++ .size = 4,
++ },
++ {
++ .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
++ .name = "byte",
++ .size = 4,
++ },
++ {
++ .type = SND_SOC_TPLG_TUPLE_TYPE_SHORT,
++ .name = "short",
++ .size = 5,
++ },
++ {
++ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
++ .name = "word",
++ .size = 4
++ },
++};
++
++static int get_tuple_type(const char *name)
++{
++ struct tuple_type *t;
++ unsigned int i;
++
++ /* skip initial index for sorting */
++ while ((*name >= '0' && *name <= '9') || *name == '_')
++ name++;
++ for (i = 0; i < ARRAY_SIZE(tuple_types); i++) {
++ t = &tuple_types[i];
++ if (strncasecmp(t->name, name, t->size) == 0)
++ return t->type;
++ }
++ return -EINVAL;
++}
++
++static const char *get_tuple_type_name(unsigned int type)
++{
++ unsigned int i;
++
++ for (i = 0; i < ARRAY_SIZE(tuple_types); i++)
++ if (tuple_types[i].type == type)
++ return tuple_types[i].name;
++ return NULL;
++}
++
+ static int parse_tuple_set(snd_config_t *cfg,
+ struct tplg_tuple_set **s)
+ {
+@@ -554,28 +753,17 @@ static int parse_tuple_set(snd_config_t *cfg,
+ snd_config_t *n;
+ const char *id, *value;
+ struct tplg_tuple_set *set;
+- unsigned int type, num_tuples = 0;
++ unsigned int num_tuples = 0;
+ struct tplg_tuple *tuple;
+ unsigned int tuple_val;
+- int ival;
++ int type, ival;
+
+ snd_config_get_id(cfg, &id);
+
+- if (strncmp(id, "uuid", 4) == 0)
+- type = SND_SOC_TPLG_TUPLE_TYPE_UUID;
+- else if (strncmp(id, "string", 5) == 0)
+- type = SND_SOC_TPLG_TUPLE_TYPE_STRING;
+- else if (strncmp(id, "bool", 4) == 0)
+- type = SND_SOC_TPLG_TUPLE_TYPE_BOOL;
+- else if (strncmp(id, "byte", 4) == 0)
+- type = SND_SOC_TPLG_TUPLE_TYPE_BYTE;
+- else if (strncmp(id, "short", 5) == 0)
+- type = SND_SOC_TPLG_TUPLE_TYPE_SHORT;
+- else if (strncmp(id, "word", 4) == 0)
+- type = SND_SOC_TPLG_TUPLE_TYPE_WORD;
+- else {
+- SNDERR("error: invalid tuple type '%s'\n", id);
+- return -EINVAL;
++ type = get_tuple_type(id);
++ if (type < 0) {
++ SNDERR("error: invalid tuple type '%s'", id);
++ return type;
+ }
+
+ snd_config_for_each(i, next, cfg)
+@@ -664,6 +852,84 @@ err:
+ return -EINVAL;
+ }
+
++/* save tuple set */
++static int tplg_save_tuple_set(struct tplg_vendor_tuples *tuples,
++ unsigned int set_index,
++ char **dst, const char *pfx)
++{
++ struct tplg_tuple_set *set;
++ struct tplg_tuple *tuple;
++ const char *s, *fmt;
++ char buf[32];
++ unsigned int i;
++ int err;
++
++ set = tuples->set[set_index];
++ if (set->num_tuples == 0)
++ return 0;
++ s = get_tuple_type_name(set->type);
++ if (s == NULL)
++ return -EINVAL;
++ if (tuples->num_sets < 10)
++ fmt = "%u_";
++ else if (tuples->num_sets < 100)
++ fmt = "%02u_";
++ else if (tuples->num_sets < 1000)
++ fmt = "%03u_";
++ else
++ return -EINVAL;
++ if (set->num_tuples > 1) {
++ snprintf(buf, sizeof(buf), "tuples.%s%%s {\n", fmt);
++ err = tplg_save_printf(dst, NULL, buf, set_index, s);
++ if (err < 0)
++ return err;
++ }
++ for (i = 0; i < set->num_tuples; i++) {
++ tuple = &set->tuple[i];
++ if (set->num_tuples == 1) {
++ snprintf(buf, sizeof(buf), "tuples.%s%%s.'%%s' ", fmt);
++ err = tplg_save_printf(dst, NULL, buf,
++ set_index, s, tuple->token);
++ } else {
++ err = tplg_save_printf(dst, pfx, "\t'%s' ",
++ tuple->token);
++ }
++ switch (set->type) {
++ case SND_SOC_TPLG_TUPLE_TYPE_UUID:
++ err = tplg_save_printf(dst, NULL, "'" UUID_FORMAT "'\n",
++ tuple->uuid[0], tuple->uuid[1],
++ tuple->uuid[2], tuple->uuid[3],
++ tuple->uuid[4], tuple->uuid[5],
++ tuple->uuid[6], tuple->uuid[7],
++ tuple->uuid[8], tuple->uuid[9],
++ tuple->uuid[10], tuple->uuid[11],
++ tuple->uuid[12], tuple->uuid[13],
++ tuple->uuid[14], tuple->uuid[15]);
++ break;
++ case SND_SOC_TPLG_TUPLE_TYPE_STRING:
++ err = tplg_save_printf(dst, NULL, "'%s'\n",
++ tuple->string);
++ break;
++ case SND_SOC_TPLG_TUPLE_TYPE_BOOL:
++ case SND_SOC_TPLG_TUPLE_TYPE_BYTE:
++ case SND_SOC_TPLG_TUPLE_TYPE_SHORT:
++ err = tplg_save_printf(dst, NULL, "%u\n", tuple->value);
++ break;
++ case SND_SOC_TPLG_TUPLE_TYPE_WORD:
++ tplg_nice_value_format(buf, sizeof(buf), tuple->value);
++ err = tplg_save_printf(dst, NULL, "%s\n", buf);
++ break;
++ default:
++ return -EINVAL;
++ }
++ if (err < 0)
++ return err;
++ }
++ if (set->num_tuples > 1)
++ return tplg_save_printf(dst, pfx, "}\n");
++ return 0;
++}
++
+ static int parse_tuple_sets(snd_config_t *cfg,
+ struct tplg_vendor_tuples *tuples)
+ {
+@@ -710,87 +976,24 @@ static int parse_tuple_sets(snd_config_t *cfg,
+ return 0;
+ }
+
+-/* Parse tuples references for a data element, either a single tuples section
+- * or a list of tuples sections.
+- */
+-static int parse_tuples_refs(snd_config_t *cfg,
+- struct tplg_elem *elem)
+-{
+- snd_config_type_t type;
+- snd_config_iterator_t i, next;
+- snd_config_t *n;
+- const char *val = NULL;
+-
+- type = snd_config_get_type(cfg);
+-
+- /* refer to a single tuples section */
+- if (type == SND_CONFIG_TYPE_STRING) {
+- if (snd_config_get_string(cfg, &val) < 0)
+- return -EINVAL;
+- tplg_dbg("\ttuples: %s\n", val);
+- return tplg_ref_add(elem, SND_TPLG_TYPE_TUPLE, val);
+- }
+-
+- if (type != SND_CONFIG_TYPE_COMPOUND) {
+- SNDERR("error: compound type expected for %s", elem->id);
+- return -EINVAL;
+- }
+-
+- /* refer to a list of data sections */
+- snd_config_for_each(i, next, cfg) {
+- const char *val;
+-
+- n = snd_config_iterator_entry(i);
+- if (snd_config_get_string(n, &val) < 0)
+- continue;
+-
+- tplg_dbg("\ttuples: %s\n", val);
+- tplg_ref_add(elem, SND_TPLG_TYPE_TUPLE, val);
+- }
+-
+- return 0;
+-}
+-
+-/* Parse private data references for the element, either a single data section
+- * or a list of data sections.
+- */
+-int tplg_parse_data_refs(snd_config_t *cfg,
+- struct tplg_elem *elem)
++/* save tuple sets */
++int tplg_save_tuple_sets(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
++ struct tplg_elem *elem,
++ char **dst, const char *pfx)
+ {
+- snd_config_type_t type;
+- snd_config_iterator_t i, next;
+- snd_config_t *n;
+- const char *val = NULL;
+-
+- type = snd_config_get_type(cfg);
+-
+- /* refer to a single data section */
+- if (type == SND_CONFIG_TYPE_STRING) {
+- if (snd_config_get_string(cfg, &val) < 0)
+- return -EINVAL;
+-
+- tplg_dbg("\tdata: %s\n", val);
+- return tplg_ref_add(elem, SND_TPLG_TYPE_DATA, val);
+- }
+-
+- if (type != SND_CONFIG_TYPE_COMPOUND) {
+- SNDERR("error: compound type expected for %s", elem->id);
+- return -EINVAL;
+- }
+-
+- /* refer to a list of data sections */
+- snd_config_for_each(i, next, cfg) {
+- const char *val;
+-
+- n = snd_config_iterator_entry(i);
+- if (snd_config_get_string(n, &val) < 0)
+- continue;
++ struct tplg_vendor_tuples *tuples = elem->tuples;
++ unsigned int i;
++ int err = 0;
+
+- tplg_dbg("\tdata: %s\n", val);
+- tplg_ref_add(elem, SND_TPLG_TYPE_DATA, val);
++ for (i = 0; i < tuples->num_sets; i++) {
++ err = tplg_save_printf(dst, pfx, "");
++ if (err < 0)
++ break;
++ err = tplg_save_tuple_set(tuples, i, dst, pfx);
++ if (err < 0)
++ break;
+ }
+-
+- return 0;
++ return err;
+ }
+
+ /* Parse vendor tokens
+@@ -844,6 +1047,31 @@ int tplg_parse_tokens(snd_tplg_t *tplg, snd_config_t *cfg,
+ return 0;
+ }
+
++/* save vendor tokens */
++int tplg_save_tokens(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
++ struct tplg_elem *elem,
++ char **dst, const char *pfx)
++{
++ struct tplg_vendor_tokens *tokens = elem->tokens;
++ unsigned int i;
++ int err;
++
++ if (!tokens || tokens->num_tokens == 0)
++ return 0;
++
++ err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
++ if (err < 0)
++ return err;
++ for (i = 0; err >= 0 && i < tokens->num_tokens; i++)
++ err = tplg_save_printf(dst, pfx, "\t'%s' %u\n",
++ tokens->token[i].id,
++ tokens->token[i].value);
++ err = tplg_save_printf(dst, pfx, "}\n");
++ if (err < 0)
++ return err;
++ return 0;
++}
++
+ /* Parse vendor tuples.
+ */
+ int tplg_parse_tuples(snd_tplg_t *tplg, snd_config_t *cfg,
+@@ -890,6 +1118,29 @@ int tplg_parse_tuples(snd_tplg_t *tplg, snd_config_t *cfg,
+ return 0;
+ }
+
++/* save vendor tuples */
++int tplg_save_tuples(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
++ struct tplg_elem *elem,
++ char **dst, const char *pfx)
++{
++ char pfx2[16];
++ int err;
++
++ if (!elem->tuples)
++ return 0;
++
++ err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
++ snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: "");
++ if (err >= 0)
++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_TOKEN,
++ "tokens", dst, pfx2);
++ if (err >= 0)
++ err = tplg_save_tuple_sets(tplg, elem, dst, pfx2);
++ if (err >= 0)
++ err = tplg_save_printf(dst, pfx, "}\n");
++ return 0;
++}
++
+ /* Free handler of tuples */
+ void tplg_free_tuples(void *obj)
+ {
+@@ -944,7 +1195,7 @@ int tplg_parse_manifest_data(snd_tplg_t *tplg, snd_config_t *cfg,
+
+
+ if (strcmp(id, "data") == 0) {
+- err = tplg_parse_data_refs(n, elem);
++ err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA);
+ if (err < 0)
+ return err;
+ continue;
+@@ -954,6 +1205,51 @@ int tplg_parse_manifest_data(snd_tplg_t *tplg, snd_config_t *cfg,
+ return 0;
+ }
+
++/* save manifest data */
++int tplg_save_manifest_data(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
++ struct tplg_elem *elem, char **dst,
++ const char *pfx)
++{
++ struct list_head *pos;
++ struct tplg_ref *ref;
++ int err, index, count;
++
++ /* for each ref in this manifest elem */
++ count = 0;
++ list_for_each(pos, &elem->ref_list) {
++ ref = list_entry(pos, struct tplg_ref, list);
++ if (ref->type != SND_TPLG_TYPE_DATA)
++ continue;
++ count++;
++ }
++ if (count > 1) {
++ err = tplg_save_printf(dst, NULL, "'%s'.data [\n", elem->id);
++ if (err < 0)
++ return err;
++ }
++ index = 0;
++ list_for_each(pos, &elem->ref_list) {
++ ref = list_entry(pos, struct tplg_ref, list);
++ if (ref->type != SND_TPLG_TYPE_DATA)
++ continue;
++ if (count == 1) {
++ err = tplg_save_printf(dst, NULL, "'%s'.data.%u '%s'\n",
++ elem->id, index, ref->id);
++ } else {
++ err = tplg_save_printf(dst, pfx, "\t'%s'\n", ref->id);
++ if (err < 0)
++ return err;
++ }
++ index++;
++ }
++ if (count > 1) {
++ err = tplg_save_printf(dst, pfx, "]\n");
++ if (err < 0)
++ return err;
++ }
++ return 0;
++}
++
+ /* merge private data of manifest */
+ int tplg_build_manifest_data(snd_tplg_t *tplg)
+ {
+@@ -1064,7 +1360,7 @@ int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg,
+ }
+
+ if (strcmp(id, "tuples") == 0) {
+- err = parse_tuples_refs(n, elem);
++ err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_TUPLE);
+ if (err < 0)
+ return err;
+ continue;
+@@ -1083,6 +1379,81 @@ int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg,
+ return err;
+ }
+
++/* save data element */
++int tplg_save_data(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
++ struct tplg_elem *elem,
++ char **dst, const char *pfx)
++{
++ struct snd_soc_tplg_private *priv = elem->data;
++ struct list_head *pos;
++ struct tplg_ref *ref;
++ char pfx2[16];
++ unsigned int i, count;
++ int err;
++
++ count = 0;
++ if (priv && priv->size > 0)
++ count++;
++ list_for_each(pos, &elem->ref_list) {
++ ref = list_entry(pos, struct tplg_ref, list);
++ if (ref->type == SND_TPLG_TYPE_TUPLE)
++ count++;
++ }
++ if (elem->vendor_type > 0)
++ count++;
++
++ if (count > 1) {
++ err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
++ if (err >= 0)
++ err = tplg_save_printf(dst, NULL, "");
++ } else {
++ err = tplg_save_printf(dst, NULL, "'%s'.", elem->id);
++ }
++ if (err >= 0 && priv && priv->size > 0) {
++ if (count > 1) {
++ err = tplg_save_printf(dst, pfx, "");
++ if (err < 0)
++ return err;
++ }
++ if (priv->size > 8) {
++ err = tplg_save_printf(dst, NULL, "bytes\n");
++ if (err >= 0)
++ err = tplg_save_printf(dst, pfx, "\t'");
++ } else {
++ err = tplg_save_printf(dst, NULL, "bytes '");
++ }
++ if (err < 0)
++ return err;
++ for (i = 0; i < priv->size; i++) {
++ if (i > 0 && (i % 8) == 0) {
++ err = tplg_save_printf(dst, NULL, ":\n");
++ if (err < 0)
++ return err;
++ err = tplg_save_printf(dst, pfx, "\t ");
++ if (err < 0)
++ return err;
++ }
++ err = tplg_save_printf(dst, NULL, "%s%02x",
++ (i % 8) == 0 ? "" : ":",
++ (unsigned char)priv->data[i]);
++ if (err < 0)
++ return err;
++ }
++ err = tplg_save_printf(dst, NULL, "'\n");
++ }
++ snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: "");
++ if (err >= 0)
++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_TUPLE,
++ "tuples", dst,
++ count > 1 ? pfx2 : NULL);
++ if (err >= 0 && elem->vendor_type > 0)
++ err = tplg_save_printf(dst, pfx, "type %u",
++ elem->vendor_type);
++ if (err >= 0 && count > 1)
++ err = tplg_save_printf(dst, pfx, "}\n");
++ return err;
++}
++
+ /* Find a referenced data element and copy its data to the parent
+ * element's private data buffer.
+ * An element can refer to multiple data sections. Data of these sections
+diff --git a/src/topology/elem.c b/src/topology/elem.c
+index e79a68b71a91..89aed1fc0449 100644
+--- a/src/topology/elem.c
++++ b/src/topology/elem.c
+@@ -30,6 +30,7 @@ struct tplg_table tplg_table[] = {
+ .size = sizeof(struct snd_soc_tplg_manifest),
+ .enew = 1,
+ .parse = tplg_parse_manifest_data,
++ .save = tplg_save_manifest_data,
+ },
+ {
+ .name = "control mixer",
+@@ -41,6 +42,7 @@ struct tplg_table tplg_table[] = {
+ .build = 1,
+ .enew = 1,
+ .parse = tplg_parse_control_mixer,
++ .save = tplg_save_control_mixer,
+ },
+ {
+ .name = "control enum",
+@@ -52,6 +54,7 @@ struct tplg_table tplg_table[] = {
+ .build = 1,
+ .enew = 1,
+ .parse = tplg_parse_control_enum,
++ .save = tplg_save_control_enum,
+ },
+ {
+ .name = "control extended (bytes)",
+@@ -63,6 +66,7 @@ struct tplg_table tplg_table[] = {
+ .build = 1,
+ .enew = 1,
+ .parse = tplg_parse_control_bytes,
++ .save = tplg_save_control_bytes,
+ },
+ {
+ .name = "dapm widget",
+@@ -74,6 +78,7 @@ struct tplg_table tplg_table[] = {
+ .build = 1,
+ .enew = 1,
+ .parse = tplg_parse_dapm_widget,
++ .save = tplg_save_dapm_widget,
+ },
+ {
+ .name = "pcm",
+@@ -85,6 +90,7 @@ struct tplg_table tplg_table[] = {
+ .build = 1,
+ .enew = 1,
+ .parse = tplg_parse_pcm,
++ .save = tplg_save_pcm,
+ },
+ {
+ .name = "physical dai",
+@@ -96,6 +102,7 @@ struct tplg_table tplg_table[] = {
+ .build = 1,
+ .enew = 1,
+ .parse = tplg_parse_dai,
++ .save = tplg_save_dai,
+ },
+ {
+ .name = "be",
+@@ -108,6 +115,7 @@ struct tplg_table tplg_table[] = {
+ .build = 1,
+ .enew = 1,
+ .parse = tplg_parse_link,
++ .save = tplg_save_link,
+ },
+ {
+ .name = "cc",
+@@ -119,6 +127,7 @@ struct tplg_table tplg_table[] = {
+ .build = 1,
+ .enew = 1,
+ .parse = tplg_parse_cc,
++ .save = tplg_save_cc,
+ },
+ {
+ .name = "route (dapm graph)",
+@@ -128,6 +137,7 @@ struct tplg_table tplg_table[] = {
+ .tsoc = SND_SOC_TPLG_TYPE_DAPM_GRAPH,
+ .build = 1,
+ .parse = tplg_parse_dapm_graph,
++ .gsave = tplg_save_dapm_graph,
+ },
+ {
+ .name = "private data",
+@@ -138,6 +148,7 @@ struct tplg_table tplg_table[] = {
+ .build = 1,
+ .enew = 1,
+ .parse = tplg_parse_data,
++ .save = tplg_save_data,
+ },
+ {
+ .name = "text",
+@@ -147,6 +158,7 @@ struct tplg_table tplg_table[] = {
+ .size = sizeof(struct tplg_texts),
+ .enew = 1,
+ .parse = tplg_parse_text,
++ .save = tplg_save_text,
+ },
+ {
+ .name = "tlv",
+@@ -156,6 +168,7 @@ struct tplg_table tplg_table[] = {
+ .size = sizeof(struct snd_soc_tplg_ctl_tlv),
+ .enew = 1,
+ .parse = tplg_parse_tlv,
++ .save = tplg_save_tlv,
+ },
+ {
+ .name = "stream config",
+@@ -172,6 +185,7 @@ struct tplg_table tplg_table[] = {
+ .size = sizeof(struct snd_soc_tplg_stream_caps),
+ .enew = 1,
+ .parse = tplg_parse_stream_caps,
++ .save = tplg_save_stream_caps,
+ },
+ {
+ .name = "token",
+@@ -180,6 +194,7 @@ struct tplg_table tplg_table[] = {
+ .type = SND_TPLG_TYPE_TOKEN,
+ .enew = 1,
+ .parse = tplg_parse_tokens,
++ .save = tplg_save_tokens,
+ },
+ {
+ .name = "tuple",
+@@ -189,6 +204,7 @@ struct tplg_table tplg_table[] = {
+ .free = tplg_free_tuples,
+ .enew = 1,
+ .parse = tplg_parse_tuples,
++ .save = tplg_save_tuples,
+ },
+ {
+ .name = "hw config",
+@@ -198,6 +214,7 @@ struct tplg_table tplg_table[] = {
+ .size = sizeof(struct snd_soc_tplg_hw_config),
+ .enew = 1,
+ .parse = tplg_parse_hw_config,
++ .save = tplg_save_hw_config,
+ }
+ };
+
+@@ -394,6 +411,7 @@ struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg,
+ tplg_elem_insert(elem, list);
+ obj_size = tptr->size;
+ elem->free = tptr->free;
++ elem->table = tptr;
+
+ /* create new object too if required */
+ if (obj_size > 0) {
+diff --git a/src/topology/ops.c b/src/topology/ops.c
+index 073acdcb1453..ad72ef1b2cb6 100644
+--- a/src/topology/ops.c
++++ b/src/topology/ops.c
+@@ -45,6 +45,18 @@ static int lookup_ops(const char *c)
+ return strtol(c, NULL, 0);
+ }
+
++const char *tplg_ops_name(int type)
++{
++ unsigned int i;
++
++ for (i = 0; i < ARRAY_SIZE(control_map); i++) {
++ if (control_map[i].id == type)
++ return control_map[i].name;
++ }
++
++ return NULL;
++}
++
+ /* Parse Control operations. Ops can come from standard names above or
+ * bespoke driver controls with numbers >= 256
+ */
+@@ -84,6 +96,46 @@ int tplg_parse_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED, snd_config_t *cfg,
+ return 0;
+ }
+
++/* save control operations */
++int tplg_save_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
++ struct snd_soc_tplg_ctl_hdr *hdr, char **dst,
++ const char *pfx)
++{
++ const char *s;
++ int err;
++
++ if (hdr->ops.info + hdr->ops.get + hdr->ops.put == 0)
++ return 0;
++ err = tplg_save_printf(dst, pfx, "ops.0 {\n");
++ if (err >= 0 && hdr->ops.info > 0) {
++ s = tplg_ops_name(hdr->ops.info);
++ if (s == NULL)
++ err = tplg_save_printf(dst, pfx, "\tinfo %u\n",
++ hdr->ops.info);
++ else
++ err = tplg_save_printf(dst, pfx, "\tinfo %s\n", s);
++ }
++ if (err >= 0 && hdr->ops.get > 0) {
++ s = tplg_ops_name(hdr->ops.get);
++ if (s == NULL)
++ err = tplg_save_printf(dst, pfx, "\tget %u\n",
++ hdr->ops.get);
++ else
++ err = tplg_save_printf(dst, pfx, "\tget %s\n", s);
++ }
++ if (err >= 0 && hdr->ops.put > 0) {
++ s = tplg_ops_name(hdr->ops.put);
++ if (s == NULL)
++ err = tplg_save_printf(dst, pfx, "\tput %u\n",
++ hdr->ops.put);
++ else
++ err = tplg_save_printf(dst, pfx, "\tput %s\n", s);
++ }
++ if (err >= 0)
++ err = tplg_save_printf(dst, pfx, "}\n");
++ return err;
++}
++
+ /* Parse External Control operations. Ops can come from standard names above or
+ * bespoke driver controls with numbers >= 256
+ */
+@@ -121,3 +173,43 @@ int tplg_parse_ext_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+
+ return 0;
+ }
++
++/* save external control operations */
++int tplg_save_ext_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
++ struct snd_soc_tplg_bytes_control *be,
++ char **dst, const char *pfx)
++{
++ const char *s;
++ int err;
++
++ if (be->ext_ops.info + be->ext_ops.get + be->ext_ops.put == 0)
++ return 0;
++ err = tplg_save_printf(dst, pfx, "extops.0 {\n");
++ if (err >= 0 && be->ext_ops.info > 0) {
++ s = tplg_ops_name(be->ext_ops.info);
++ if (s == NULL)
++ err = tplg_save_printf(dst, pfx, "\tinfo %u\n",
++ be->ext_ops.info);
++ else
++ err = tplg_save_printf(dst, pfx, "\tinfo %s\n", s);
++ }
++ if (err >= 0 && be->ext_ops.get > 0) {
++ s = tplg_ops_name(be->ext_ops.get);
++ if (s == NULL)
++ err = tplg_save_printf(dst, pfx, "\tget %u\n",
++ be->ext_ops.get);
++ else
++ err = tplg_save_printf(dst, pfx, "\tget %s\n", s);
++ }
++ if (err >= 0 && be->ext_ops.put > 0) {
++ s = tplg_ops_name(be->ext_ops.put);
++ if (s == NULL)
++ err = tplg_save_printf(dst, pfx, "\tput %u\n",
++ be->ext_ops.put);
++ else
++ err = tplg_save_printf(dst, pfx, "\tput %s\n", s);
++ }
++ if (err >= 0)
++ err = tplg_save_printf(dst, pfx, "}\n");
++ return err;
++}
+diff --git a/src/topology/parser.c b/src/topology/parser.c
+index 11202769391c..de5edd1b6591 100644
+--- a/src/topology/parser.c
++++ b/src/topology/parser.c
+@@ -71,6 +71,8 @@ int tplg_get_unsigned(snd_config_t *n, unsigned *val, int base)
+ err = snd_config_get_integer(n, &lval);
+ if (err < 0)
+ return err;
++ if (lval < 0 && lval >= INT_MIN)
++ lval = UINT_MAX + lval + 1;
+ if (lval < 0 || lval > UINT_MAX)
+ return -ERANGE;
+ *val = lval;
+@@ -79,6 +81,8 @@ int tplg_get_unsigned(snd_config_t *n, unsigned *val, int base)
+ err = snd_config_get_integer64(n, &llval);
+ if (err < 0)
+ return err;
++ if (llval < 0 && llval >= INT_MIN)
++ llval = UINT_MAX + llval + 1;
+ if (llval < 0 || llval > UINT_MAX)
+ return -ERANGE;
+ *val = llval;
+diff --git a/src/topology/pcm.c b/src/topology/pcm.c
+index 9b87549cabbd..d09fbe42f5da 100644
+--- a/src/topology/pcm.c
++++ b/src/topology/pcm.c
+@@ -345,6 +345,13 @@ static int get_rate_value(const char* name)
+ return SND_PCM_RATE_UNKNOWN;
+ }
+
++static const char *get_rate_name(int rate)
++{
++ if (rate >= 0 && rate <= SND_PCM_RATE_LAST)
++ return snd_pcm_rate_names[rate];
++ return NULL;
++}
++
+ static int split_rate(struct snd_soc_tplg_stream_caps *caps, char *str)
+ {
+ char *s = NULL;
+@@ -527,6 +534,80 @@ int tplg_parse_stream_caps(snd_tplg_t *tplg,
+ return 0;
+ }
+
++/* save stream caps */
++int tplg_save_stream_caps(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
++ struct tplg_elem *elem,
++ char **dst, const char *pfx)
++{
++ struct snd_soc_tplg_stream_caps *sc = elem->stream_caps;
++ const char *s;
++ unsigned int i;
++ int err, first;
++
++ err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
++ if (err >= 0 && sc->formats) {
++ err = tplg_save_printf(dst, pfx, "\tformats '");
++ first = 1;
++ for (i = 0; err >= 0 && i < SND_PCM_FORMAT_LAST; i++) {
++ if (sc->formats & (1ULL << i)) {
++ s = snd_pcm_format_name(i);
++ err = tplg_save_printf(dst, NULL, "%s%s",
++ !first ? ", " : "", s);
++ first = 0;
++ }
++ }
++ if (err >= 0)
++ err = tplg_save_printf(dst, NULL, "'\n");
++ }
++ if (err >= 0 && sc->rates) {
++ err = tplg_save_printf(dst, pfx, "\trates '");
++ first = 1;
++ for (i = 0; err >= 0 && i < SND_PCM_RATE_LAST; i++) {
++ if (sc->rates & (1ULL << i)) {
++ s = get_rate_name(i);
++ err = tplg_save_printf(dst, NULL, "%s%s",
++ !first ? ", " : "", s);
++ first = 0;
++ }
++ }
++ if (err >= 0)
++ err = tplg_save_printf(dst, NULL, "'\n");
++ }
++ if (err >= 0 && sc->rate_min)
++ err = tplg_save_printf(dst, pfx, "\trate_min %u\n",
++ sc->rate_min);
++ if (err >= 0 && sc->rate_max)
++ err = tplg_save_printf(dst, pfx, "\trate_max %u\n",
++ sc->rate_max);
++ if (err >= 0 && sc->channels_min)
++ err = tplg_save_printf(dst, pfx, "\tchannels_min %u\n",
++ sc->channels_min);
++ if (err >= 0 && sc->channels_max)
++ err = tplg_save_printf(dst, pfx, "\tchannels_max %u\n",
++ sc->channels_max);
++ if (err >= 0 && sc->periods_min)
++ err = tplg_save_printf(dst, pfx, "\tperiods_min %u\n",
++ sc->periods_min);
++ if (err >= 0 && sc->periods_max)
++ err = tplg_save_printf(dst, pfx, "\tperiods_max %u\n",
++ sc->periods_max);
++ if (err >= 0 && sc->period_size_min)
++ err = tplg_save_printf(dst, pfx, "\tperiod_size_min %u\n",
++ sc->period_size_min);
++ if (err >= 0 && sc->period_size_max)
++ err = tplg_save_printf(dst, pfx, "\tperiod_size_max %u\n",
++ sc->period_size_max);
++ if (err >= 0 && sc->buffer_size_min)
++ err = tplg_save_printf(dst, pfx, "\tbuffer_size_min %u\n",
++ sc->buffer_size_min);
++ if (err >= 0 && sc->buffer_size_max)
++ err = tplg_save_printf(dst, pfx, "\tbuffer_size_max %u\n",
++ sc->buffer_size_max);
++ if (err >= 0)
++ err = tplg_save_printf(dst, pfx, "}\n");
++ return err;
++}
++
+ /* Parse the caps and config of a pcm stream */
+ static int tplg_parse_streams(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ snd_config_t *cfg, void *private)
+@@ -598,6 +679,61 @@ static int tplg_parse_streams(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ return 0;
+ }
+
++/* Save the caps and config of a pcm stream */
++int tplg_save_streams(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
++ struct tplg_elem *elem,
++ char **dst, const char *pfx)
++{
++ static const char *stream_ids[2] = {
++ "playback",
++ "capture"
++ };
++ static unsigned int stream_types[2] = {
++ SND_SOC_TPLG_STREAM_PLAYBACK,
++ SND_SOC_TPLG_STREAM_CAPTURE
++ };
++ struct snd_soc_tplg_stream_caps *caps;
++ unsigned int streams[2], stream;
++ const char *s;
++ int err;
++
++ switch (elem->type) {
++ case SND_TPLG_TYPE_PCM:
++ streams[0] = elem->pcm->playback;
++ streams[1] = elem->pcm->capture;
++ caps = elem->pcm->caps;
++ break;
++ case SND_TPLG_TYPE_DAI:
++ streams[0] = elem->dai->playback;
++ streams[1] = elem->dai->capture;
++ caps = elem->dai->caps;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ for (stream = 0; stream < 2; stream++) {
++ if (streams[stream] == 0)
++ continue;
++ if (!caps)
++ continue;
++ s = caps[stream_types[stream]].name;
++ if (s[0] == '\0')
++ continue;
++ err = tplg_save_printf(dst, pfx, "pcm.%s {\n", stream_ids[stream]);
++ if (err < 0)
++ return err;
++ err = tplg_save_printf(dst, pfx, "\tcapabilities '%s'\n", s);
++ if (err < 0)
++ return err;
++ err = tplg_save_printf(dst, pfx, "}\n");
++ if (err < 0)
++ return err;
++ }
++
++ return 0;
++}
++
+ /* Parse name and id of a front-end DAI (ie. cpu dai of a FE DAI link) */
+ static int tplg_parse_fe_dai(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ snd_config_t *cfg, void *private)
+@@ -633,6 +769,19 @@ static int tplg_parse_fe_dai(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ return 0;
+ }
+
++/* Save the caps and config of a pcm stream */
++int tplg_save_fe_dai(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
++ struct tplg_elem *elem,
++ char **dst, const char *pfx)
++{
++ struct snd_soc_tplg_pcm *pcm = elem->pcm;
++ int err = 0;
++
++ if (pcm->dai_id > 0)
++ err = tplg_save_printf(dst, pfx, "dai.0.id %u\n", pcm->dai_id);
++ return err;
++}
++
+ /* parse a flag bit of the given mask */
+ static int parse_flag(snd_config_t *n, unsigned int mask_in,
+ unsigned int *mask, unsigned int *flags)
+@@ -652,6 +801,32 @@ static int parse_flag(snd_config_t *n, unsigned int mask_in,
+ return 0;
+ }
+
++static int save_flags(unsigned int flags, unsigned int mask,
++ char **dst, const char *pfx)
++{
++ static unsigned int flag_masks[3] = {
++ SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES,
++ SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS,
++ SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS,
++ };
++ static const char *flag_ids[3] = {
++ "symmetric_rates",
++ "symmetric_channels",
++ "symmetric_sample_bits",
++ };
++ unsigned int i;
++ int err = 0;
++
++ for (i = 0; err >= 0 && i < ARRAY_SIZE(flag_masks); i++) {
++ if (mask & flag_masks[i]) {
++ unsigned int v = (flags & flag_masks[i]) ? 1 : 0;
++ err = tplg_save_printf(dst, pfx, "%s %u\n",
++ flag_ids[i], v);
++ }
++ }
++ return err;
++}
++
+ /* Parse PCM (for front end DAI & DAI link) in text conf file */
+ int tplg_parse_pcm(snd_tplg_t *tplg, snd_config_t *cfg,
+ void *private ATTRIBUTE_UNUSED)
+@@ -748,7 +923,7 @@ int tplg_parse_pcm(snd_tplg_t *tplg, snd_config_t *cfg,
+
+ /* private data */
+ if (strcmp(id, "data") == 0) {
+- err = tplg_parse_data_refs(n, elem);
++ err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA);
+ if (err < 0)
+ return err;
+ continue;
+@@ -758,6 +933,40 @@ int tplg_parse_pcm(snd_tplg_t *tplg, snd_config_t *cfg,
+ return 0;
+ }
+
++/* save PCM */
++int tplg_save_pcm(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
++ struct tplg_elem *elem,
++ char **dst, const char *pfx)
++{
++ struct snd_soc_tplg_pcm *pcm = elem->pcm;
++ char pfx2[16];
++ int err;
++
++ snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: "");
++ err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
++ if (err >= 0 && elem->index)
++ err = tplg_save_printf(dst, pfx, "\tindex %u\n",
++ elem->index);
++ if (err >= 0 && pcm->pcm_id)
++ err = tplg_save_printf(dst, pfx, "\tid %u\n",
++ pcm->pcm_id);
++ if (err >= 0 && pcm->compress)
++ err = tplg_save_printf(dst, pfx, "\tcompress 1\n");
++ snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: "");
++ if (err >= 0)
++ err = tplg_save_fe_dai(tplg, elem, dst, pfx2);
++ if (err >= 0)
++ err = tplg_save_streams(tplg, elem, dst, pfx2);
++ if (err >= 0)
++ err = save_flags(pcm->flags, pcm->flag_mask, dst, pfx);
++ if (err >= 0)
++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_DATA,
++ "data", dst, pfx2);
++ if (err >= 0)
++ err = tplg_save_printf(dst, pfx, "}\n");
++ return err;
++}
++
+ /* Parse physical DAI */
+ int tplg_parse_dai(snd_tplg_t *tplg, snd_config_t *cfg,
+ void *private ATTRIBUTE_UNUSED)
+@@ -766,7 +975,7 @@ int tplg_parse_dai(snd_tplg_t *tplg, snd_config_t *cfg,
+ struct tplg_elem *elem;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+- const char *id, *val = NULL;
++ const char *id;
+ int err;
+
+ elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_DAI);
+@@ -851,11 +1060,9 @@ int tplg_parse_dai(snd_tplg_t *tplg, snd_config_t *cfg,
+
+ /* private data */
+ if (strcmp(id, "data") == 0) {
+- if (snd_config_get_string(n, &val) < 0)
+- return -EINVAL;
+-
+- tplg_ref_add(elem, SND_TPLG_TYPE_DATA, val);
+- tplg_dbg("\t%s: %s\n", id, val);
++ err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA);
++ if (err < 0)
++ return err;
+ continue;
+ }
+ }
+@@ -863,55 +1070,55 @@ int tplg_parse_dai(snd_tplg_t *tplg, snd_config_t *cfg,
+ return 0;
+ }
+
++/* save DAI */
++int tplg_save_dai(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
++ struct tplg_elem *elem,
++ char **dst, const char *pfx)
++{
++ struct snd_soc_tplg_dai *dai = elem->dai;
++ char pfx2[16];
++ int err;
++
++ if (!dai)
++ return 0;
++ snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: "");
++ err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
++ if (err >= 0 && elem->index)
++ err = tplg_save_printf(dst, pfx, "\tindex %u\n",
++ elem->index);
++ if (err >= 0 && dai->dai_id)
++ err = tplg_save_printf(dst, pfx, "\tid %u\n",
++ dai->dai_id);
++ if (err >= 0 && dai->playback)
++ err = tplg_save_printf(dst, pfx, "\tplayback %u\n",
++ dai->playback);
++ if (err >= 0 && dai->capture)
++ err = tplg_save_printf(dst, pfx, "\tcapture %u\n",
++ dai->capture);
++ if (err >= 0)
++ err = tplg_save_streams(tplg, elem, dst, pfx2);
++ if (err >= 0)
++ err = save_flags(dai->flags, dai->flag_mask, dst, pfx);
++ if (err >= 0)
++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_DATA,
++ "data", dst, pfx2);
++ if (err >= 0)
++ err = tplg_save_printf(dst, pfx, "}\n");
++ return err;
++}
++
+ /* parse physical link runtime supported HW configs in text conf file */
+ static int parse_hw_config_refs(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ snd_config_t *cfg,
+ struct tplg_elem *elem)
+ {
+ struct snd_soc_tplg_link_config *link = elem->link;
+- snd_config_type_t type;
+- snd_config_iterator_t i, next;
+- snd_config_t *n;
+- const char *id, *val = NULL;
+-
+- if (snd_config_get_id(cfg, &id) < 0)
+- return -EINVAL;
+- type = snd_config_get_type(cfg);
+-
+- /* refer to a single HW config */
+- if (type == SND_CONFIG_TYPE_STRING) {
+- if (snd_config_get_string(cfg, &val) < 0)
+- return -EINVAL;
+-
+- link->num_hw_configs = 1;
+- return tplg_ref_add(elem, SND_TPLG_TYPE_HW_CONFIG, val);
+- }
+-
+- if (type != SND_CONFIG_TYPE_COMPOUND) {
+- SNDERR("error: compound type expected for %s", id);
+- return -EINVAL;
+- }
+-
+- /* refer to a list of HW configs */
+- snd_config_for_each(i, next, cfg) {
+- const char *val;
+- int err;
+-
+- n = snd_config_iterator_entry(i);
+- if (snd_config_get_string(n, &val) < 0)
+- continue;
+-
+- if (link->num_hw_configs >= SND_SOC_TPLG_HW_CONFIG_MAX) {
+- SNDERR("error: exceed max hw configs for link %s", id);
+- return -EINVAL;
+- }
+-
+- link->num_hw_configs++;
+- err = tplg_ref_add(elem, SND_TPLG_TYPE_HW_CONFIG, val);
+- if (err < 0)
+- return err;
+- }
++ int err;
+
++ err = tplg_parse_refs(cfg, elem, SND_TPLG_TYPE_HW_CONFIG);
++ if (err < 0)
++ return err;
++ link->num_hw_configs = err;
+ return 0;
+ }
+
+@@ -1007,7 +1214,7 @@ int tplg_parse_link(snd_tplg_t *tplg,
+
+ /* private data */
+ if (strcmp(id, "data") == 0) {
+- err = tplg_parse_data_refs(n, elem);
++ err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA);
+ if (err < 0)
+ return err;
+ continue;
+@@ -1017,6 +1224,44 @@ int tplg_parse_link(snd_tplg_t *tplg,
+ return 0;
+ }
+
++/* save physical link */
++int tplg_save_link(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
++ struct tplg_elem *elem,
++ char **dst, const char *pfx)
++{
++ struct snd_soc_tplg_link_config *link = elem->link;
++ char pfx2[16];
++ int err;
++
++ if (!link)
++ return 0;
++ snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: "");
++ err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
++ if (err >= 0 && elem->index)
++ err = tplg_save_printf(dst, pfx, "\tindex %u\n",
++ elem->index);
++ if (err >= 0 && link->id)
++ err = tplg_save_printf(dst, pfx, "\tid %u\n",
++ link->id);
++ if (err >= 0 && link->stream_name[0])
++ err = tplg_save_printf(dst, pfx, "\tstream_name '%s'\n",
++ link->stream_name);
++ if (err >= 0 && link->default_hw_config_id)
++ err = tplg_save_printf(dst, pfx, "\tdefault_hw_conf_id %u\n",
++ link->default_hw_config_id);
++ if (err >= 0)
++ err = save_flags(link->flags, link->flag_mask, dst, pfx);
++ if (err >= 0)
++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_HW_CONFIG,
++ "hw_configs", dst, pfx2);
++ if (err >= 0)
++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_DATA,
++ "data", dst, pfx2);
++ if (err >= 0)
++ err = tplg_save_printf(dst, pfx, "}\n");
++ return err;
++}
++
+ /* Parse cc */
+ int tplg_parse_cc(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
+@@ -1059,36 +1304,95 @@ int tplg_parse_cc(snd_tplg_t *tplg,
+ return 0;
+ }
+
+-static int get_audio_hw_format(const char *val)
++/* save CC */
++int tplg_save_cc(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
++ struct tplg_elem *elem,
++ char **dst, const char *pfx)
+ {
+- if (!strlen(val))
+- return -EINVAL;
+-
+- if (!strcmp(val, "I2S"))
+- return SND_SOC_DAI_FORMAT_I2S;
++ struct snd_soc_tplg_link_config *link = elem->link;
++ char pfx2[16];
++ int err;
+
+- if (!strcmp(val, "RIGHT_J"))
+- return SND_SOC_DAI_FORMAT_RIGHT_J;
++ if (!link)
++ return 0;
++ snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: "");
++ err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
++ if (err >= 0 && elem->index)
++ err = tplg_save_printf(dst, pfx, "\tindex %u\n",
++ elem->index);
++ if (err >= 0 && link->id)
++ err = tplg_save_printf(dst, pfx, "\tid %u\n",
++ link->id);
++ if (err >= 0)
++ err = tplg_save_printf(dst, pfx, "}\n");
++ return err;
++}
+
+- if (!strcmp(val, "LEFT_J"))
+- return SND_SOC_DAI_FORMAT_LEFT_J;
++struct audio_hw_format {
++ unsigned int type;
++ const char *name;
++};
+
+- if (!strcmp(val, "DSP_A"))
+- return SND_SOC_DAI_FORMAT_DSP_A;
++static struct audio_hw_format audio_hw_formats[] = {
++ {
++ .type = SND_SOC_DAI_FORMAT_I2S,
++ .name = "I2S",
++ },
++ {
++ .type = SND_SOC_DAI_FORMAT_RIGHT_J,
++ .name = "RIGHT_J",
++ },
++ {
++ .type = SND_SOC_DAI_FORMAT_LEFT_J,
++ .name = "LEFT_J",
++ },
++ {
++ .type = SND_SOC_DAI_FORMAT_DSP_A,
++ .name = "DSP_A",
++ },
++ {
++ .type = SND_SOC_DAI_FORMAT_DSP_B,
++ .name = "DSP_B",
++ },
++ {
++ .type = SND_SOC_DAI_FORMAT_AC97,
++ .name = "AC97",
++ },
++ {
++ .type = SND_SOC_DAI_FORMAT_AC97,
++ .name = "AC97",
++ },
++ {
++ .type = SND_SOC_DAI_FORMAT_PDM,
++ .name = "PDM",
++ },
++};
+
+- if (!strcmp(val, "DSP_B"))
+- return SND_SOC_DAI_FORMAT_DSP_B;
++static int get_audio_hw_format(const char *val)
++{
++ unsigned int i;
+
+- if (!strcmp(val, "AC97"))
+- return SND_SOC_DAI_FORMAT_AC97;
++ if (val[0] == '\0')
++ return -EINVAL;
+
+- if (!strcmp(val, "PDM"))
+- return SND_SOC_DAI_FORMAT_PDM;
++ for (i = 0; i < ARRAY_SIZE(audio_hw_formats); i++)
++ if (strcasecmp(audio_hw_formats[i].name, val) == 0)
++ return audio_hw_formats[i].type;
+
+ SNDERR("error: invalid audio HW format %s\n", val);
+ return -EINVAL;
+ }
+
++static const char *get_audio_hw_format_name(unsigned int type)
++{
++ unsigned int i;
++
++ for (i = 0; i < ARRAY_SIZE(audio_hw_formats); i++)
++ if (audio_hw_formats[i].type == type)
++ return audio_hw_formats[i].name;
++ return NULL;
++}
++
+ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg,
+ void *private ATTRIBUTE_UNUSED)
+ {
+@@ -1299,6 +1603,71 @@ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg,
+ return 0;
+ }
+
++/* save hw config */
++int tplg_save_hw_config(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
++ struct tplg_elem *elem,
++ char **dst, const char *pfx)
++{
++ struct snd_soc_tplg_hw_config *hc = elem->hw_cfg;
++ int err;
++
++ err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
++ if (err >= 0 && hc->id)
++ err = tplg_save_printf(dst, pfx, "\tid %u\n",
++ hc->id);
++ if (err >= 0 && hc->fmt)
++ err = tplg_save_printf(dst, pfx, "\tformat '%s'\n",
++ get_audio_hw_format_name(hc->fmt));
++ if (err >= 0 && hc->bclk_master)
++ err = tplg_save_printf(dst, pfx, "\tbclk '%s'\n",
++ hc->bclk_master == SND_SOC_TPLG_BCLK_CS ?
++ "codec_slave" : "codec_master");
++ if (err >= 0 && hc->bclk_rate)
++ err = tplg_save_printf(dst, pfx, "\tbclk_freq %u\n",
++ hc->bclk_rate);
++ if (err >= 0 && hc->invert_bclk)
++ err = tplg_save_printf(dst, pfx, "\tbclk_invert 1\n");
++ if (err >= 0 && hc->fsync_master)
++ err = tplg_save_printf(dst, pfx, "\tfsync_master '%s'\n",
++ hc->fsync_master == SND_SOC_TPLG_FSYNC_CS ?
++ "codec_slave" : "codec_master");
++ if (err >= 0 && hc->fsync_rate)
++ err = tplg_save_printf(dst, pfx, "\tfsync_freq %u\n",
++ hc->fsync_rate);
++ if (err >= 0 && hc->invert_fsync)
++ err = tplg_save_printf(dst, pfx, "\tfsync_invert 1\n");
++ if (err >= 0 && hc->mclk_rate)
++ err = tplg_save_printf(dst, pfx, "\tmclk_freq %u\n",
++ hc->mclk_rate);
++ if (err >= 0 && hc->mclk_direction)
++ err = tplg_save_printf(dst, pfx, "\tmclk '%s'\n",
++ hc->mclk_direction == SND_SOC_TPLG_MCLK_CI ?
++ "codec_mclk_in" : "codec_mclk_out");
++ if (err >= 0 && hc->clock_gated)
++ err = tplg_save_printf(dst, pfx, "\tpm_gate_clocks 1\n");
++ if (err >= 0 && hc->tdm_slots)
++ err = tplg_save_printf(dst, pfx, "\ttdm_slots %u\n",
++ hc->tdm_slots);
++ if (err >= 0 && hc->tdm_slot_width)
++ err = tplg_save_printf(dst, pfx, "\ttdm_slot_width %u\n",
++ hc->tdm_slot_width);
++ if (err >= 0 && hc->tx_slots)
++ err = tplg_save_printf(dst, pfx, "\ttx_slots %u\n",
++ hc->tx_slots);
++ if (err >= 0 && hc->rx_slots)
++ err = tplg_save_printf(dst, pfx, "\trx_slots %u\n",
++ hc->rx_slots);
++ if (err >= 0 && hc->tx_channels)
++ err = tplg_save_printf(dst, pfx, "\ttx_channels %u\n",
++ hc->tx_channels);
++ if (err >= 0 && hc->rx_channels)
++ err = tplg_save_printf(dst, pfx, "\trx_channels %u\n",
++ hc->rx_channels);
++ if (err >= 0)
++ err = tplg_save_printf(dst, pfx, "}\n");
++ return err;
++}
++
+ /* copy stream object */
+ static void tplg_add_stream_object(struct snd_soc_tplg_stream *strm,
+ struct snd_tplg_stream_template *strm_tpl)
+diff --git a/src/topology/save.c b/src/topology/save.c
+new file mode 100644
+index 000000000000..0498911f67d5
+--- /dev/null
++++ b/src/topology/save.c
+@@ -0,0 +1,632 @@
++/*
++ Copyright(c) 2019 Red Hat Inc.
++ All rights reserved.
++
++ This library is free software; you can redistribute it and/or modify
++ it under the terms of the GNU Lesser General Public License as
++ published by the Free Software Foundation; either version 2.1 of
++ the License, or (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU Lesser General Public License for more details.
++
++ Authors: Jaroslav Kysela
++*/
++
++#include "list.h"
++#include "tplg_local.h"
++
++#define SAVE_ALLOC_SHIFT (13) /* 8192 bytes */
++
++int tplg_save_printf(char **dst, const char *pfx, const char *fmt, ...)
++{
++ va_list va;
++ char buf[1024], *s;
++ size_t n, l, t, pl;
++
++ if (pfx == NULL)
++ pfx = "";
++
++ va_start(va, fmt);
++ n = vsnprintf(buf, sizeof(buf), fmt, va);
++ va_end(va);
++
++ if (n >= sizeof(buf))
++ return -EOVERFLOW;
++
++ pl = strlen(pfx);
++ l = *dst ? strlen(*dst) : 0;
++ t = l + pl + n + 1;
++ /* allocate chunks */
++ if (*dst == NULL ||
++ (l >> SAVE_ALLOC_SHIFT) != (t >> SAVE_ALLOC_SHIFT)) {
++ s = realloc(*dst, ((t >> SAVE_ALLOC_SHIFT) + 1) <<
++ SAVE_ALLOC_SHIFT);
++ if (s == NULL) {
++ free(*dst);
++ *dst = NULL;
++ return -ENOMEM;
++ }
++ } else {
++ s = *dst;
++ }
++
++ if (pl > 0)
++ strcpy(s + l, pfx);
++ strcpy(s + l + pl, buf);
++ *dst = s;
++ return 0;
++}
++
++int tplg_nice_value_format(char *dst, size_t dst_size, unsigned int value)
++{
++ if ((value % 1000) != 0) {
++ if (value > 0xfffffff0)
++ return snprintf(dst, dst_size, "%d", (int)value);
++ if (value >= 0xffff0000)
++ return snprintf(dst, dst_size, "0x%x", value);
++ }
++ return snprintf(dst, dst_size, "%u", value);
++}
++
++static int tplg_pprint_integer(snd_config_t *n, char **ret)
++{
++ long lval;
++ int err, type;
++ char buf[16];
++
++ type = snd_config_get_type(n);
++ if (type == SND_CONFIG_TYPE_INTEGER) {
++ err = snd_config_get_integer(n, &lval);
++ if (err < 0)
++ return err;
++ if (lval < INT_MIN || lval > UINT_MAX)
++ return snd_config_get_ascii(n, ret);
++ } else if (type == SND_CONFIG_TYPE_INTEGER64) {
++ long long llval;
++ err = snd_config_get_integer64(n, &llval);
++ if (err < 0)
++ return err;
++ if (llval < INT_MIN || llval > UINT_MAX)
++ return snd_config_get_ascii(n, ret);
++ lval = llval;
++ }
++ err = tplg_nice_value_format(buf, sizeof(buf), (unsigned int)lval);
++ if (err < 0)
++ return err;
++ *ret = strdup(buf);
++ if (*ret == NULL)
++ return -ENOMEM;
++ return 0;
++}
++
++static int tplg_check_array_item(const char *id, int index)
++{
++ const char *p;
++ long int val;
++
++ for (p = id; *p; p++) {
++ if (*p < '0' || *p > '9')
++ return 0;
++ }
++
++ errno = 0;
++ val = strtol(id, NULL, 10);
++ return errno == 0 && val == index;
++}
++
++static int _compar(const void *a, const void *b)
++{
++ const snd_config_t *c1 = *(snd_config_t **)a;
++ const snd_config_t *c2 = *(snd_config_t **)b;
++ const char *id1, *id2;
++ if (snd_config_get_id(c1, &id1)) return 0;
++ if (snd_config_get_id(c2, &id2)) return 0;
++ return strcmp(id1, id2);
++}
++
++static snd_config_t *sort_config(const char *id, snd_config_t *src)
++{
++ snd_config_t *dst, **a;
++ snd_config_iterator_t i, next;
++ int index, array, count;
++
++ if (snd_config_get_type(src) != SND_CONFIG_TYPE_COMPOUND) {
++
++ if (snd_config_copy(&dst, src) >= 0)
++ return dst;
++ return NULL;
++ }
++ count = 0;
++ snd_config_for_each(i, next, src)
++ count++;
++ a = malloc(sizeof(dst) * count);
++ if (a == NULL)
++ return NULL;
++ index = array = 0;
++ snd_config_for_each(i, next, src) {
++ snd_config_t *s = snd_config_iterator_entry(i);
++ const char *id2;
++ a[index++] = s;
++ if (array < 0)
++ continue;
++ if (snd_config_get_id(s, &id2)) {
++ free(a);
++ return NULL;
++ }
++ if (array >= 0 && tplg_check_array_item(id2, array))
++ array++;
++ else
++ array = -1;
++ }
++ if (array < 0)
++ qsort(a, count, sizeof(a[0]), _compar);
++ if (snd_config_make_compound(&dst, id, count == 1)) {
++ free(a);
++ return NULL;
++ }
++ for (index = 0; index < count; index++) {
++ snd_config_t *s = a[index];
++ const char *id2;
++ if (snd_config_get_id(s, &id2)) {
++ snd_config_delete(dst);
++ free(a);
++ return NULL;
++ }
++ s = sort_config(id2, s);
++ if (s == NULL || snd_config_add(dst, s)) {
++ if (s)
++ snd_config_delete(s);
++ snd_config_delete(dst);
++ free(a);
++ return NULL;
++ }
++ }
++ free(a);
++ return dst;
++}
++
++static int tplg_check_quoted(const unsigned char *p)
++{
++ for ( ; *p != '\0'; p++) {
++ switch (*p) {
++ case ' ':
++ case '=':
++ case ';':
++ case ',':
++ case '.':
++ case '{':
++ case '}':
++ case '\'':
++ case '"':
++ return 1;
++ default:
++ if (*p <= 31 || *p >= 127)
++ return 1;
++
++ }
++ }
++ return 0;
++}
++
++static int tplg_save_quoted(char **dst, const char *str)
++{
++ static const char nibble[16] = "0123456789abcdef";
++ unsigned char *p, *d, *t;
++ int c;
++
++ d = t = alloca(strlen(str) * 5 + 1 + 1);
++ for (p = (unsigned char *)str; *p != '\0'; p++) {
++ c = *p;
++ switch (c) {
++ case '\n':
++ *t++ = '\\';
++ *t++ = 'n';
++ break;
++ case '\t':
++ *t++ = '\\';
++ *t++ = 't';
++ break;
++ case '\v':
++ *t++ = '\\';
++ *t++ = 'v';
++ break;
++ case '\b':
++ *t++ = '\\';
++ *t++ = 'b';
++ break;
++ case '\r':
++ *t++ = '\\';
++ *t++ = 'r';
++ break;
++ case '\f':
++ *t++ = '\\';
++ *t++ = 'f';
++ break;
++ case '\'':
++ *t++ = '\\';
++ *t++ = c;
++ break;
++ default:
++ if (c >= 32 && c <= 126) {
++ *t++ = c;
++ } else {
++ *t++ = '\\';
++ *t++ = 'x';
++ *t++ = nibble[(c >> 4) & 0x0f];
++ *t++ = nibble[(c >> 0) & 0x0f];
++ }
++ break;
++ }
++ }
++ *t = '\0';
++ return tplg_save_printf(dst, NULL, "'%s'", d);
++}
++
++static int tplg_save_string(char **dst, const char *str, int id)
++{
++ const unsigned char *p = (const unsigned char *)str;
++
++ if (!p || !*p)
++ return tplg_save_printf(dst, NULL, "''");
++
++ if (!id && ((*p >= '0' && *p <= '9') || *p == '-'))
++ return tplg_save_quoted(dst, str);
++
++ if (tplg_check_quoted(p))
++ return tplg_save_quoted(dst, str);
++
++ return tplg_save_printf(dst, NULL, "%s", str);
++}
++
++static int save_config(char **dst, int level, const char *delim, snd_config_t *src)
++{
++ snd_config_iterator_t i, next;
++ snd_config_t *s;
++ const char *id;
++ char *pfx;
++ unsigned int count;
++ int type, err, quoted, array;
++
++ if (delim == NULL)
++ delim = "";
++
++ type = snd_config_get_type(src);
++ if (type != SND_CONFIG_TYPE_COMPOUND) {
++ char *val;
++ if (type == SND_CONFIG_TYPE_INTEGER ||
++ type == SND_CONFIG_TYPE_INTEGER64) {
++ err = tplg_pprint_integer(src, &val);
++ } else {
++ err = snd_config_get_ascii(src, &val);
++ }
++ if (err < 0)
++ return err;
++ if (type == SND_CONFIG_TYPE_STRING) {
++ /* hexa array pretty print */
++ id = strchr(val, '\n');
++ if (id) {
++ err = tplg_save_printf(dst, NULL, "\n");
++ if (err < 0)
++ goto retval;
++ for (id++; *id == '\t'; id++) {
++ err = tplg_save_printf(dst, NULL, "\t");
++ if (err < 0)
++ goto retval;
++ }
++ delim = "";
++ }
++ err = tplg_save_printf(dst, NULL, "%s'%s'\n", delim, val);
++ } else {
++ err = tplg_save_printf(dst, NULL, "%s%s\n", delim, val);
++ }
++retval:
++ free(val);
++ return err;
++ }
++
++ count = 0;
++ quoted = 0;
++ array = 0;
++ s = NULL;
++ snd_config_for_each(i, next, src) {
++ s = snd_config_iterator_entry(i);
++ err = snd_config_get_id(s, &id);
++ if (err < 0)
++ return err;
++ if (!quoted && tplg_check_quoted((unsigned char *)id))
++ quoted = 1;
++ if (array >= 0 && tplg_check_array_item(id, array))
++ array++;
++ else
++ array = -1;
++ count++;
++ }
++ if (count == 0)
++ return 0;
++
++ if (count == 1) {
++ err = snd_config_get_id(s, &id);
++ if (err >= 0 && level > 0)
++ err = tplg_save_printf(dst, NULL, ".");
++ if (err >= 0)
++ err = tplg_save_string(dst, id, 1);
++ if (err >= 0)
++ err = save_config(dst, level, " ", s);
++ return err;
++ }
++
++ pfx = alloca(level + 1);
++ memset(pfx, '\t', level);
++ pfx[level] = '\0';
++
++ if (level > 0) {
++ err = tplg_save_printf(dst, NULL, "%s%s\n", delim,
++ array >= 0 ? "[" : "{");
++ if (err < 0)
++ return err;
++ }
++
++ snd_config_for_each(i, next, src) {
++ s = snd_config_iterator_entry(i);
++ const char *id;
++ err = snd_config_get_id(s, &id);
++ if (err < 0)
++ return err;
++ err = tplg_save_printf(dst, pfx, "");
++ if (err < 0)
++ return err;
++ if (array < 0) {
++ delim = " ";
++ if (quoted) {
++ err = tplg_save_quoted(dst, id);
++ } else {
++ err = tplg_save_string(dst, id, 1);
++ if (err < 0)
++ return err;
++ }
++ } else {
++ delim = "";
++ }
++ err = save_config(dst, level + 1, delim, s);
++ if (err < 0)
++ return err;
++ }
++
++ if (level > 0) {
++ pfx[level - 1] = '\0';
++ err = tplg_save_printf(dst, pfx, "%s\n",
++ array >= 0 ? "]" : "}");
++ if (err < 0)
++ return err;
++ }
++
++ return 0;
++}
++
++static int tplg_save(snd_tplg_t *tplg, char **dst, int gindex, const char *prefix)
++{
++ struct tplg_table *tptr;
++ struct tplg_elem *elem;
++ struct list_head *list, *pos;
++ char pfx2[16];
++ unsigned int index;
++ int err, count;
++
++ snprintf(pfx2, sizeof(pfx2), "%s\t", prefix ?: "");
++
++ /* write all blocks */
++ for (index = 0; index < tplg_table_items; index++) {
++ tptr = &tplg_table[index];
++ list = (struct list_head *)((void *)tplg + tptr->loff);
++
++ /* count elements */
++ count = 0;
++ list_for_each(pos, list) {
++ elem = list_entry(pos, struct tplg_elem, list);
++ if (gindex >= 0 && elem->index != gindex)
++ continue;
++ if (tptr->save == NULL && tptr->gsave == NULL) {
++ SNDERR("unable to create %s block (no callback)",
++ tptr->id);
++ err = -ENXIO;
++ goto _err;
++ }
++ if (tptr->save)
++ count++;
++ }
++
++ if (count == 0)
++ continue;
++
++ if (count > 1) {
++ err = tplg_save_printf(dst, prefix, "%s {\n",
++ elem->table ?
++ elem->table->id : "_NOID_");
++ } else {
++ err = tplg_save_printf(dst, prefix, "%s.",
++ elem->table ?
++ elem->table->id : "_NOID_");
++ }
++
++ if (err < 0)
++ goto _err;
++
++ list_for_each(pos, list) {
++ elem = list_entry(pos, struct tplg_elem, list);
++ if (gindex >= 0 && elem->index != gindex)
++ continue;
++ if (count > 1) {
++ err = tplg_save_printf(dst, pfx2, "");
++ if (err < 0)
++ goto _err;
++ }
++ err = tptr->save(tplg, elem, dst, count > 1 ? pfx2 : prefix);
++ if (err < 0) {
++ SNDERR("failed to save %s elements: %s",
++ tptr->id, snd_strerror(-err));
++ goto _err;
++ }
++ }
++ if (count > 1) {
++ err = tplg_save_printf(dst, prefix, "}\n");
++ if (err < 0)
++ goto _err;
++ }
++ }
++
++ /* save globals */
++ for (index = 0; index < tplg_table_items; index++) {
++ tptr = &tplg_table[index];
++ if (tptr->gsave) {
++ err = tptr->gsave(tplg, gindex, dst, prefix);
++ if (err < 0)
++ goto _err;
++ }
++ }
++
++ return 0;
++
++_err:
++ free(*dst);
++ *dst = NULL;
++ return err;
++}
++
++static int tplg_index_compar(const void *a, const void *b)
++{
++ const int *a1 = a, *b1 = b;
++ return *a1 - *b1;
++}
++
++static int tplg_index_groups(snd_tplg_t *tplg, int **indexes)
++{
++ struct tplg_table *tptr;
++ struct tplg_elem *elem;
++ struct list_head *list, *pos;
++ unsigned int index, j, count, size;
++ int *a, *b;
++
++ count = 0;
++ size = 16;
++ a = malloc(size * sizeof(a[0]));
++
++ for (index = 0; index < tplg_table_items; index++) {
++ tptr = &tplg_table[index];
++ list = (struct list_head *)((void *)tplg + tptr->loff);
++ list_for_each(pos, list) {
++ elem = list_entry(pos, struct tplg_elem, list);
++ for (j = 0; j < count; j++) {
++ if (a[j] == elem->index)
++ break;
++ }
++ if (j < count)
++ continue;
++ if (count + 1 >= size) {
++ size += 8;
++ b = realloc(a, size * sizeof(a[0]));
++ if (b == NULL) {
++ free(a);
++ return -ENOMEM;
++ }
++ a = b;
++ }
++ a[count++] = elem->index;
++ }
++ }
++ a[count] = -1;
++
++ qsort(a, count, sizeof(a[0]), tplg_index_compar);
++
++ *indexes = a;
++ return 0;
++}
++
++int snd_tplg_save(snd_tplg_t *tplg, char **dst, int flags)
++{
++ snd_input_t *in;
++ snd_config_t *top, *top2;
++ char *dst2;
++ int *indexes, *a;
++ int err;
++
++ assert(tplg);
++ assert(dst);
++ *dst = NULL;
++
++ if (flags & SND_TPLG_SAVE_GROUPS) {
++ err = tplg_index_groups(tplg, &indexes);
++ if (err < 0)
++ return err;
++ for (a = indexes; err >= 0 && *a >= 0; a++) {
++ err = tplg_save_printf(dst, NULL,
++ "IndexGroup.%d {\n",
++ *a);
++ if (err >= 0)
++ err = tplg_save(tplg, dst, *a, "\t");
++ if (err >= 0)
++ err = tplg_save_printf(dst, NULL, "}\n");
++ }
++ free(indexes);
++ } else {
++ err = tplg_save(tplg, dst, -1, NULL);
++ }
++
++ if (err < 0)
++ goto _err;
++
++ if (flags & SND_TPLG_SAVE_NOCHECK)
++ return 0;
++
++ /* always load configuration - check */
++ err = snd_input_buffer_open(&in, *dst, strlen(*dst));
++ if (err < 0) {
++ SNDERR("could not create input buffer");
++ goto _err;
++ }
++
++ err = snd_config_top(&top);
++ if (err < 0) {
++ snd_input_close(in);
++ goto _err;
++ }
++
++ err = snd_config_load(top, in);
++ snd_input_close(in);
++ if (err < 0) {
++ SNDERR("could not load configuration");
++ snd_config_delete(top);
++ goto _err;
++ }
++
++ if (flags & SND_TPLG_SAVE_SORT) {
++ top2 = sort_config(NULL, top);
++ if (top2 == NULL) {
++ SNDERR("could not sort configuration");
++ snd_config_delete(top);
++ err = -EINVAL;
++ goto _err;
++ }
++ snd_config_delete(top);
++ top = top2;
++ }
++
++ dst2 = NULL;
++ err = save_config(&dst2, 0, NULL, top);
++ snd_config_delete(top);
++ if (err < 0) {
++ SNDERR("could not save configuration");
++ goto _err;
++ }
++
++ free(*dst);
++ *dst = dst2;
++ return 0;
++
++_err:
++ free(*dst);
++ *dst = NULL;
++ return err;
++}
+diff --git a/src/topology/text.c b/src/topology/text.c
+index f301a4ded727..e9386e7df492 100644
+--- a/src/topology/text.c
++++ b/src/topology/text.c
+@@ -89,3 +89,22 @@ int tplg_parse_text(snd_tplg_t *tplg, snd_config_t *cfg,
+
+ return err;
+ }
++
++/* save text data */
++int tplg_save_text(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
++ struct tplg_elem *elem,
++ char **dst, const char *pfx)
++{
++ struct tplg_texts *texts = elem->texts;
++ unsigned int i;
++ int err;
++
++ if (!texts || texts->num_items == 0)
++ return 0;
++ err = tplg_save_printf(dst, pfx, "'%s'.values [\n", elem->id);
++ for (i = 0; err >= 0 && i < texts->num_items; i++)
++ err = tplg_save_printf(dst, pfx, "\t'%s'\n", texts->items[i][0]);
++ if (err >= 0)
++ err = tplg_save_printf(dst, pfx, "]\n");
++ return err;
++}
+diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h
+index bea88ba35608..42a3aa96ba0e 100644
+--- a/src/topology/tplg_local.h
++++ b/src/topology/tplg_local.h
+@@ -37,6 +37,7 @@
+
+ struct tplg_ref;
+ struct tplg_elem;
++struct tplg_table;
+
+ typedef enum _snd_pcm_rates {
+ SND_PCM_RATE_UNKNOWN = -1,
+@@ -147,6 +148,8 @@ struct tplg_vendor_tuples {
+ /* topology element */
+ struct tplg_elem {
+
++ struct tplg_table *table;
++
+ char id[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+
+ int index;
+@@ -209,6 +212,10 @@ struct tplg_table {
+ unsigned enew: 1;
+ void (*free)(void *);
+ int (*parse)(snd_tplg_t *tplg, snd_config_t *cfg, void *priv);
++ int (*save)(snd_tplg_t *tplg, struct tplg_elem *elem,
++ char **dst, const char *prefix);
++ int (*gsave)(snd_tplg_t *tplg, int index,
++ char **dst, const char *prefix);
+ };
+
+ extern struct tplg_table tplg_table[];
+@@ -250,7 +257,8 @@ int tplg_build_pcm_dai(snd_tplg_t *tplg, unsigned int type);
+ int tplg_copy_data(snd_tplg_t *tplg, struct tplg_elem *elem,
+ struct tplg_ref *ref);
+
+-int tplg_parse_data_refs(snd_config_t *cfg, struct tplg_elem *elem);
++int tplg_parse_refs(snd_config_t *cfg, struct tplg_elem *elem,
++ unsigned int type);
+
+ int tplg_ref_add(struct tplg_elem *elem, int type, const char* id);
+ int tplg_ref_add_elem(struct tplg_elem *elem, struct tplg_elem *elem_ref);
+@@ -269,9 +277,11 @@ struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg,
+ int tplg_get_integer(snd_config_t *n, int *val, int base);
+ int tplg_get_unsigned(snd_config_t *n, unsigned *val, int base);
+
++const char *tplg_channel_name(int type);
+ int tplg_parse_channel(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ snd_config_t *cfg, void *private);
+
++const char *tplg_ops_name(int type);
+ int tplg_parse_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ snd_config_t *cfg, void *private);
+ int tplg_parse_ext_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+@@ -299,3 +309,49 @@ int tplg_build_links(snd_tplg_t *tplg, unsigned int type);
+ int tplg_add_link_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t);
+ int tplg_add_pcm_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t);
+ int tplg_add_dai_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t);
++
++int tplg_nice_value_format(char *dst, size_t dst_size, unsigned int value);
++
++int tplg_save_printf(char **dst, const char *prefix, const char *fmt, ...);
++int tplg_save_refs(snd_tplg_t *tplg, struct tplg_elem *elem, unsigned int type,
++ const char *id, char **dst, const char *pfx);
++int tplg_save_channels(snd_tplg_t *tplg, struct snd_soc_tplg_channel *channel,
++ unsigned int channel_count, char **dst, const char *pfx);
++int tplg_save_ops(snd_tplg_t *tplg, struct snd_soc_tplg_ctl_hdr *hdr,
++ char **dst, const char *pfx);
++int tplg_save_ext_ops(snd_tplg_t *tplg, struct snd_soc_tplg_bytes_control *be,
++ char **dst, const char *pfx);
++int tplg_save_manifest_data(snd_tplg_t *tplg, struct tplg_elem *elem,
++ char **dst, const char *pfx);
++int tplg_save_control_mixer(snd_tplg_t *tplg, struct tplg_elem *elem,
++ char **dst, const char *pfx);
++int tplg_save_control_enum(snd_tplg_t *tplg, struct tplg_elem *elem,
++ char **dst, const char *pfx);
++int tplg_save_control_bytes(snd_tplg_t *tplg, struct tplg_elem *elem,
++ char **dst, const char *pfx);
++int tplg_save_tlv(snd_tplg_t *tplg, struct tplg_elem *elem,
++ char **dst, const char *pfx);
++int tplg_save_data(snd_tplg_t *tplg, struct tplg_elem *elem,
++ char **dst, const char *pfx);
++int tplg_save_text(snd_tplg_t *tplg, struct tplg_elem *elem,
++ char **dst, const char *pfx);
++int tplg_save_tokens(snd_tplg_t *tplg, struct tplg_elem *elem,
++ char **dst, const char *pfx);
++int tplg_save_tuples(snd_tplg_t *tplg, struct tplg_elem *elem,
++ char **dst, const char *pfx);
++int tplg_save_dapm_graph(snd_tplg_t *tplg, int index,
++ char **dst, const char *pfx);
++int tplg_save_dapm_widget(snd_tplg_t *tplg, struct tplg_elem *elem,
++ char **dst, const char *pfx);
++int tplg_save_link(snd_tplg_t *tplg, struct tplg_elem *elem,
++ char **dst, const char *pfx);
++int tplg_save_cc(snd_tplg_t *tplg, struct tplg_elem *elem,
++ char **dst, const char *pfx);
++int tplg_save_pcm(snd_tplg_t *tplg, struct tplg_elem *elem,
++ char **dst, const char *pfx);
++int tplg_save_hw_config(snd_tplg_t *tplg, struct tplg_elem *elem,
++ char **dst, const char *pfx);
++int tplg_save_stream_caps(snd_tplg_t *tplg, struct tplg_elem *elem,
++ char **dst, const char *pfx);
++int tplg_save_dai(snd_tplg_t *tplg, struct tplg_elem *elem,
++ char **dst, const char *pfx);
+--
+2.16.4
+
diff --git a/0044-topology-add-snd_tplg_create-with-flags.patch b/0044-topology-add-snd_tplg_create-with-flags.patch
new file mode 100644
index 0000000..f200579
--- /dev/null
+++ b/0044-topology-add-snd_tplg_create-with-flags.patch
@@ -0,0 +1,109 @@
+From b336aea507b80493cdae439f09f710eec4bcd4ae Mon Sep 17 00:00:00 2001
+From: Jaroslav Kysela
+Date: Fri, 20 Dec 2019 14:59:00 +0100
+Subject: [PATCH 44/63] topology: add snd_tplg_create() with flags
+
+Add SND_TPLG_CREATE_VERBOSE and SND_TPLG_CREATE_DAPM_NOSORT
+flags for the special operations.
+
+Signed-off-by: Jaroslav Kysela
+---
+ include/topology.h | 10 ++++++++++
+ src/topology/dapm.c | 5 ++++-
+ src/topology/parser.c | 10 +++++++++-
+ src/topology/tplg_local.h | 1 +
+ 4 files changed, 24 insertions(+), 2 deletions(-)
+
+diff --git a/include/topology.h b/include/topology.h
+index 69aa5ed733e3..63c13a9885e9 100644
+--- a/include/topology.h
++++ b/include/topology.h
+@@ -771,12 +771,22 @@ enum snd_tplg_type {
+ /** Fit for all user cases */
+ #define SND_TPLG_INDEX_ALL 0
+
++/** Flags for the snd_tplg_create */
++#define SND_TPLG_CREATE_VERBOSE (1<<0) /*!< Verbose output */
++#define SND_TPLG_CREATE_DAPM_NOSORT (1<<1) /*!< Do not sort DAPM objects by index */
++
+ /**
+ * \brief Create a new topology parser instance.
+ * \return New topology parser instance
+ */
+ snd_tplg_t *snd_tplg_new(void);
+
++/**
++ * \brief Create a new topology parser instance.
++ * \return New topology parser instance
++ */
++snd_tplg_t *snd_tplg_create(int flags);
++
+ /**
+ * \brief Free a topology parser instance.
+ * \param tplg Topology parser instance
+diff --git a/src/topology/dapm.c b/src/topology/dapm.c
+index 2bdacedca125..d6c15fc1dfaa 100644
+--- a/src/topology/dapm.c
++++ b/src/topology/dapm.c
+@@ -268,7 +268,10 @@ struct tplg_elem *tplg_elem_new_route(snd_tplg_t *tplg, int index)
+ return NULL;
+
+ elem->index = index;
+- tplg_elem_insert(elem, &tplg->route_list);
++ if (tplg->dapm_sort)
++ tplg_elem_insert(elem, &tplg->route_list);
++ else
++ list_add_tail(&elem->list, &tplg->route_list);
+ strcpy(elem->id, "line");
+ elem->type = SND_TPLG_TYPE_DAPM_GRAPH;
+ elem->size = sizeof(*line);
+diff --git a/src/topology/parser.c b/src/topology/parser.c
+index de5edd1b6591..8f810f751533 100644
+--- a/src/topology/parser.c
++++ b/src/topology/parser.c
+@@ -432,7 +432,7 @@ static bool is_little_endian(void)
+ return false;
+ }
+
+-snd_tplg_t *snd_tplg_new(void)
++snd_tplg_t *snd_tplg_create(int flags)
+ {
+ snd_tplg_t *tplg;
+
+@@ -445,6 +445,9 @@ snd_tplg_t *snd_tplg_new(void)
+ if (!tplg)
+ return NULL;
+
++ tplg->verbose = !!(flags & SND_TPLG_CREATE_VERBOSE);
++ tplg->dapm_sort = (flags & SND_TPLG_CREATE_DAPM_NOSORT) == 0;
++
+ tplg->manifest.size = sizeof(struct snd_soc_tplg_manifest);
+
+ INIT_LIST_HEAD(&tplg->tlv_list);
+@@ -469,6 +472,11 @@ snd_tplg_t *snd_tplg_new(void)
+ return tplg;
+ }
+
++snd_tplg_t *snd_tplg_new(void)
++{
++ return snd_tplg_create(0);
++}
++
+ void snd_tplg_free(snd_tplg_t *tplg)
+ {
+ free(tplg->bin);
+diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h
+index 42a3aa96ba0e..74b3a55cada4 100644
+--- a/src/topology/tplg_local.h
++++ b/src/topology/tplg_local.h
+@@ -66,6 +66,7 @@ struct snd_tplg {
+ size_t bin_size;
+
+ int verbose;
++ unsigned int dapm_sort: 1;
+ unsigned int version;
+
+ /* runtime state */
+--
+2.16.4
+
diff --git a/0045-topology-add-snd_tplg_version-function.patch b/0045-topology-add-snd_tplg_version-function.patch
new file mode 100644
index 0000000..d8621b4
--- /dev/null
+++ b/0045-topology-add-snd_tplg_version-function.patch
@@ -0,0 +1,44 @@
+From 0793ef064a97afd0b1335af0d187ede227b90582 Mon Sep 17 00:00:00 2001
+From: Jaroslav Kysela
+Date: Fri, 20 Dec 2019 21:28:30 +0100
+Subject: [PATCH 45/63] topology: add snd_tplg_version() function
+
+Signed-off-by: Jaroslav Kysela
+---
+ include/topology.h | 6 ++++++
+ src/topology/parser.c | 5 +++++
+ 2 files changed, 11 insertions(+)
+
+diff --git a/include/topology.h b/include/topology.h
+index 63c13a9885e9..37bced1a4434 100644
+--- a/include/topology.h
++++ b/include/topology.h
+@@ -775,6 +775,12 @@ enum snd_tplg_type {
+ #define SND_TPLG_CREATE_VERBOSE (1<<0) /*!< Verbose output */
+ #define SND_TPLG_CREATE_DAPM_NOSORT (1<<1) /*!< Do not sort DAPM objects by index */
+
++/**
++ * \brief Return the version of the topology library.
++ * \return A static string with the version number.
++ */
++const char *snd_tplg_version(void);
++
+ /**
+ * \brief Create a new topology parser instance.
+ * \return New topology parser instance
+diff --git a/src/topology/parser.c b/src/topology/parser.c
+index 8f810f751533..1eaa24bd42f5 100644
+--- a/src/topology/parser.c
++++ b/src/topology/parser.c
+@@ -503,3 +503,8 @@ void snd_tplg_free(snd_tplg_t *tplg)
+
+ free(tplg);
+ }
++
++const char *snd_tplg_version(void)
++{
++ return SND_LIB_VERSION_STR;
++}
+--
+2.16.4
+
diff --git a/0046-topology-cleanup-the-SNDERR-calls.patch b/0046-topology-cleanup-the-SNDERR-calls.patch
new file mode 100644
index 0000000..495b09e
--- /dev/null
+++ b/0046-topology-cleanup-the-SNDERR-calls.patch
@@ -0,0 +1,876 @@
+From f42b2c088a23e1c6156c0d5710efd7689b8c15be Mon Sep 17 00:00:00 2001
+From: Jaroslav Kysela
+Date: Fri, 20 Dec 2019 23:48:40 +0100
+Subject: [PATCH 46/63] topology: cleanup the SNDERR() calls
+
+- remove the wrong new lines
+- remove error/warning prefixes (error is error)
+
+Signed-off-by: Jaroslav Kysela
+---
+ src/topology/builder.c | 16 +++++++--------
+ src/topology/channel.c | 2 +-
+ src/topology/ctl.c | 36 +++++++++++++++-------------------
+ src/topology/dapm.c | 40 +++++++++++++++++--------------------
+ src/topology/data.c | 53 +++++++++++++++++++++++++-------------------------
+ src/topology/parser.c | 33 +++++++++++++++----------------
+ src/topology/pcm.c | 29 +++++++++++++--------------
+ src/topology/text.c | 2 +-
+ 8 files changed, 99 insertions(+), 112 deletions(-)
+
+diff --git a/src/topology/builder.c b/src/topology/builder.c
+index cadb55830809..74c44405e653 100644
+--- a/src/topology/builder.c
++++ b/src/topology/builder.c
+@@ -65,8 +65,8 @@ static ssize_t write_block_header(snd_tplg_t *tplg, unsigned int type,
+
+ /* make sure file offset is aligned with the calculated HDR offset */
+ if (tplg->bin_pos != tplg->next_hdr_pos) {
+- SNDERR("error: New header is at offset 0x%zx but file"
+- " offset 0x%zx is %s by %ld bytes\n",
++ SNDERR("New header is at offset 0x%zx but file"
++ " offset 0x%zx is %s by %ld bytes",
+ tplg->next_hdr_pos, tplg->bin_pos,
+ tplg->bin_pos > tplg->next_hdr_pos ? "ahead" : "behind",
+ labs(tplg->bin_pos - tplg->next_hdr_pos));
+@@ -108,7 +108,7 @@ static int write_elem_block(snd_tplg_t *tplg,
+ ret = write_block_header(tplg, tplg_type, elem->vendor_type,
+ tplg->version, elem->index, block_size, count);
+ if (ret < 0) {
+- SNDERR("error: failed to write %s block %d\n",
++ SNDERR("failed to write %s block %d",
+ obj_name, ret);
+ return ret;
+ }
+@@ -148,7 +148,7 @@ static int write_elem_block(snd_tplg_t *tplg,
+
+ /* make sure we have written the correct size */
+ if (total_size != size) {
+- SNDERR("error: size mismatch. Expected %zu wrote %zu\n",
++ SNDERR("size mismatch. Expected %zu wrote %zu",
+ size, total_size);
+ return -EIO;
+ }
+@@ -221,7 +221,7 @@ static ssize_t write_manifest_data(snd_tplg_t *tplg)
+ tplg->version, 0,
+ sizeof(tplg->manifest) + tplg->manifest.priv.size, 1);
+ if (ret < 0) {
+- SNDERR("error: failed to write manifest block\n");
++ SNDERR("failed to write manifest block");
+ return ret;
+ }
+
+@@ -266,7 +266,7 @@ int tplg_write_data(snd_tplg_t *tplg)
+ /* write manifest */
+ ret = write_manifest_data(tplg);
+ if (ret < 0) {
+- SNDERR("failed to write manifest %d\n", ret);
++ SNDERR("failed to write manifest %d", ret);
+ return ret;
+ }
+
+@@ -286,7 +286,7 @@ int tplg_write_data(snd_tplg_t *tplg)
+ ret = write_elem_block(tplg, list, size,
+ tptr->tsoc, tptr->name);
+ if (ret < 0) {
+- SNDERR("failed to write %s elements: %s\n",
++ SNDERR("failed to write %s elements: %s",
+ tptr->name, snd_strerror(-ret));
+ return ret;
+ }
+@@ -295,7 +295,7 @@ int tplg_write_data(snd_tplg_t *tplg)
+ verbose(tplg, "total size is 0x%zx/%zd\n", tplg->bin_pos, tplg->bin_pos);
+
+ if (total_size != tplg->bin_pos) {
+- SNDERR("total size mismatch (%zd != %zd)\n",
++ SNDERR("total size mismatch (%zd != %zd)",
+ total_size, tplg->bin_pos);
+ return -EINVAL;
+ }
+diff --git a/src/topology/channel.c b/src/topology/channel.c
+index b54a10c89379..110775a8053d 100644
+--- a/src/topology/channel.c
++++ b/src/topology/channel.c
+@@ -104,7 +104,7 @@ int tplg_parse_channel(snd_tplg_t *tplg, snd_config_t *cfg,
+
+ channel_id = lookup_channel(id);
+ if (channel_id < 0) {
+- SNDERR("error: invalid channel %s\n", id);
++ SNDERR("invalid channel %s", id);
+ return -EINVAL;
+ }
+
+diff --git a/src/topology/ctl.c b/src/topology/ctl.c
+index 979cc1b035f1..03874b27c76f 100644
+--- a/src/topology/ctl.c
++++ b/src/topology/ctl.c
+@@ -94,7 +94,7 @@ int parse_access(snd_config_t *cfg,
+ if (strcmp(id, "access") == 0) {
+ err = parse_access_values(n, hdr);
+ if (err < 0) {
+- SNDERR("error: failed to parse access");
++ SNDERR("failed to parse access");
+ return err;
+ }
+ continue;
+@@ -187,8 +187,8 @@ static int tplg_build_mixer_control(snd_tplg_t *tplg,
+ }
+
+ if (!ref->elem) {
+- SNDERR("error: cannot find '%s' referenced by"
+- " control '%s'\n", ref->id, elem->id);
++ SNDERR("cannot find '%s' referenced by"
++ " control '%s'", ref->id, elem->id);
+ return -EINVAL;
+ } else if (err < 0)
+ return err;
+@@ -236,8 +236,8 @@ static int tplg_build_enum_control(snd_tplg_t *tplg,
+ return err;
+ }
+ if (!ref->elem) {
+- SNDERR("error: cannot find '%s' referenced by"
+- " control '%s'\n", ref->id, elem->id);
++ SNDERR("cannot find '%s' referenced by"
++ " control '%s'", ref->id, elem->id);
+ return -EINVAL;
+ }
+ }
+@@ -341,10 +341,8 @@ static int tplg_parse_tlv_dbscale(snd_config_t *cfg, struct tplg_elem *elem)
+ n = snd_config_iterator_entry(i);
+
+ /* get ID */
+- if (snd_config_get_id(n, &id) < 0) {
+- SNDERR("error: cant get ID\n");
++ if (snd_config_get_id(n, &id) < 0)
+ return -EINVAL;
+- }
+
+ /* get value */
+ if (tplg_get_integer(n, &val, 0))
+@@ -360,7 +358,7 @@ static int tplg_parse_tlv_dbscale(snd_config_t *cfg, struct tplg_elem *elem)
+ else if (strcmp(id, "mute") == 0)
+ scale->mute = val;
+ else
+- SNDERR("error: unknown key %s\n", id);
++ SNDERR("unknown id '%s'", id);
+ }
+
+ return 0;
+@@ -389,7 +387,7 @@ int tplg_parse_tlv(snd_tplg_t *tplg, snd_config_t *cfg,
+ if (strcmp(id, "scale") == 0) {
+ err = tplg_parse_tlv_dbscale(n, elem);
+ if (err < 0) {
+- SNDERR("error: failed to DBScale");
++ SNDERR("failed to DBScale");
+ return err;
+ }
+ continue;
+@@ -651,8 +649,7 @@ int tplg_parse_control_enum(snd_tplg_t *tplg, snd_config_t *cfg,
+
+ if (strcmp(id, "channel") == 0) {
+ if (ec->num_channels >= SND_SOC_TPLG_MAX_CHAN) {
+- SNDERR("error: too many channels %s\n",
+- elem->id);
++ SNDERR("too many channels %s", elem->id);
+ return -EINVAL;
+ }
+
+@@ -779,8 +776,7 @@ int tplg_parse_control_mixer(snd_tplg_t *tplg,
+
+ if (strcmp(id, "channel") == 0) {
+ if (mc->num_channels >= SND_SOC_TPLG_MAX_CHAN) {
+- SNDERR("error: too many channels %s\n",
+- elem->id);
++ SNDERR("too many channels %s", elem->id);
+ return -EINVAL;
+ }
+
+@@ -933,7 +929,7 @@ static int init_ctl_hdr(struct snd_soc_tplg_ctl_hdr *hdr,
+ struct snd_soc_tplg_tlv_dbscale *scale;
+
+ if (!tlvt) {
+- SNDERR("error: missing TLV data\n");
++ SNDERR("missing TLV data");
+ return -EINVAL;
+ }
+
+@@ -952,7 +948,7 @@ static int init_ctl_hdr(struct snd_soc_tplg_ctl_hdr *hdr,
+
+ /* TODO: add support for other TLV types */
+ default:
+- SNDERR("error: unsupported TLV type %d\n", tlv->type);
++ SNDERR("unsupported TLV type %d", tlv->type);
+ break;
+ }
+ }
+@@ -971,7 +967,7 @@ int tplg_add_mixer(snd_tplg_t *tplg, struct snd_tplg_mixer_template *mixer,
+ tplg_dbg(" Control Mixer: %s\n", mixer->hdr.name);
+
+ if (mixer->hdr.type != SND_SOC_TPLG_TYPE_MIXER) {
+- SNDERR("error: invalid mixer type %d\n", mixer->hdr.type);
++ SNDERR("invalid mixer type %d", mixer->hdr.type);
+ return -EINVAL;
+ }
+
+@@ -1039,7 +1035,7 @@ int tplg_add_enum(snd_tplg_t *tplg, struct snd_tplg_enum_template *enum_ctl,
+ tplg_dbg(" Control Enum: %s\n", enum_ctl->hdr.name);
+
+ if (enum_ctl->hdr.type != SND_SOC_TPLG_TYPE_ENUM) {
+- SNDERR("error: invalid enum type %d\n", enum_ctl->hdr.type);
++ SNDERR("invalid enum type %d", enum_ctl->hdr.type);
+ return -EINVAL;
+ }
+
+@@ -1113,7 +1109,7 @@ int tplg_add_bytes(snd_tplg_t *tplg, struct snd_tplg_bytes_template *bytes_ctl,
+ tplg_dbg(" Control Bytes: %s\n", bytes_ctl->hdr.name);
+
+ if (bytes_ctl->hdr.type != SND_SOC_TPLG_TYPE_BYTES) {
+- SNDERR("error: invalid bytes type %d\n", bytes_ctl->hdr.type);
++ SNDERR("invalid bytes type %d", bytes_ctl->hdr.type);
+ return -EINVAL;
+ }
+
+@@ -1157,7 +1153,7 @@ int tplg_add_bytes(snd_tplg_t *tplg, struct snd_tplg_bytes_template *bytes_ctl,
+ if (be->hdr.access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
+ if ((be->hdr.access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE)
+ != SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) {
+- SNDERR("error: Invalid TLV bytes control access 0x%x\n",
++ SNDERR("Invalid TLV bytes control access 0x%x",
+ be->hdr.access);
+ tplg_elem_free(elem);
+ return -EINVAL;
+diff --git a/src/topology/dapm.c b/src/topology/dapm.c
+index d6c15fc1dfaa..f61fe07157ac 100644
+--- a/src/topology/dapm.c
++++ b/src/topology/dapm.c
+@@ -154,8 +154,7 @@ static int tplg_build_widget(snd_tplg_t *tplg, struct tplg_elem *elem)
+ }
+
+ if (!ref->elem) {
+- SNDERR("error: cannot find '%s'"
+- " referenced by widget '%s'\n",
++ SNDERR("cannot find '%s' referenced by widget '%s'",
+ ref->id, elem->id);
+ return -EINVAL;
+ }
+@@ -179,8 +178,7 @@ int tplg_build_widgets(snd_tplg_t *tplg)
+
+ elem = list_entry(pos, struct tplg_elem, list);
+ if (!elem->widget || elem->type != SND_TPLG_TYPE_DAPM_WIDGET) {
+- SNDERR("error: invalid widget '%s'\n",
+- elem->id);
++ SNDERR("invalid widget '%s'", elem->id);
+ return -EINVAL;
+ }
+
+@@ -207,8 +205,7 @@ int tplg_build_routes(snd_tplg_t *tplg)
+ elem = list_entry(pos, struct tplg_elem, list);
+
+ if (!elem->route || elem->type != SND_TPLG_TYPE_DAPM_GRAPH) {
+- SNDERR("error: invalid route '%s'\n",
+- elem->id);
++ SNDERR("invalid route '%s'", elem->id);
+ return -EINVAL;
+ }
+
+@@ -218,14 +215,13 @@ int tplg_build_routes(snd_tplg_t *tplg)
+
+ /* validate sink */
+ if (strlen(route->sink) <= 0) {
+- SNDERR("error: no sink\n");
++ SNDERR("no sink");
+ return -EINVAL;
+
+ }
+ if (!tplg_elem_lookup(&tplg->widget_list, route->sink,
+ SND_TPLG_TYPE_DAPM_WIDGET, SND_TPLG_INDEX_ALL)) {
+- SNDERR("warning: undefined sink widget/stream '%s'\n",
+- route->sink);
++ SNDERR("undefined sink widget/stream '%s'", route->sink);
+ }
+
+ /* validate control name */
+@@ -234,21 +230,21 @@ int tplg_build_routes(snd_tplg_t *tplg)
+ SND_TPLG_TYPE_MIXER, elem->index) &&
+ !tplg_elem_lookup(&tplg->enum_list, route->control,
+ SND_TPLG_TYPE_ENUM, elem->index)) {
+- SNDERR("warning: Undefined mixer/enum control '%s'\n",
+- route->control);
++ SNDERR("Undefined mixer/enum control '%s'",
++ route->control);
+ }
+ }
+
+ /* validate source */
+ if (strlen(route->source) <= 0) {
+- SNDERR("error: no source\n");
++ SNDERR("no source");
+ return -EINVAL;
+
+ }
+ if (!tplg_elem_lookup(&tplg->widget_list, route->source,
+ SND_TPLG_TYPE_DAPM_WIDGET, SND_TPLG_INDEX_ALL)) {
+- SNDERR("warning: Undefined source widget/stream '%s'\n",
+- route->source);
++ SNDERR("Undefined source widget/stream '%s'",
++ route->source);
+ }
+
+ /* add graph to manifest */
+@@ -300,7 +296,7 @@ static int tplg_parse_line(const char *text,
+
+ len = strlen(buf);
+ if (len <= 2) {
+- SNDERR("error: invalid route \"%s\"\n", buf);
++ SNDERR("invalid route \"%s\"", buf);
+ return -EINVAL;
+ }
+
+@@ -309,7 +305,7 @@ static int tplg_parse_line(const char *text,
+ if (buf[i] == ',')
+ goto second;
+ }
+- SNDERR("error: invalid route \"%s\"\n", buf);
++ SNDERR("invalid route \"%s\"", buf);
+ return -EINVAL;
+
+ second:
+@@ -323,7 +319,7 @@ second:
+ goto done;
+ }
+
+- SNDERR("error: invalid route \"%s\"\n", buf);
++ SNDERR("invalid route \"%s\"", buf);
+ return -EINVAL;
+
+ done:
+@@ -378,7 +374,7 @@ int tplg_parse_dapm_graph(snd_tplg_t *tplg, snd_config_t *cfg,
+ int index = -1;
+
+ if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
+- SNDERR("error: compound is expected for dapm graph definition\n");
++ SNDERR("compound is expected for dapm graph definition");
+ return -EINVAL;
+ }
+
+@@ -401,13 +397,13 @@ int tplg_parse_dapm_graph(snd_tplg_t *tplg, snd_config_t *cfg,
+
+ if (strcmp(id, "lines") == 0) {
+ if (index < 0) {
+- SNDERR("error: failed to parse dapm graph %s, missing index\n",
++ SNDERR("failed to parse dapm graph %s, missing index",
+ graph_id);
+ return -EINVAL;
+ }
+ err = tplg_parse_routes(tplg, n, index);
+ if (err < 0) {
+- SNDERR("error: failed to parse dapm graph %s\n",
++ SNDERR("failed to parse dapm graph %s",
+ graph_id);
+ return err;
+ }
+@@ -528,7 +524,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg,
+
+ widget_type = lookup_widget(val);
+ if (widget_type < 0){
+- SNDERR("Widget '%s': Unsupported widget type %s\n",
++ SNDERR("widget '%s': Unsupported widget type %s",
+ elem->id, val);
+ return -EINVAL;
+ }
+@@ -821,7 +817,7 @@ int tplg_add_widget_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
+ break;
+
+ default:
+- SNDERR("error: widget %s: invalid type %d for ctl %d\n",
++ SNDERR("widget %s: invalid type %d for ctl %d",
+ wt->name, ct->type, i);
+ break;
+ }
+diff --git a/src/topology/data.c b/src/topology/data.c
+index 11cd73f5ed6e..f00ed3011869 100644
+--- a/src/topology/data.c
++++ b/src/topology/data.c
+@@ -61,7 +61,7 @@ struct snd_soc_tplg_private *get_priv_data(struct tplg_elem *elem)
+ priv = &elem->pcm->priv;
+ break;
+ default:
+- SNDERR("error: '%s': no support for private data for type %d\n",
++ SNDERR("'%s': no support for private data for type %d",
+ elem->id, elem->type);
+ }
+
+@@ -95,7 +95,7 @@ int tplg_parse_refs(snd_config_t *cfg, struct tplg_elem *elem,
+ }
+
+ if (cfg_type != SND_CONFIG_TYPE_COMPOUND) {
+- SNDERR("error: compound type expected for %s", elem->id);
++ SNDERR("compound type expected for %s", elem->id);
+ return -EINVAL;
+ }
+
+@@ -183,8 +183,7 @@ static int tplg_parse_data_file(snd_config_t *cfg, struct tplg_elem *elem)
+
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+- SNDERR("error: invalid data file path '%s'\n",
+- filename);
++ SNDERR("invalid data file path '%s'", filename);
+ return -errno;
+ }
+
+@@ -192,12 +191,12 @@ static int tplg_parse_data_file(snd_config_t *cfg, struct tplg_elem *elem)
+ size = ftell(fp);
+ fseek(fp, 0L, SEEK_SET);
+ if (size <= 0) {
+- SNDERR("error: invalid data file size %zu\n", size);
++ SNDERR("invalid data file size %zu", size);
+ ret = -EINVAL;
+ goto err;
+ }
+ if (size > TPLG_MAX_PRIV_SIZE) {
+- SNDERR("error: data file too big %zu\n", size);
++ SNDERR("data file too big %zu", size);
+ ret = -EINVAL;
+ goto err;
+ }
+@@ -350,7 +349,7 @@ static int get_uuid(const char *str, unsigned char *uuid_le)
+ if ((errno == ERANGE && val == ULONG_MAX)
+ || (errno != 0 && val == 0)
+ || (val > UCHAR_MAX)) {
+- SNDERR("error: invalid value for uuid\n");
++ SNDERR("invalid value for uuid");
+ ret = -EINVAL;
+ goto out;
+ }
+@@ -365,7 +364,7 @@ static int get_uuid(const char *str, unsigned char *uuid_le)
+ }
+
+ if (values < 16) {
+- SNDERR("error: less than 16 integers for uuid\n");
++ SNDERR("less than 16 integers for uuid");
+ ret = -EINVAL;
+ }
+
+@@ -446,7 +445,7 @@ static int tplg_parse_data_hex(snd_config_t *cfg, struct tplg_elem *elem,
+
+ num = get_hex_num(value);
+ if (num <= 0) {
+- SNDERR("error: malformed hex variable list %s\n", value);
++ SNDERR("malformed hex variable list %s", value);
+ return -EINVAL;
+ }
+
+@@ -454,7 +453,7 @@ static int tplg_parse_data_hex(snd_config_t *cfg, struct tplg_elem *elem,
+ priv = elem->data;
+
+ if (size > TPLG_MAX_PRIV_SIZE) {
+- SNDERR("error: data too big %d\n", size);
++ SNDERR("data too big %d", size);
+ return -EINVAL;
+ }
+
+@@ -492,7 +491,7 @@ static int get_token_value(const char *token_id,
+ return tokens->token[i].value;
+ }
+
+- SNDERR("error: cannot find token id '%s'\n", token_id);
++ SNDERR("cannot find token id '%s'", token_id);
+ return -1;
+ }
+
+@@ -579,7 +578,7 @@ static int copy_tuples(struct tplg_elem *elem,
+ * tuple_set->num_tuples;
+ size += set_size;
+ if (size > TPLG_MAX_PRIV_SIZE) {
+- SNDERR("error: data too big %d\n", size);
++ SNDERR("data too big %d", size);
+ return -EINVAL;
+ }
+
+@@ -662,13 +661,13 @@ static int build_tuples(snd_tplg_t *tplg, struct tplg_elem *elem)
+ ref->id, SND_TPLG_TYPE_TUPLE, elem->index);
+ tuples = ref->elem;
+ if (!tuples) {
+- SNDERR("error: cannot find tuples %s\n", ref->id);
++ SNDERR("cannot find tuples %s", ref->id);
+ return -EINVAL;
+ }
+
+ tokens = get_tokens(tplg, tuples);
+ if (!tokens) {
+- SNDERR("error: cannot find token for %s\n", ref->id);
++ SNDERR("cannot find token for %s", ref->id);
+ return -EINVAL;
+ }
+
+@@ -762,7 +761,7 @@ static int parse_tuple_set(snd_config_t *cfg,
+
+ type = get_tuple_type(id);
+ if (type < 0) {
+- SNDERR("error: invalid tuple type '%s'", id);
++ SNDERR("invalid tuple type '%s'", id);
+ return type;
+ }
+
+@@ -819,7 +818,7 @@ static int parse_tuple_set(snd_config_t *cfg,
+ case SND_SOC_TPLG_TUPLE_TYPE_WORD:
+ ival = tplg_get_unsigned(n, &tuple_val, 0);
+ if (ival < 0) {
+- SNDERR("error: tuple %s: %s\n", id, snd_strerror(ival));
++ SNDERR("tuple %s: %s", id, snd_strerror(ival));
+ goto err;
+ }
+
+@@ -829,7 +828,7 @@ static int parse_tuple_set(snd_config_t *cfg,
+ && tuple_val > USHRT_MAX)
+ || (type == SND_SOC_TPLG_TUPLE_TYPE_BYTE
+ && tuple_val > UCHAR_MAX)) {
+- SNDERR("error: tuple %s: invalid value\n", id);
++ SNDERR("tuple %s: invalid value", id);
+ goto err;
+ }
+
+@@ -941,7 +940,7 @@ static int parse_tuple_sets(snd_config_t *cfg,
+
+ if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
+ if (snd_config_get_id(cfg, &id) >= 0)
+- SNDERR("error: compound type expected for %s", id);
++ SNDERR("compound type expected for %s", id);
+ return -EINVAL;
+ }
+
+@@ -959,8 +958,8 @@ static int parse_tuple_sets(snd_config_t *cfg,
+ snd_config_for_each(i, next, cfg) {
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
+- SNDERR("error: compound type expected for %s, is %d",
+- id, snd_config_get_type(n));
++ SNDERR("compound type expected for %s, is %d",
++ id, snd_config_get_type(n));
+ return -EINVAL;
+ }
+
+@@ -1169,7 +1168,7 @@ int tplg_parse_manifest_data(snd_tplg_t *tplg, snd_config_t *cfg,
+ int err;
+
+ if (!list_empty(&tplg->manifest_list)) {
+- SNDERR("error: already has manifest data\n");
++ SNDERR("already has manifest data");
+ return -EINVAL;
+ }
+
+@@ -1326,7 +1325,7 @@ int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg,
+ if (strcmp(id, "file") == 0) {
+ err = tplg_parse_data_file(n, elem);
+ if (err < 0) {
+- SNDERR("error: failed to parse data file\n");
++ SNDERR("failed to parse data file");
+ return err;
+ }
+ continue;
+@@ -1335,7 +1334,7 @@ int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg,
+ if (strcmp(id, "bytes") == 0) {
+ err = tplg_parse_data_hex(n, elem, 1);
+ if (err < 0) {
+- SNDERR("error: failed to parse data bytes\n");
++ SNDERR("failed to parse data bytes");
+ return err;
+ }
+ continue;
+@@ -1344,7 +1343,7 @@ int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg,
+ if (strcmp(id, "shorts") == 0) {
+ err = tplg_parse_data_hex(n, elem, 2);
+ if (err < 0) {
+- SNDERR("error: failed to parse data shorts\n");
++ SNDERR("failed to parse data shorts");
+ return err;
+ }
+ continue;
+@@ -1353,7 +1352,7 @@ int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg,
+ if (strcmp(id, "words") == 0) {
+ err = tplg_parse_data_hex(n, elem, 4);
+ if (err < 0) {
+- SNDERR("error: failed to parse data words\n");
++ SNDERR("failed to parse data words");
+ return err;
+ }
+ continue;
+@@ -1470,8 +1469,8 @@ int tplg_copy_data(snd_tplg_t *tplg, struct tplg_elem *elem,
+ ref_elem = tplg_elem_lookup(&tplg->pdata_list,
+ ref->id, SND_TPLG_TYPE_DATA, elem->index);
+ if (!ref_elem) {
+- SNDERR("error: cannot find data '%s' referenced by"
+- " element '%s'\n", ref->id, elem->id);
++ SNDERR("cannot find data '%s' referenced by"
++ " element '%s'", ref->id, elem->id);
+ return -EINVAL;
+ }
+
+diff --git a/src/topology/parser.c b/src/topology/parser.c
+index 1eaa24bd42f5..5a5dd14f2bdd 100644
+--- a/src/topology/parser.c
++++ b/src/topology/parser.c
+@@ -122,7 +122,7 @@ int tplg_parse_compound(snd_tplg_t *tplg, snd_config_t *cfg,
+ return -EINVAL;
+
+ if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
+- SNDERR("error: compound type expected for %s", id);
++ SNDERR("compound type expected for %s", id);
+ return -EINVAL;
+ }
+
+@@ -131,7 +131,7 @@ int tplg_parse_compound(snd_tplg_t *tplg, snd_config_t *cfg,
+ n = snd_config_iterator_entry(i);
+
+ if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
+- SNDERR("error: compound type expected for %s, is %d",
++ SNDERR("compound type expected for %s, is %d",
+ id, snd_config_get_type(cfg));
+ return -EINVAL;
+ }
+@@ -155,7 +155,7 @@ static int tplg_parse_config(snd_tplg_t *tplg, snd_config_t *cfg)
+ int err;
+
+ if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
+- SNDERR("error: compound type expected at top level");
++ SNDERR("compound type expected at top level");
+ return -EINVAL;
+ }
+
+@@ -180,7 +180,7 @@ static int tplg_parse_config(snd_tplg_t *tplg, snd_config_t *cfg)
+ }
+
+ if (parser == NULL) {
+- SNDERR("error: unknown section %s\n", id);
++ SNDERR("unknown section %s", id);
+ continue;
+ }
+
+@@ -202,7 +202,7 @@ static int tplg_load_config(snd_tplg_t *tplg, snd_input_t *in)
+
+ ret = snd_config_load(top, in);
+ if (ret < 0) {
+- SNDERR("error: could not load configuration");
++ SNDERR("could not load configuration");
+ snd_config_delete(top);
+ return ret;
+ }
+@@ -210,7 +210,7 @@ static int tplg_load_config(snd_tplg_t *tplg, snd_input_t *in)
+ ret = tplg_parse_config(tplg, top);
+ snd_config_delete(top);
+ if (ret < 0) {
+- SNDERR("error: failed to parse topology");
++ SNDERR("failed to parse topology");
+ return ret;
+ }
+
+@@ -267,7 +267,7 @@ int snd_tplg_load(snd_tplg_t *tplg, const char *buf, size_t size)
+
+ err = snd_input_buffer_open(&in, buf, size);
+ if (err < 0) {
+- SNDERR("error: could not create input buffer");
++ SNDERR("could not create input buffer");
+ return err;
+ }
+
+@@ -282,13 +282,13 @@ static int tplg_build(snd_tplg_t *tplg)
+
+ err = tplg_build_integ(tplg);
+ if (err < 0) {
+- SNDERR("error: failed to check topology integrity\n");
++ SNDERR("failed to check topology integrity");
+ return err;
+ }
+
+ err = tplg_write_data(tplg);
+ if (err < 0) {
+- SNDERR("error: failed to write data %d\n", err);
++ SNDERR("failed to write data %d", err);
+ return err;
+ }
+ return 0;
+@@ -304,15 +304,14 @@ int snd_tplg_build_file(snd_tplg_t *tplg,
+
+ fp = fopen(infile, "r");
+ if (fp == NULL) {
+- SNDERR("error: could not open configuration file %s",
+- infile);
++ SNDERR("could not open configuration file %s", infile);
+ return -errno;
+ }
+
+ err = snd_input_stdio_attach(&in, fp, 1);
+ if (err < 0) {
+ fclose(fp);
+- SNDERR("error: could not attach stdio %s", infile);
++ SNDERR("could not attach stdio %s", infile);
+ return err;
+ }
+
+@@ -346,7 +345,7 @@ int snd_tplg_add_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
+ case SND_TPLG_TYPE_CC:
+ return tplg_add_link_object(tplg, t);
+ default:
+- SNDERR("error: invalid object type %d\n", t->type);
++ SNDERR("invalid object type %d", t->type);
+ return -EINVAL;
+ };
+ }
+@@ -362,18 +361,18 @@ int snd_tplg_build(snd_tplg_t *tplg, const char *outfile)
+
+ fd = open(outfile, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+ if (fd < 0) {
+- SNDERR("error: failed to open %s err %d\n", outfile, -errno);
++ SNDERR("failed to open %s err %d", outfile, -errno);
+ return -errno;
+ }
+ r = write(fd, tplg->bin, tplg->bin_size);
+ close(fd);
+ if (r < 0) {
+ err = -errno;
+- SNDERR("error: write error: %s\n", strerror(errno));
++ SNDERR("write error: %s", strerror(errno));
+ return err;
+ }
+ if ((size_t)r != tplg->bin_size) {
+- SNDERR("error: partial write (%zd != %zd)\n", r, tplg->bin_size);
++ SNDERR("partial write (%zd != %zd)", r, tplg->bin_size);
+ return -EIO;
+ }
+ return 0;
+@@ -437,7 +436,7 @@ snd_tplg_t *snd_tplg_create(int flags)
+ snd_tplg_t *tplg;
+
+ if (!is_little_endian()) {
+- SNDERR("error: cannot support big-endian machines\n");
++ SNDERR("cannot support big-endian machines");
+ return NULL;
+ }
+
+diff --git a/src/topology/pcm.c b/src/topology/pcm.c
+index d09fbe42f5da..bc3070816867 100644
+--- a/src/topology/pcm.c
++++ b/src/topology/pcm.c
+@@ -116,8 +116,8 @@ static int build_pcm(snd_tplg_t *tplg, struct tplg_elem *elem)
+ return err;
+ }
+ if (!ref->elem) {
+- SNDERR("error: cannot find '%s' referenced by"
+- " PCM '%s'\n", ref->id, elem->id);
++ SNDERR("cannot find '%s' referenced by"
++ " PCM '%s'", ref->id, elem->id);
+ return -EINVAL;
+ }
+ }
+@@ -137,7 +137,7 @@ int tplg_build_pcms(snd_tplg_t *tplg, unsigned int type)
+
+ elem = list_entry(pos, struct tplg_elem, list);
+ if (elem->type != type) {
+- SNDERR("error: invalid elem '%s'\n", elem->id);
++ SNDERR("invalid elem '%s'", elem->id);
+ return -EINVAL;
+ }
+
+@@ -196,7 +196,7 @@ int tplg_build_dais(snd_tplg_t *tplg, unsigned int type)
+
+ elem = list_entry(pos, struct tplg_elem, list);
+ if (elem->type != type) {
+- SNDERR("error: invalid elem '%s'\n", elem->id);
++ SNDERR("invalid elem '%s'", elem->id);
+ return -EINVAL;
+ }
+
+@@ -251,8 +251,8 @@ static int build_link(snd_tplg_t *tplg, struct tplg_elem *elem)
+ ref->elem = tplg_elem_lookup(&tplg->hw_cfg_list,
+ ref->id, SND_TPLG_TYPE_HW_CONFIG, elem->index);
+ if (!ref->elem) {
+- SNDERR("error: cannot find HW config '%s'"
+- " referenced by link '%s'\n",
++ SNDERR("cannot find HW config '%s'"
++ " referenced by link '%s'",
+ ref->id, elem->id);
+ return -EINVAL;
+ }
+@@ -320,7 +320,7 @@ static int split_format(struct snd_soc_tplg_stream_caps *caps, char *str)
+ while ((s != NULL) && (i < SND_SOC_TPLG_MAX_FORMATS)) {
+ format = snd_pcm_format_value(s);
+ if (format == SND_PCM_FORMAT_UNKNOWN) {
+- SNDERR("error: unsupported stream format %s\n", s);
++ SNDERR("unsupported stream format %s", s);
+ return -EINVAL;
+ }
+
+@@ -363,7 +363,7 @@ static int split_rate(struct snd_soc_tplg_stream_caps *caps, char *str)
+ rate = get_rate_value(s);
+
+ if (rate == SND_PCM_RATE_UNKNOWN) {
+- SNDERR("error: unsupported stream rate %s\n", s);
++ SNDERR("unsupported stream rate %s", s);
+ return -EINVAL;
+ }
+
+@@ -758,7 +758,7 @@ static int tplg_parse_fe_dai(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+
+ if (strcmp(id, "id") == 0) {
+ if (tplg_get_unsigned(n, &pcm->dai_id, 0)) {
+- SNDERR("error: invalid fe dai ID\n");
++ SNDERR("invalid fe dai ID");
+ return -EINVAL;
+ }
+
+@@ -1379,7 +1379,7 @@ static int get_audio_hw_format(const char *val)
+ if (strcasecmp(audio_hw_formats[i].name, val) == 0)
+ return audio_hw_formats[i].type;
+
+- SNDERR("error: invalid audio HW format %s\n", val);
++ SNDERR("invalid audio HW format %s", val);
+ return -EINVAL;
+ }
+
+@@ -1452,8 +1452,7 @@ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg,
+ /* For backwards capability,
+ * "master" == "codec is slave"
+ */
+- SNDERR("warning: deprecated bclk value '%s'\n",
+- val);
++ SNDERR("deprecated bclk value '%s'", val);
+
+ hw_cfg->bclk_master = SND_SOC_TPLG_BCLK_CS;
+ } else if (!strcmp(val, "codec_slave")) {
+@@ -1490,8 +1489,7 @@ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg,
+ /* For backwards capability,
+ * "master" == "codec is slave"
+ */
+- SNDERR("warning: deprecated fsync value '%s'\n",
+- val);
++ SNDERR("deprecated fsync value '%s'", val);
+
+ hw_cfg->fsync_master = SND_SOC_TPLG_FSYNC_CS;
+ } else if (!strcmp(val, "codec_slave")) {
+@@ -1535,8 +1533,7 @@ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg,
+ /* For backwards capability,
+ * "master" == "for codec, mclk is input"
+ */
+- SNDERR("warning: deprecated mclk value '%s'\n",
+- val);
++ SNDERR("deprecated mclk value '%s'", val);
+
+ hw_cfg->mclk_direction = SND_SOC_TPLG_MCLK_CI;
+ } else if (!strcmp(val, "codec_mclk_in")) {
+diff --git a/src/topology/text.c b/src/topology/text.c
+index e9386e7df492..6dbf22305fd3 100644
+--- a/src/topology/text.c
++++ b/src/topology/text.c
+@@ -38,7 +38,7 @@ static int parse_text_values(snd_config_t *cfg, struct tplg_elem *elem)
+ n = snd_config_iterator_entry(i);
+
+ if (j == SND_SOC_TPLG_NUM_TEXTS) {
+- tplg_dbg("error: text string number exceeds %d\n", j);
++ tplg_dbg("text string number exceeds %d\n", j);
+ return -ENOMEM;
+ }
+
+--
+2.16.4
+
diff --git a/0047-topology-dapm-fix-the-SNDERR-Undefined.patch b/0047-topology-dapm-fix-the-SNDERR-Undefined.patch
new file mode 100644
index 0000000..ac3658e
--- /dev/null
+++ b/0047-topology-dapm-fix-the-SNDERR-Undefined.patch
@@ -0,0 +1,35 @@
+From 712fbacf160f899582a9adc3f30f297211b063bb Mon Sep 17 00:00:00 2001
+From: Jaroslav Kysela
+Date: Fri, 27 Dec 2019 19:32:03 +0100
+Subject: [PATCH 47/63] topology: dapm - fix the SNDERR() - Undefined
+
+Signed-off-by: Jaroslav Kysela
+---
+ src/topology/dapm.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/topology/dapm.c b/src/topology/dapm.c
+index f61fe07157ac..88bddca3c08e 100644
+--- a/src/topology/dapm.c
++++ b/src/topology/dapm.c
+@@ -230,7 +230,7 @@ int tplg_build_routes(snd_tplg_t *tplg)
+ SND_TPLG_TYPE_MIXER, elem->index) &&
+ !tplg_elem_lookup(&tplg->enum_list, route->control,
+ SND_TPLG_TYPE_ENUM, elem->index)) {
+- SNDERR("Undefined mixer/enum control '%s'",
++ SNDERR("undefined mixer/enum control '%s'",
+ route->control);
+ }
+ }
+@@ -243,7 +243,7 @@ int tplg_build_routes(snd_tplg_t *tplg)
+ }
+ if (!tplg_elem_lookup(&tplg->widget_list, route->source,
+ SND_TPLG_TYPE_DAPM_WIDGET, SND_TPLG_INDEX_ALL)) {
+- SNDERR("Undefined source widget/stream '%s'",
++ SNDERR("undefined source widget/stream '%s'",
+ route->source);
+ }
+
+--
+2.16.4
+
diff --git a/0048-topology-fix-the-unitialized-tuples.patch b/0048-topology-fix-the-unitialized-tuples.patch
new file mode 100644
index 0000000..616e297
--- /dev/null
+++ b/0048-topology-fix-the-unitialized-tuples.patch
@@ -0,0 +1,25 @@
+From 07d779143bfd24448034cd55945b46c46407247c Mon Sep 17 00:00:00 2001
+From: Jaroslav Kysela
+Date: Sat, 28 Dec 2019 10:18:34 +0100
+Subject: [PATCH 48/63] topology: fix the unitialized tuples
+
+Signed-off-by: Jaroslav Kysela
+---
+ src/topology/data.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/topology/data.c b/src/topology/data.c
+index f00ed3011869..4e43fcc9d5a7 100644
+--- a/src/topology/data.c
++++ b/src/topology/data.c
+@@ -601,6 +601,7 @@ static int copy_tuples(struct tplg_elem *elem,
+ elem->data = priv;
+
+ array = (struct snd_soc_tplg_vendor_array *)(priv->data + off);
++ memset(array, 0, set_size);
+ array->size = set_size;
+ array->type = tuple_set->type;
+ array->num_elems = tuple_set->num_tuples;
+--
+2.16.4
+
diff --git a/0049-topology-implement-shorter-hexa-uuid-00-00-parser.patch b/0049-topology-implement-shorter-hexa-uuid-00-00-parser.patch
new file mode 100644
index 0000000..579fe94
--- /dev/null
+++ b/0049-topology-implement-shorter-hexa-uuid-00-00-parser.patch
@@ -0,0 +1,96 @@
+From 2947d83c1322bcdb31c1da180acb0f779a63dcdd Mon Sep 17 00:00:00 2001
+From: Jaroslav Kysela
+Date: Sat, 28 Dec 2019 21:44:03 +0100
+Subject: [PATCH 49/63] topology: implement shorter hexa uuid 00:00 parser
+
+Signed-off-by: Jaroslav Kysela
+---
+ src/topology/data.c | 37 +++++++++++++++++++++++++++++++------
+ 1 file changed, 31 insertions(+), 6 deletions(-)
+
+diff --git a/src/topology/data.c b/src/topology/data.c
+index 4e43fcc9d5a7..1ddd3c509e64 100644
+--- a/src/topology/data.c
++++ b/src/topology/data.c
+@@ -22,8 +22,8 @@
+ #include
+
+ #define UUID_FORMAT "\
+-0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, \
+-0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x"
++%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:\
++%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x"
+
+ /* Get private data buffer of an element */
+ struct snd_soc_tplg_private *get_priv_data(struct tplg_elem *elem)
+@@ -316,7 +316,6 @@ format2:
+ values++;
+ s += 2;
+ }
+- s++;
+ }
+
+ s++;
+@@ -341,6 +340,32 @@ static int get_uuid(const char *str, unsigned char *uuid_le)
+ if (tmp == NULL)
+ return -ENOMEM;
+
++ if (strchr(tmp, ':') == NULL)
++ goto data2;
++
++ s = strtok(tmp, ":");
++ while (s != NULL) {
++ errno = 0;
++ val = strtoul(s, NULL, 16);
++ if ((errno == ERANGE && val == ULONG_MAX)
++ || (errno != 0 && val == 0)
++ || (val > UCHAR_MAX)) {
++ SNDERR("invalid value for uuid");
++ ret = -EINVAL;
++ goto out;
++ }
++
++ *(uuid_le + values) = (unsigned char)val;
++
++ values++;
++ if (values >= 16)
++ break;
++
++ s = strtok(NULL, ":");
++ }
++ goto out;
++
++data2:
+ s = strtok(tmp, ",");
+
+ while (s != NULL) {
+@@ -354,7 +379,7 @@ static int get_uuid(const char *str, unsigned char *uuid_le)
+ goto out;
+ }
+
+- *(uuid_le + values) = (unsigned char)val;
++ *(uuid_le + values) = (unsigned char)val;
+
+ values++;
+ if (values >= 16)
+@@ -413,7 +438,7 @@ static int copy_data_hex(char *data, int off, const char *str, int width)
+ return -ENOMEM;
+
+ p += off;
+- s = strtok(tmp, ",");
++ s = strtok(tmp, ",:");
+
+ while (s != NULL) {
+ ret = write_hex(p, s, width);
+@@ -422,7 +447,7 @@ static int copy_data_hex(char *data, int off, const char *str, int width)
+ return ret;
+ }
+
+- s = strtok(NULL, ",");
++ s = strtok(NULL, ",:");
+ p += width;
+ }
+
+--
+2.16.4
+
diff --git a/0050-topology-fix-the-TPLG_DEBUG-compilation.patch b/0050-topology-fix-the-TPLG_DEBUG-compilation.patch
new file mode 100644
index 0000000..b5f54b7
--- /dev/null
+++ b/0050-topology-fix-the-TPLG_DEBUG-compilation.patch
@@ -0,0 +1,66 @@
+From 1482d1f254fe42d83d904e52c911df8693b62653 Mon Sep 17 00:00:00 2001
+From: Jaroslav Kysela
+Date: Sun, 29 Dec 2019 20:05:14 +0100
+Subject: [PATCH 50/63] topology: fix the TPLG_DEBUG compilation
+
+Signed-off-by: Jaroslav Kysela
+---
+ src/topology/channel.c | 2 +-
+ src/topology/data.c | 5 ++++-
+ src/topology/pcm.c | 2 +-
+ 3 files changed, 6 insertions(+), 3 deletions(-)
+
+diff --git a/src/topology/channel.c b/src/topology/channel.c
+index 110775a8053d..390c3f164e97 100644
+--- a/src/topology/channel.c
++++ b/src/topology/channel.c
+@@ -129,7 +129,7 @@ int tplg_parse_channel(snd_tplg_t *tplg, snd_config_t *cfg,
+ else if (strcmp(id, "shift") == 0)
+ channel->shift = value;
+
+- tplg_dbg("\t\t%s = %s\n", id, value);
++ tplg_dbg("\t\t%s = %d\n", id, value);
+ }
+
+ tplg->channel_idx++;
+diff --git a/src/topology/data.c b/src/topology/data.c
+index 1ddd3c509e64..7b4bdccde602 100644
+--- a/src/topology/data.c
++++ b/src/topology/data.c
+@@ -230,9 +230,11 @@ err:
+ return ret;
+ }
+
+-static void dump_priv_data(struct tplg_elem *elem)
++static void dump_priv_data(struct tplg_elem *elem ATTRIBUTE_UNUSED)
+ {
++#ifdef TPLG_DEBUG
+ struct snd_soc_tplg_private *priv = elem->data;
++ unsigned char *p = (unsigned char *)priv->data;
+ unsigned int i;
+
+ tplg_dbg(" elem size = %d, priv data size = %d\n",
+@@ -246,6 +248,7 @@ static void dump_priv_data(struct tplg_elem *elem)
+ }
+
+ tplg_dbg("\n\n");
++#endif
+ }
+
+ static inline int check_nibble(unsigned char c)
+diff --git a/src/topology/pcm.c b/src/topology/pcm.c
+index bc3070816867..8e5afbe6ea72 100644
+--- a/src/topology/pcm.c
++++ b/src/topology/pcm.c
+@@ -881,7 +881,7 @@ int tplg_parse_pcm(snd_tplg_t *tplg, snd_config_t *cfg,
+
+ pcm->compress = ival;
+
+- tplg_dbg("\t%s: %s\n", id, val);
++ tplg_dbg("\t%s: %d\n", id, ival);
+ continue;
+ }
+
+--
+2.16.4
+
diff --git a/0051-topology-fix-the-ops-parser-accept-integer-hexa-valu.patch b/0051-topology-fix-the-ops-parser-accept-integer-hexa-valu.patch
new file mode 100644
index 0000000..c6eea1e
--- /dev/null
+++ b/0051-topology-fix-the-ops-parser-accept-integer-hexa-valu.patch
@@ -0,0 +1,91 @@
+From c765615bce7903a0f3e3d5e7826483708398c184 Mon Sep 17 00:00:00 2001
+From: Jaroslav Kysela
+Date: Tue, 31 Dec 2019 15:27:58 +0100
+Subject: [PATCH 51/63] topology: fix the ops parser (accept integer/hexa
+ values)
+
+Signed-off-by: Jaroslav Kysela
+---
+ src/topology/ops.c | 36 +++++++++++++++++++++++++-----------
+ 1 file changed, 25 insertions(+), 11 deletions(-)
+
+diff --git a/src/topology/ops.c b/src/topology/ops.c
+index ad72ef1b2cb6..2885c7814604 100644
+--- a/src/topology/ops.c
++++ b/src/topology/ops.c
+@@ -67,6 +67,7 @@ int tplg_parse_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED, snd_config_t *cfg,
+ snd_config_t *n;
+ struct snd_soc_tplg_ctl_hdr *hdr = private;
+ const char *id, *value;
++ int ival;
+
+ tplg_dbg("\tOps\n");
+ hdr->size = sizeof(*hdr);
+@@ -80,17 +81,23 @@ int tplg_parse_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED, snd_config_t *cfg,
+ continue;
+
+ /* get value - try strings then ints */
+- if (snd_config_get_string(n, &value) < 0)
+- continue;
++ if (snd_config_get_type(n) == SND_CONFIG_TYPE_STRING) {
++ if (snd_config_get_string(n, &value) < 0)
++ continue;
++ ival = lookup_ops(value);
++ } else {
++ if (tplg_get_integer(n, &ival, 0))
++ continue;
++ }
+
+ if (strcmp(id, "info") == 0)
+- hdr->ops.info = lookup_ops(value);
++ hdr->ops.info = ival;
+ else if (strcmp(id, "put") == 0)
+- hdr->ops.put = lookup_ops(value);
++ hdr->ops.put = ival;
+ else if (strcmp(id, "get") == 0)
+- hdr->ops.get = lookup_ops(value);
++ hdr->ops.get = ival;
+
+- tplg_dbg("\t\t%s = %s\n", id, value);
++ tplg_dbg("\t\t%s = %d\n", id, ival);
+ }
+
+ return 0;
+@@ -146,6 +153,7 @@ int tplg_parse_ext_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ snd_config_t *n;
+ struct snd_soc_tplg_bytes_control *be = private;
+ const char *id, *value;
++ int ival;
+
+ tplg_dbg("\tExt Ops\n");
+
+@@ -158,15 +166,21 @@ int tplg_parse_ext_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ continue;
+
+ /* get value - try strings then ints */
+- if (snd_config_get_string(n, &value) < 0)
+- continue;
++ if (snd_config_get_type(n) == SND_CONFIG_TYPE_STRING) {
++ if (snd_config_get_string(n, &value) < 0)
++ continue;
++ ival = lookup_ops(value);
++ } else {
++ if (tplg_get_integer(n, &ival, 0))
++ continue;
++ }
+
+ if (strcmp(id, "info") == 0)
+- be->ext_ops.info = lookup_ops(value);
++ be->ext_ops.info = ival;
+ else if (strcmp(id, "put") == 0)
+- be->ext_ops.put = lookup_ops(value);
++ be->ext_ops.put = ival;
+ else if (strcmp(id, "get") == 0)
+- be->ext_ops.get = lookup_ops(value);
++ be->ext_ops.get = ival;
+
+ tplg_dbg("\t\t%s = %s\n", id, value);
+ }
+--
+2.16.4
+
diff --git a/0052-topology-fix-the-wrong-memory-access-object-realloc.patch b/0052-topology-fix-the-wrong-memory-access-object-realloc.patch
new file mode 100644
index 0000000..f867fb3
--- /dev/null
+++ b/0052-topology-fix-the-wrong-memory-access-object-realloc.patch
@@ -0,0 +1,36 @@
+From 0ba4d6d9c0ae4576f35724d2a5735990f09ceeb0 Mon Sep 17 00:00:00 2001
+From: Jaroslav Kysela
+Date: Wed, 1 Jan 2020 19:10:20 +0100
+Subject: [PATCH 52/63] topology: fix the wrong memory access (object realloc)
+
+Signed-off-by: Jaroslav Kysela
+---
+ src/topology/pcm.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/src/topology/pcm.c b/src/topology/pcm.c
+index 8e5afbe6ea72..bd72895971f5 100644
+--- a/src/topology/pcm.c
++++ b/src/topology/pcm.c
+@@ -252,8 +252,8 @@ static int build_link(snd_tplg_t *tplg, struct tplg_elem *elem)
+ ref->id, SND_TPLG_TYPE_HW_CONFIG, elem->index);
+ if (!ref->elem) {
+ SNDERR("cannot find HW config '%s'"
+- " referenced by link '%s'",
+- ref->id, elem->id);
++ " referenced by link '%s'",
++ ref->id, elem->id);
+ return -EINVAL;
+ }
+
+@@ -267,6 +267,7 @@ static int build_link(snd_tplg_t *tplg, struct tplg_elem *elem)
+ err = tplg_copy_data(tplg, elem, ref);
+ if (err < 0)
+ return err;
++ link = elem->link; /* realloc */
+ break;
+
+ default:
+--
+2.16.4
+
diff --git a/0053-topology-implement-snd_tplg_decode.patch b/0053-topology-implement-snd_tplg_decode.patch
new file mode 100644
index 0000000..d55690e
--- /dev/null
+++ b/0053-topology-implement-snd_tplg_decode.patch
@@ -0,0 +1,2375 @@
+From b6c9afb4f59bb678dc834028680d579f47dc273b Mon Sep 17 00:00:00 2001
+From: Jaroslav Kysela
+Date: Sun, 15 Dec 2019 15:24:57 +0100
+Subject: [PATCH 53/63] topology: implement snd_tplg_decode
+
+Signed-off-by: Jaroslav Kysela
+---
+ include/topology.h | 14 +-
+ src/topology/Makefile.am | 3 +-
+ src/topology/ctl.c | 450 ++++++++++++++++++++++++++++++++++++++++------
+ src/topology/dapm.c | 253 ++++++++++++++++++++++++--
+ src/topology/data.c | 440 ++++++++++++++++++++++++++++++++++++++++++++-
+ src/topology/decoder.c | 136 ++++++++++++++
+ src/topology/elem.c | 80 +++++++++
+ src/topology/parser.c | 20 ++-
+ src/topology/pcm.c | 396 ++++++++++++++++++++++++++++++++++------
+ src/topology/save.c | 3 +
+ src/topology/tplg_local.h | 71 +++++++-
+ 11 files changed, 1727 insertions(+), 139 deletions(-)
+ create mode 100644 src/topology/decoder.c
+
+diff --git a/include/topology.h b/include/topology.h
+index 37bced1a4434..1f52e66ea7b0 100644
+--- a/include/topology.h
++++ b/include/topology.h
+@@ -885,7 +885,10 @@ struct snd_tplg_ctl_template {
+ const char *name; /*!< Control name */
+ int access; /*!< Control access */
+ struct snd_tplg_io_ops_template ops; /*!< operations */
+- struct snd_tplg_tlv_template *tlv; /*!< non NULL means we have TLV data */
++ union {
++ struct snd_tplg_tlv_template *tlv; /*!< non NULL means we have TLV data */
++ struct snd_tplg_tlv_dbscale_template *tlv_scale; /*!< scale TLV data */
++ };
+ };
+
+ /** \struct snd_tplg_mixer_template
+@@ -1155,6 +1158,15 @@ int snd_tplg_set_version(snd_tplg_t *tplg, unsigned int version);
+ */
+ int snd_tplg_save(snd_tplg_t *tplg, char **dst, int flags);
+
++/**
++ * \brief Decode the binary topology contents.
++ * \param tplg Topology instance.
++ * \param bin Binary topology input buffer.
++ * \param size Binary topology input buffer size.
++ * \return Zero on success, otherwise a negative error code
++ */
++int snd_tplg_decode(snd_tplg_t *tplg, void *bin, size_t size, int dflags);
++
+ /* \} */
+
+ #ifdef __cplusplus
+diff --git a/src/topology/Makefile.am b/src/topology/Makefile.am
+index a850ec4c195c..12d1d4455883 100644
+--- a/src/topology/Makefile.am
++++ b/src/topology/Makefile.am
+@@ -28,7 +28,8 @@ libatopology_la_SOURCES =\
+ channel.c \
+ ops.c \
+ elem.c \
+- save.c
++ save.c \
++ decoder.c
+
+ noinst_HEADERS = tplg_local.h
+
+diff --git a/src/topology/ctl.c b/src/topology/ctl.c
+index 03874b27c76f..24d437aa4a2e 100644
+--- a/src/topology/ctl.c
++++ b/src/topology/ctl.c
+@@ -621,8 +621,9 @@ int tplg_parse_control_enum(snd_tplg_t *tplg, snd_config_t *cfg,
+ tplg->channel_idx = 0;
+
+ /* set channel reg to default state */
+- for (j = 0; j < SND_SOC_TPLG_MAX_CHAN; j++)
++ for (j = 0; j < SND_SOC_TPLG_MAX_CHAN; j++) {
+ ec->channel[j].reg = -1;
++ }
+
+ tplg_dbg(" Control Enum: %s\n", elem->id);
+
+@@ -896,9 +897,14 @@ int tplg_save_control_mixer(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ return err;
+ }
+
+-static int init_ctl_hdr(struct snd_soc_tplg_ctl_hdr *hdr,
+- struct snd_tplg_ctl_template *t)
++static int init_ctl_hdr(snd_tplg_t *tplg,
++ struct tplg_elem *parent,
++ struct snd_soc_tplg_ctl_hdr *hdr,
++ struct snd_tplg_ctl_template *t)
+ {
++ struct tplg_elem *elem;
++ int err;
++
+ hdr->size = sizeof(struct snd_soc_tplg_ctl_hdr);
+ hdr->type = t->type;
+
+@@ -924,7 +930,7 @@ static int init_ctl_hdr(struct snd_soc_tplg_ctl_hdr *hdr,
+ && !(hdr->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)) {
+
+ struct snd_tplg_tlv_template *tlvt = t->tlv;
+- struct snd_soc_tplg_ctl_tlv *tlv = &hdr->tlv;
++ struct snd_soc_tplg_ctl_tlv *tlv;
+ struct snd_tplg_tlv_dbscale_template *scalet;
+ struct snd_soc_tplg_tlv_dbscale *scale;
+
+@@ -933,6 +939,17 @@ static int init_ctl_hdr(struct snd_soc_tplg_ctl_hdr *hdr,
+ return -EINVAL;
+ }
+
++ elem = tplg_elem_new_common(tplg, NULL, parent->id,
++ SND_TPLG_TYPE_TLV);
++ if (!elem)
++ return -ENOMEM;
++
++ tlv = elem->tlv;
++
++ err = tplg_ref_add(parent, SND_TPLG_TYPE_TLV, parent->id);
++ if (err < 0)
++ return err;
++
+ tlv->size = sizeof(struct snd_soc_tplg_ctl_tlv);
+ tlv->type = tlvt->type;
+
+@@ -957,10 +974,10 @@ static int init_ctl_hdr(struct snd_soc_tplg_ctl_hdr *hdr,
+ }
+
+ int tplg_add_mixer(snd_tplg_t *tplg, struct snd_tplg_mixer_template *mixer,
+- struct tplg_elem **e)
++ struct tplg_elem **e)
+ {
+- struct snd_soc_tplg_private *priv = mixer->priv;
+ struct snd_soc_tplg_mixer_control *mc;
++ struct snd_soc_tplg_private *priv;
+ struct tplg_elem *elem;
+ int ret, i, num_channels;
+
+@@ -979,7 +996,7 @@ int tplg_add_mixer(snd_tplg_t *tplg, struct snd_tplg_mixer_template *mixer,
+ /* init new mixer */
+ mc = elem->mixer_ctrl;
+ mc->size = elem->size;
+- ret = init_ctl_hdr(&mc->hdr, &mixer->hdr);
++ ret = init_ctl_hdr(tplg, elem, &mc->hdr, &mixer->hdr);
+ if (ret < 0) {
+ tplg_elem_free(elem);
+ return ret;
+@@ -1000,25 +1017,20 @@ int tplg_add_mixer(snd_tplg_t *tplg, struct snd_tplg_mixer_template *mixer,
+ for (i = 0; i < num_channels; i++) {
+ struct snd_tplg_channel_elem *channel = &mixer->map->channel[i];
+
+- mc->channel[i].size = channel->size;
++ mc->channel[i].size = sizeof(mc->channel[0]);
+ mc->channel[i].reg = channel->reg;
+ mc->channel[i].shift = channel->shift;
+ mc->channel[i].id = channel->id;
+ }
+
+ /* priv data */
+- if (priv) {
+- mc = realloc(mc, elem->size + priv->size);
+- if (!mc) {
+- tplg_elem_free(elem);
+- return -ENOMEM;
+- }
+-
+- elem->mixer_ctrl = mc;
+- elem->size += priv->size;
+- mc->priv.size = priv->size;
+- memcpy(mc->priv.data, priv->data, priv->size);
+- }
++ priv = mixer->priv;
++ if (priv && priv->size > 0) {
++ ret = tplg_add_data(tplg, elem, priv,
++ sizeof(*priv) + priv->size);
++ if (ret < 0)
++ return ret;
++ }
+
+ if (e)
+ *e = elem;
+@@ -1026,11 +1038,12 @@ int tplg_add_mixer(snd_tplg_t *tplg, struct snd_tplg_mixer_template *mixer,
+ }
+
+ int tplg_add_enum(snd_tplg_t *tplg, struct snd_tplg_enum_template *enum_ctl,
+- struct tplg_elem **e)
++ struct tplg_elem **e)
+ {
+ struct snd_soc_tplg_enum_control *ec;
++ struct snd_soc_tplg_private *priv;
+ struct tplg_elem *elem;
+- int ret, i, num_items;
++ int ret, i, num_items, num_channels;
+
+ tplg_dbg(" Control Enum: %s\n", enum_ctl->hdr.name);
+
+@@ -1046,7 +1059,7 @@ int tplg_add_enum(snd_tplg_t *tplg, struct snd_tplg_enum_template *enum_ctl,
+
+ ec = elem->enum_ctrl;
+ ec->size = elem->size;
+- ret = init_ctl_hdr(&ec->hdr, &enum_ctl->hdr);
++ ret = init_ctl_hdr(tplg, elem, &ec->hdr, &enum_ctl->hdr);
+ if (ret < 0) {
+ tplg_elem_free(elem);
+ return ret;
+@@ -1058,6 +1071,22 @@ int tplg_add_enum(snd_tplg_t *tplg, struct snd_tplg_enum_template *enum_ctl,
+ ec->mask = enum_ctl->mask;
+ ec->count = enum_ctl->items;
+
++ /* set channel reg to default state */
++ for (i = 0; i < SND_SOC_TPLG_MAX_CHAN; i++)
++ ec->channel[i].reg = -1;
++
++ num_channels = enum_ctl->map ? enum_ctl->map->num_channels : 0;
++ ec->num_channels = num_channels;
++
++ for (i = 0; i < num_channels; i++) {
++ struct snd_tplg_channel_elem *channel = &enum_ctl->map->channel[i];
++
++ ec->channel[i].size = sizeof(ec->channel[0]);
++ ec->channel[i].reg = channel->reg;
++ ec->channel[i].shift = channel->shift;
++ ec->channel[i].id = channel->id;
++ }
++
+ if (enum_ctl->texts != NULL) {
+ for (i = 0; i < num_items; i++) {
+ if (enum_ctl->texts[i] != NULL)
+@@ -1077,21 +1106,13 @@ int tplg_add_enum(snd_tplg_t *tplg, struct snd_tplg_enum_template *enum_ctl,
+ }
+ }
+
+- if (enum_ctl->priv != NULL) {
+- ec = realloc(ec,
+- elem->size + enum_ctl->priv->size);
+- if (!ec) {
+- tplg_elem_free(elem);
+- return -ENOMEM;
+- }
+-
+- elem->enum_ctrl = ec;
+- elem->size += enum_ctl->priv->size;
+-
+- memcpy(ec->priv.data, enum_ctl->priv->data,
+- enum_ctl->priv->size);
+-
+- ec->priv.size = enum_ctl->priv->size;
++ /* priv data */
++ priv = enum_ctl->priv;
++ if (priv && priv->size > 0) {
++ ret = tplg_add_data(tplg, elem, priv,
++ sizeof(*priv) + priv->size);
++ if (ret < 0)
++ return ret;
+ }
+
+ if (e)
+@@ -1100,9 +1121,10 @@ int tplg_add_enum(snd_tplg_t *tplg, struct snd_tplg_enum_template *enum_ctl,
+ }
+
+ int tplg_add_bytes(snd_tplg_t *tplg, struct snd_tplg_bytes_template *bytes_ctl,
+- struct tplg_elem **e)
++ struct tplg_elem **e)
+ {
+ struct snd_soc_tplg_bytes_control *be;
++ struct snd_soc_tplg_private *priv;
+ struct tplg_elem *elem;
+ int ret;
+
+@@ -1120,7 +1142,7 @@ int tplg_add_bytes(snd_tplg_t *tplg, struct snd_tplg_bytes_template *bytes_ctl,
+
+ be = elem->bytes_ext;
+ be->size = elem->size;
+- ret = init_ctl_hdr(&be->hdr, &bytes_ctl->hdr);
++ ret = init_ctl_hdr(tplg, elem, &be->hdr, &bytes_ctl->hdr);
+ if (ret < 0) {
+ tplg_elem_free(elem);
+ return ret;
+@@ -1133,20 +1155,13 @@ int tplg_add_bytes(snd_tplg_t *tplg, struct snd_tplg_bytes_template *bytes_ctl,
+ be->ext_ops.put = bytes_ctl->ext_ops.put;
+ be->ext_ops.get = bytes_ctl->ext_ops.get;
+
+- if (bytes_ctl->priv != NULL) {
+- be = realloc(be,
+- elem->size + bytes_ctl->priv->size);
+- if (!be) {
+- tplg_elem_free(elem);
+- return -ENOMEM;
+- }
+- elem->bytes_ext = be;
+- elem->size += bytes_ctl->priv->size;
+-
+- memcpy(be->priv.data, bytes_ctl->priv->data,
+- bytes_ctl->priv->size);
+-
+- be->priv.size = bytes_ctl->priv->size;
++ /* priv data */
++ priv = bytes_ctl->priv;
++ if (priv && priv->size > 0) {
++ ret = tplg_add_data(tplg, elem, priv,
++ sizeof(*priv) + priv->size);
++ if (ret < 0)
++ return ret;
+ }
+
+ /* check on TLV bytes control */
+@@ -1184,3 +1199,330 @@ int tplg_add_bytes_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
+ {
+ return tplg_add_bytes(tplg, t->bytes_ctl, NULL);
+ }
++
++int tplg_decode_control_mixer1(snd_tplg_t *tplg,
++ struct list_head *heap,
++ struct snd_tplg_mixer_template *mt,
++ size_t pos,
++ void *bin, size_t size)
++{
++ struct snd_soc_tplg_mixer_control *mc = bin;
++ struct snd_tplg_channel_map_template *map;
++ struct snd_tplg_tlv_dbscale_template *db;
++ int i;
++
++ if (size < sizeof(*mc)) {
++ SNDERR("mixer: small size %d", size);
++ return -EINVAL;
++ }
++
++ tplg_dv(tplg, pos, "mixer: size %d TLV size %d private size %d",
++ mc->size, mc->hdr.tlv.size, mc->priv.size);
++ if (size != mc->size + mc->priv.size) {
++ SNDERR("mixer: unexpected element size %d", size);
++ return -EINVAL;
++ }
++
++ memset(mt, 0, sizeof(*mt));
++ mt->hdr.type = mc->hdr.type;
++ mt->hdr.name = mc->hdr.name;
++ mt->hdr.access = mc->hdr.access;
++ mt->hdr.ops.get = mc->hdr.ops.get;
++ mt->hdr.ops.put = mc->hdr.ops.put;
++ mt->hdr.ops.info = mc->hdr.ops.info;
++ mt->min = mc->min;
++ mt->max = mc->max;
++ mt->platform_max = mc->platform_max;
++ tplg_dv(tplg, pos, "mixer: name '%s' access 0x%x",
++ mt->hdr.name, mt->hdr.access);
++ if (mc->num_channels > 0) {
++ map = tplg_calloc(heap, sizeof(*map));
++ map->num_channels = mc->num_channels;
++ for (i = 0; i < map->num_channels; i++) {
++ map->channel[i].reg = mc->channel[i].reg;
++ map->channel[i].shift = mc->channel[i].shift;
++ map->channel[i].id = mc->channel[i].id;
++ }
++ mt->map = map;
++ }
++ if (mc->hdr.tlv.size == 0) {
++ /* nothing */
++ } else if (mc->hdr.tlv.size == sizeof(struct snd_soc_tplg_ctl_tlv)) {
++ if (mc->hdr.tlv.type != SNDRV_CTL_TLVT_DB_SCALE) {
++ SNDERR("mixer: unknown TLV type %d",
++ mc->hdr.tlv.type);
++ return -EINVAL;
++ }
++ db = tplg_calloc(heap, sizeof(*db));
++ if (db == NULL)
++ return -ENOMEM;
++ mt->hdr.tlv_scale = db;
++ db->hdr.type = mc->hdr.tlv.type;
++ db->min = mc->hdr.tlv.scale.min;
++ db->step = mc->hdr.tlv.scale.step;
++ db->mute = mc->hdr.tlv.scale.mute;
++ tplg_dv(tplg, pos, "mixer: dB scale TLV: min %d step %d mute %d",
++ db->min, db->step, db->mute);
++ } else {
++ SNDERR("mixer: wrong TLV size %d", mc->hdr.tlv.size);
++ return -EINVAL;
++ }
++
++ mt->priv = &mc->priv;
++ tplg_dv(tplg, pos + offsetof(struct snd_soc_tplg_mixer_control, priv),
++ "mixer: private start");
++ return 0;
++}
++
++int tplg_decode_control_mixer(snd_tplg_t *tplg,
++ size_t pos,
++ struct snd_soc_tplg_hdr *hdr,
++ void *bin, size_t size)
++{
++ struct list_head heap;
++ snd_tplg_obj_template_t t;
++ struct snd_tplg_mixer_template mt;
++ struct snd_soc_tplg_mixer_control *mc;
++ size_t size2;
++ int err;
++
++ err = tplg_decode_template(tplg, pos, hdr, &t);
++ if (err < 0)
++ return err;
++
++next:
++ if (size < sizeof(*mc)) {
++ SNDERR("mixer: small size %d", size);
++ return -EINVAL;
++ }
++ INIT_LIST_HEAD(&heap);
++ mc = bin;
++ size2 = mc->size + mc->priv.size;
++ if (size2 > size) {
++ SNDERR("mixer: wrong element size (%d, priv %d)",
++ mc->size, mc->priv.size);
++ return -EINVAL;
++ }
++
++ err = tplg_decode_control_mixer1(tplg, &heap, &mt, pos, bin, size2);
++ if (err >= 0) {
++ t.mixer = &mt;
++ err = snd_tplg_add_object(tplg, &t);
++ }
++ tplg_free(&heap);
++ if (err < 0)
++ return err;
++
++ bin += size2;
++ size -= size2;
++ pos += size2;
++
++ if (size > 0)
++ goto next;
++
++ return 0;
++}
++
++int tplg_decode_control_enum1(snd_tplg_t *tplg,
++ struct list_head *heap,
++ struct snd_tplg_enum_template *et,
++ size_t pos,
++ void *bin, size_t size)
++{
++ struct snd_soc_tplg_enum_control *ec = bin;
++ struct snd_tplg_channel_map_template cmt;
++ int i;
++
++ if (size < sizeof(*ec)) {
++ SNDERR("enum: small size %d", size);
++ return -EINVAL;
++ }
++
++ tplg_dv(tplg, pos, "enum: size %d private size %d",
++ ec->size, ec->priv.size);
++ if (size != ec->size + ec->priv.size) {
++ SNDERR("enum: unexpected element size %d", size);
++ return -EINVAL;
++ }
++ if (ec->num_channels > SND_TPLG_MAX_CHAN ||
++ ec->num_channels > SND_SOC_TPLG_MAX_CHAN) {
++ SNDERR("enum: unexpected channel count %d", ec->num_channels);
++ return -EINVAL;
++ }
++ if (ec->items > SND_SOC_TPLG_NUM_TEXTS) {
++ SNDERR("enum: unexpected texts count %d", ec->items);
++ return -EINVAL;
++ }
++
++ memset(et, 0, sizeof(*et));
++ et->hdr.type = ec->hdr.type;
++ et->hdr.name = ec->hdr.name;
++ et->hdr.access = ec->hdr.access;
++ et->hdr.ops.get = ec->hdr.ops.get;
++ et->hdr.ops.put = ec->hdr.ops.put;
++ et->hdr.ops.info = ec->hdr.ops.info;
++ et->mask = ec->mask;
++
++ if (ec->items > 0) {
++ et->items = ec->items;
++ et->texts = tplg_calloc(heap, sizeof(char *) * ec->items);
++ if (!et->texts)
++ return -ENOMEM;
++ for (i = 0; ec->items; i++) {
++ unsigned int j = i * sizeof(int) * ENUM_VAL_SIZE;
++ et->texts[i] = ec->texts[i];
++ et->values[i] = (int *)&ec->values[j];
++ }
++ }
++
++ et->map = &cmt;
++ memset(&cmt, 0, sizeof(cmt));
++ cmt.num_channels = ec->num_channels;
++ for (i = 0; i < cmt.num_channels; i++) {
++ struct snd_tplg_channel_elem *channel = &cmt.channel[i];
++ tplg_dv(tplg, pos + ((void *)&ec->channel[i] - (void *)ec),
++ "enum: channel size %d", ec->channel[i].size);
++ channel->reg = ec->channel[i].reg;
++ channel->shift = ec->channel[i].shift;
++ channel->id = ec->channel[i].id;
++ }
++
++ et->priv = &ec->priv;
++ return 0;
++}
++
++int tplg_decode_control_enum(snd_tplg_t *tplg,
++ size_t pos,
++ struct snd_soc_tplg_hdr *hdr,
++ void *bin, size_t size)
++{
++ struct list_head heap;
++ snd_tplg_obj_template_t t;
++ struct snd_tplg_enum_template et;
++ struct snd_soc_tplg_enum_control *ec;
++ size_t size2;
++ int err;
++
++ err = tplg_decode_template(tplg, pos, hdr, &t);
++ if (err < 0)
++ return err;
++
++next:
++ if (size < sizeof(*ec)) {
++ SNDERR("enum: small size %d", size);
++ return -EINVAL;
++ }
++ INIT_LIST_HEAD(&heap);
++ ec = bin;
++ size2 = ec->size + ec->priv.size;
++ if (size2 > size) {
++ SNDERR("enum: wrong element size (%d, priv %d)",
++ ec->size, ec->priv.size);
++ return -EINVAL;
++ }
++
++ err = tplg_decode_control_enum1(tplg, &heap, &et, pos, bin, size);
++ if (err >= 0) {
++ t.enum_ctl = &et;
++ err = snd_tplg_add_object(tplg, &t);
++ }
++ tplg_free(&heap);
++ if (err < 0)
++ return err;
++
++ bin += size2;
++ size -= size2;
++ pos += size2;
++
++ if (size > 0)
++ goto next;
++
++ return 0;
++}
++
++int tplg_decode_control_bytes1(snd_tplg_t *tplg,
++ struct snd_tplg_bytes_template *bt,
++ size_t pos,
++ void *bin, size_t size)
++{
++ struct snd_soc_tplg_bytes_control *bc = bin;
++
++ if (size < sizeof(*bc)) {
++ SNDERR("bytes: small size %d", size);
++ return -EINVAL;
++ }
++
++ tplg_dv(tplg, pos, "control bytes: size %d private size %d",
++ bc->size, bc->priv.size);
++ if (size != bc->size + bc->priv.size) {
++ SNDERR("bytes: unexpected element size %d", size);
++ return -EINVAL;
++ }
++
++ memset(bt, 0, sizeof(*bt));
++ bt->hdr.type = bc->hdr.type;
++ bt->hdr.name = bc->hdr.name;
++ bt->hdr.access = bc->hdr.access;
++ bt->hdr.ops.get = bc->hdr.ops.get;
++ bt->hdr.ops.put = bc->hdr.ops.put;
++ bt->hdr.ops.info = bc->hdr.ops.info;
++ bt->max = bc->max;
++ bt->mask = bc->mask;
++ bt->base = bc->base;
++ bt->num_regs = bc->num_regs;
++ bt->ext_ops.get = bc->ext_ops.get;
++ bt->ext_ops.put = bc->ext_ops.put;
++ bt->ext_ops.info = bc->ext_ops.info;
++ tplg_dv(tplg, pos, "control bytes: name '%s' access 0x%x",
++ bt->hdr.name, bt->hdr.access);
++
++ bt->priv = &bc->priv;
++ return 0;
++}
++
++int tplg_decode_control_bytes(snd_tplg_t *tplg,
++ size_t pos,
++ struct snd_soc_tplg_hdr *hdr,
++ void *bin, size_t size)
++{
++ snd_tplg_obj_template_t t;
++ struct snd_tplg_bytes_template bt;
++ struct snd_soc_tplg_bytes_control *bc;
++ size_t size2;
++ int err;
++
++ err = tplg_decode_template(tplg, pos, hdr, &t);
++ if (err < 0)
++ return err;
++
++next:
++ if (size < sizeof(*bc)) {
++ SNDERR("bytes: small size %d", size);
++ return -EINVAL;
++ }
++ bc = bin;
++ size2 = bc->size + bc->priv.size;
++ if (size2 > size) {
++ SNDERR("bytes: wrong element size (%d, priv %d)",
++ bc->size, bc->priv.size);
++ return -EINVAL;
++ }
++
++ err = tplg_decode_control_bytes1(tplg, &bt, pos, bin, size);
++ if (err < 0)
++ return err;
++
++ t.bytes_ctl = &bt;
++ err = snd_tplg_add_object(tplg, &t);
++ if (err < 0)
++ return err;
++
++ bin += size2;
++ size -= size2;
++ pos += size2;
++
++ if (size > 0)
++ goto next;
++
++ return 0;
++}
+diff --git a/src/topology/dapm.c b/src/topology/dapm.c
+index 88bddca3c08e..9fab2d92c150 100644
+--- a/src/topology/dapm.c
++++ b/src/topology/dapm.c
+@@ -420,19 +420,39 @@ int tplg_save_dapm_graph(snd_tplg_t *tplg, int index, char **dst, const char *pf
+ struct snd_soc_tplg_dapm_graph_elem *route;
+ struct list_head *pos;
+ struct tplg_elem *elem;
+- int err, first = 1, old_index = -1;
+- unsigned block = -1, count = 0;
++ int err, first, old_index;
++ unsigned block, count;
++ const char *fmt;
+
++ old_index = -1;
++ block = 0;
++ count = 0;
+ list_for_each(pos, &tplg->route_list) {
+ elem = list_entry(pos, struct tplg_elem, list);
+ if (!elem->route || elem->type != SND_TPLG_TYPE_DAPM_GRAPH)
+ continue;
+ if (index >= 0 && elem->index != index)
+ continue;
++ if (old_index != elem->index) {
++ block++;
++ old_index = elem->index;
++ }
+ count++;
+ }
+ if (count == 0)
+ return 0;
++ if (block < 10) {
++ fmt = "\tset%u {\n";
++ } else if (block < 100) {
++ fmt = "\tset%02u {\n";
++ } else if (block < 1000) {
++ fmt = "\tset%03u {\n";
++ } else {
++ return -EINVAL;
++ }
++ old_index = -1;
++ block = -1;
++ first = 1;
+ err = tplg_save_printf(dst, pfx, "SectionGraph {\n");
+ list_for_each(pos, &tplg->route_list) {
+ elem = list_entry(pos, struct tplg_elem, list);
+@@ -452,7 +472,7 @@ int tplg_save_dapm_graph(snd_tplg_t *tplg, int index, char **dst, const char *pf
+ old_index = elem->index;
+ block++;
+ first = 1;
+- err = tplg_save_printf(dst, pfx, "\tset%u {\n", block);
++ err = tplg_save_printf(dst, pfx, fmt, block);
+ if (err >= 0)
+ err = tplg_save_printf(dst, pfx, "\t\tindex %u\n",
+ elem->index);
+@@ -771,20 +791,14 @@ int tplg_add_widget_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
+ w->event_flags = wt->event_flags;
+ w->event_type = wt->event_type;
+
+- if (wt->priv != NULL) {
+- w = realloc(w,
+- elem->size + wt->priv->size);
+- if (!w) {
++ /* add private data */
++ if (wt->priv != NULL && wt->priv->size > 0) {
++ ret = tplg_add_data(tplg, elem, wt->priv,
++ sizeof(*wt->priv) + wt->priv->size);
++ if (ret < 0) {
+ tplg_elem_free(elem);
+- return -ENOMEM;
++ return ret;
+ }
+-
+- elem->widget = w;
+- elem->size += wt->priv->size;
+-
+- memcpy(w->priv.data, wt->priv->data,
+- wt->priv->size);
+- w->priv.size = wt->priv->size;
+ }
+
+ /* add controls to the widget's reference list */
+@@ -836,3 +850,212 @@ int tplg_add_widget_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
+
+ return 0;
+ }
++
++/* decode dapm widget from the binary input */
++int tplg_decode_dapm_widget(snd_tplg_t *tplg,
++ size_t pos,
++ struct snd_soc_tplg_hdr *hdr,
++ void *bin, size_t size)
++{
++ struct list_head heap;
++ struct snd_soc_tplg_dapm_widget *w;
++ snd_tplg_obj_template_t t;
++ struct snd_tplg_widget_template *wt;
++ struct snd_tplg_mixer_template *mt;
++ struct snd_tplg_enum_template *et;
++ struct snd_tplg_bytes_template *bt;
++ struct snd_soc_tplg_ctl_hdr *chdr;
++ struct snd_soc_tplg_mixer_control *mc;
++ struct snd_soc_tplg_enum_control *ec;
++ struct snd_soc_tplg_bytes_control *bc;
++ size_t size2;
++ unsigned int index;
++ int err;
++
++ err = tplg_decode_template(tplg, pos, hdr, &t);
++ if (err < 0)
++ return err;
++
++next:
++ INIT_LIST_HEAD(&heap);
++ w = bin;
++
++ if (size < sizeof(*w)) {
++ SNDERR("dapm widget: small size %d", size);
++ return -EINVAL;
++ }
++ if (sizeof(*w) != w->size) {
++ SNDERR("dapm widget: unknown element size %d (expected %zd)",
++ w->size, sizeof(*w));
++ return -EINVAL;
++ }
++ if (w->num_kcontrols > 16) {
++ SNDERR("dapm widget: too many kcontrols %d",
++ w->num_kcontrols);
++ return -EINVAL;
++ }
++
++ tplg_dv(tplg, pos, "dapm widget: size %d private size %d kcontrols %d",
++ w->size, w->priv.size, w->num_kcontrols);
++
++ wt = tplg_calloc(&heap, sizeof(*wt) + sizeof(void *) * w->num_kcontrols);
++ if (wt == NULL)
++ return -ENOMEM;
++ wt->id = w->id;
++ wt->name = w->name;
++ wt->sname = w->sname;
++ wt->reg = w->reg;
++ wt->shift = w->shift;
++ wt->mask = w->mask;
++ wt->subseq = w->subseq;
++ wt->invert = w->invert;
++ wt->ignore_suspend = w->ignore_suspend;
++ wt->event_flags = w->event_flags;
++ wt->event_type = w->event_type;
++
++ tplg_dv(tplg, pos, "dapm widget: name '%s' sname '%s'", wt->name, wt->sname);
++
++ if (sizeof(*w) + w->priv.size > size) {
++ SNDERR("dapm widget: wrong private data size %d",
++ w->priv.size);
++ return -EINVAL;
++ }
++
++ tplg_dv(tplg, pos + offsetof(struct snd_soc_tplg_dapm_widget, priv),
++ "dapm widget: private start");
++
++ wt->priv = &w->priv;
++ bin += sizeof(*w) + w->priv.size;
++ size -= sizeof(*w) + w->priv.size;
++ pos += sizeof(*w) + w->priv.size;
++
++ for (index = 0; index < w->num_kcontrols; index++) {
++ chdr = bin;
++ switch (chdr->type) {
++ case SND_SOC_TPLG_TYPE_MIXER:
++ mt = tplg_calloc(&heap, sizeof(*mt));
++ if (mt == NULL) {
++ err = -ENOMEM;
++ goto retval;
++ }
++ wt->ctl[index] = (void *)mt;
++ wt->num_ctls++;
++ mc = bin;
++ size2 = mc->size + mc->priv.size;
++ tplg_dv(tplg, pos, "kcontrol mixer size %zd", size2);
++ if (size2 > size) {
++ SNDERR("dapm widget: small mixer size %d",
++ size2);
++ err = -EINVAL;
++ goto retval;
++ }
++ err = tplg_decode_control_mixer1(tplg, &heap, mt, pos,
++ bin, size2);
++ break;
++ case SND_SOC_TPLG_TYPE_ENUM:
++ et = tplg_calloc(&heap, sizeof(*mt));
++ if (et == NULL) {
++ err = -ENOMEM;
++ goto retval;
++ }
++ wt->ctl[index] = (void *)et;
++ wt->num_ctls++;
++ ec = bin;
++ size2 = ec->size + ec->priv.size;
++ tplg_dv(tplg, pos, "kcontrol enum size %zd", size2);
++ if (size2 > size) {
++ SNDERR("dapm widget: small enum size %d",
++ size2);
++ err = -EINVAL;
++ goto retval;
++ }
++ err = tplg_decode_control_enum1(tplg, &heap, et, pos,
++ bin, size2);
++ break;
++ case SND_SOC_TPLG_TYPE_BYTES:
++ bt = tplg_calloc(&heap, sizeof(*bt));
++ if (bt == NULL) {
++ err = -ENOMEM;
++ goto retval;
++ }
++ wt->ctl[index] = (void *)bt;
++ wt->num_ctls++;
++ bc = bin;
++ size2 = bc->size + bc->priv.size;
++ tplg_dv(tplg, pos, "kcontrol bytes size %zd", size2);
++ if (size2 > size) {
++ SNDERR("dapm widget: small bytes size %d",
++ size2);
++ err = -EINVAL;
++ goto retval;
++ }
++ err = tplg_decode_control_bytes1(tplg, bt, pos,
++ bin, size2);
++ break;
++ default:
++ SNDERR("dapm widget: wrong control type %d",
++ chdr->type);
++ err = -EINVAL;
++ goto retval;
++ }
++ if (err < 0)
++ goto retval;
++ bin += size2;
++ size -= size2;
++ pos += size2;
++ }
++
++ t.widget = wt;
++ err = snd_tplg_add_object(tplg, &t);
++ tplg_free(&heap);
++ if (err < 0)
++ return err;
++ if (size > 0)
++ goto next;
++ return 0;
++
++retval:
++ tplg_free(&heap);
++ return err;
++}
++
++/* decode dapm link from the binary input */
++int tplg_decode_dapm_graph(snd_tplg_t *tplg,
++ size_t pos,
++ struct snd_soc_tplg_hdr *hdr,
++ void *bin, size_t size)
++{
++ struct snd_soc_tplg_dapm_graph_elem *g;
++ snd_tplg_obj_template_t t;
++ struct snd_tplg_graph_template *gt;
++ struct snd_tplg_graph_elem *ge;
++ size_t asize;
++ int err;
++
++ err = tplg_decode_template(tplg, pos, hdr, &t);
++ if (err < 0)
++ return err;
++
++ asize = sizeof(*gt) + (size / sizeof(*g)) * sizeof(*ge);
++ gt = alloca(asize);
++ memset(gt, 0, asize);
++ for (ge = gt->elem; size > 0; ge++) {
++ g = bin;
++ if (size < sizeof(*g)) {
++ SNDERR("dapm graph: small size %d", size);
++ return -EINVAL;
++ }
++ ge->src = g->source;
++ ge->ctl = g->control;
++ ge->sink = g->sink;
++ gt->count++;
++ tplg_dv(tplg, pos, "dapm graph: src='%s' ctl='%s' sink='%s'",
++ ge->src, ge->ctl, ge->sink);
++ bin += sizeof(*g);
++ size -= sizeof(*g);
++ pos += sizeof(*g);
++ }
++
++ t.graph = gt;
++ return snd_tplg_add_object(tplg, &t);
++}
+diff --git a/src/topology/data.c b/src/topology/data.c
+index 7b4bdccde602..64563920a4e2 100644
+--- a/src/topology/data.c
++++ b/src/topology/data.c
+@@ -566,7 +566,7 @@ static bool has_tuples(struct tplg_elem *elem)
+ }
+
+ /* get size of a tuple element from its type */
+-static unsigned int get_tuple_size(int type)
++unsigned int tplg_get_tuple_size(int type)
+ {
+ switch (type) {
+
+@@ -602,7 +602,7 @@ static int copy_tuples(struct tplg_elem *elem,
+ for (i = 0; i < tuples->num_sets ; i++) {
+ tuple_set = tuples->set[i];
+ set_size = sizeof(struct snd_soc_tplg_vendor_array)
+- + get_tuple_size(tuple_set->type)
++ + tplg_get_tuple_size(tuple_set->type)
+ * tuple_set->num_tuples;
+ size += set_size;
+ if (size > TPLG_MAX_PRIV_SIZE) {
+@@ -1250,6 +1250,9 @@ int tplg_save_manifest_data(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ continue;
+ count++;
+ }
++ if (count == 0)
++ return tplg_save_printf(dst, NULL,
++ "'%s'.comment 'empty'\n", elem->id);
+ if (count > 1) {
+ err = tplg_save_printf(dst, NULL, "'%s'.data [\n", elem->id);
+ if (err < 0)
+@@ -1557,3 +1560,436 @@ int tplg_build_data(snd_tplg_t *tplg)
+
+ return 0;
+ }
++
++/* decode manifest data */
++int tplg_decode_manifest_data(snd_tplg_t *tplg,
++ size_t pos,
++ struct snd_soc_tplg_hdr *hdr,
++ void *bin, size_t size)
++{
++ struct snd_soc_tplg_manifest *m = bin;
++ struct tplg_elem *elem;
++ size_t off;
++
++ if (hdr->index != 0) {
++ SNDERR("manifest - wrong index %d", hdr->index);
++ return -EINVAL;
++ }
++
++ if (sizeof(*m) > size) {
++ SNDERR("manifest - wrong size %zd (minimal %zd)",
++ size, sizeof(*m));
++ return -EINVAL;
++ }
++
++ if (m->size != sizeof(*m)) {
++ SNDERR("manifest - wrong sructure size %d", m->size);
++ return -EINVAL;
++ }
++
++ off = offsetof(struct snd_soc_tplg_manifest, priv);
++ if (off + m->priv.size > size) {
++ SNDERR("manifest - wrong private size %d", m->priv.size);
++ return -EINVAL;
++ }
++
++ tplg->manifest = *m;
++
++ bin += off;
++ size -= off;
++ pos += off;
++
++ elem = tplg_elem_new_common(tplg, NULL, "manifest",
++ SND_TPLG_TYPE_MANIFEST);
++ if (!elem)
++ return -ENOMEM;
++
++ tplg_dv(tplg, pos, "manifest: private size %d", size);
++ return tplg_add_data(tplg, elem, bin, size);
++}
++
++int tplg_add_token(snd_tplg_t *tplg, struct tplg_elem *parent,
++ unsigned int token,
++ char str_ref[SNDRV_CTL_ELEM_ID_NAME_MAXLEN])
++{
++ struct tplg_elem *elem;
++ struct tplg_token *t;
++ struct tplg_vendor_tokens *tokens;
++ unsigned int i;
++ size_t size;
++
++ elem = tplg_elem_lookup(&tplg->token_list, parent->id,
++ SND_TPLG_TYPE_TOKEN, parent->index);
++ if (elem == NULL) {
++ elem = tplg_elem_new_common(tplg, NULL, parent->id,
++ SND_TPLG_TYPE_TOKEN);
++ if (!elem)
++ return -ENOMEM;
++ }
++
++ tokens = elem->tokens;
++ if (tokens) {
++ for (i = 0; i < tokens->num_tokens; i++) {
++ t = &tokens->token[i];
++ if (t->value == token)
++ goto found;
++ }
++ size = sizeof(*tokens) +
++ (tokens->num_tokens + 1) * sizeof(struct tplg_token);
++ tokens = realloc(tokens, size);
++ } else {
++ size = sizeof(*tokens) + 1 * sizeof(struct tplg_token);
++ tokens = calloc(1, size);
++ }
++
++ if (!tokens)
++ return -ENOMEM;
++
++ memset(&tokens->token[tokens->num_tokens], 0, sizeof(struct tplg_token));
++ elem->tokens = tokens;
++ t = &tokens->token[tokens->num_tokens];
++ tokens->num_tokens++;
++ snprintf(t->id, sizeof(t->id), "token%u", token);
++ t->value = token;
++found:
++ snd_strlcpy(str_ref, t->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
++ return 0;
++}
++
++static int tplg_verify_tuple_set(snd_tplg_t *tplg, size_t pos,
++ const void *bin, size_t size)
++{
++ const struct snd_soc_tplg_vendor_array *va;
++ unsigned int j;
++
++ va = bin;
++ if (size < sizeof(*va) || size < va->size) {
++ tplg_dv(tplg, pos, "tuple set verify: wrong size %d", size);
++ return -EINVAL;
++ }
++
++ switch (va->type) {
++ case SND_SOC_TPLG_TUPLE_TYPE_UUID:
++ case SND_SOC_TPLG_TUPLE_TYPE_STRING:
++ case SND_SOC_TPLG_TUPLE_TYPE_BOOL:
++ case SND_SOC_TPLG_TUPLE_TYPE_BYTE:
++ case SND_SOC_TPLG_TUPLE_TYPE_WORD:
++ case SND_SOC_TPLG_TUPLE_TYPE_SHORT:
++ break;
++ default:
++ tplg_dv(tplg, pos, "tuple set verify: unknown array type %d", va->type);
++ return -EINVAL;
++ }
++
++ j = tplg_get_tuple_size(va->type) * va->num_elems;
++ if (j + sizeof(*va) != va->size) {
++ tplg_dv(tplg, pos, "tuple set verify: wrong vendor array size %d "
++ "(expected %d for %d count %d)",
++ va->size, j + sizeof(*va), va->type, va->num_elems);
++ return -EINVAL;
++ }
++
++ if (va->num_elems > 4096) {
++ tplg_dv(tplg, pos, "tuple set verify: tuples overflow %d", va->num_elems);
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static int tplg_decode_tuple_set(snd_tplg_t *tplg,
++ size_t pos,
++ struct tplg_elem *parent,
++ struct tplg_tuple_set **_set,
++ const void *bin, size_t size)
++{
++ const struct snd_soc_tplg_vendor_array *va;
++ struct tplg_tuple_set *set;
++ struct tplg_tuple *tuple;
++ unsigned int j;
++ int err;
++
++ va = bin;
++ if (size < sizeof(*va) || size < va->size) {
++ SNDERR("tuples: wrong size %d", size);
++ return -EINVAL;
++ }
++
++ switch (va->type) {
++ case SND_SOC_TPLG_TUPLE_TYPE_UUID:
++ case SND_SOC_TPLG_TUPLE_TYPE_STRING:
++ case SND_SOC_TPLG_TUPLE_TYPE_BOOL:
++ case SND_SOC_TPLG_TUPLE_TYPE_BYTE:
++ case SND_SOC_TPLG_TUPLE_TYPE_WORD:
++ case SND_SOC_TPLG_TUPLE_TYPE_SHORT:
++ break;
++ default:
++ SNDERR("tuples: unknown array type %d", va->type);
++ return -EINVAL;
++ }
++
++ j = tplg_get_tuple_size(va->type) * va->num_elems;
++ if (j + sizeof(*va) != va->size) {
++ SNDERR("tuples: wrong vendor array size %d "
++ "(expected %d for %d count %d)",
++ va->size, j + sizeof(*va), va->type, va->num_elems);
++ return -EINVAL;
++ }
++
++ if (va->num_elems > 4096) {
++ SNDERR("tuples: tuples overflow %d", va->num_elems);
++ return -EINVAL;
++ }
++
++ set = calloc(1, sizeof(*set) + va->num_elems * sizeof(struct tplg_tuple));
++ if (!set)
++ return -ENOMEM;
++
++ set->type = va->type;
++ set->num_tuples = va->num_elems;
++
++ tplg_dv(tplg, pos, "tuple set: type %d (%s) tuples %d size %d", set->type,
++ get_tuple_type_name(set->type), set->num_tuples, va->size);
++ for (j = 0; j < set->num_tuples; j++) {
++ tuple = &set->tuple[j];
++ switch (va->type) {
++ case SND_SOC_TPLG_TUPLE_TYPE_UUID:
++ err = tplg_add_token(tplg, parent, va->uuid[j].token,
++ tuple->token);
++ if (err < 0)
++ goto retval;
++ memcpy(tuple->uuid, va->uuid[j].uuid,
++ sizeof(va->uuid[j].uuid));
++ break;
++ case SND_SOC_TPLG_TUPLE_TYPE_STRING:
++ err = tplg_add_token(tplg, parent, va->string[j].token,
++ tuple->token);
++ if (err < 0)
++ goto retval;
++ snd_strlcpy(tuple->string, va->string[j].string,
++ sizeof(tuple->string));
++ break;
++ case SND_SOC_TPLG_TUPLE_TYPE_BOOL:
++ case SND_SOC_TPLG_TUPLE_TYPE_BYTE:
++ case SND_SOC_TPLG_TUPLE_TYPE_WORD:
++ case SND_SOC_TPLG_TUPLE_TYPE_SHORT:
++ err = tplg_add_token(tplg, parent, va->value[j].token,
++ tuple->token);
++ if (err < 0)
++ goto retval;
++ tuple->value = va->value[j].value;
++ break;
++ }
++ }
++
++ *_set = set;
++ return 0;
++
++retval:
++ free(set);
++ return err;
++}
++
++/* verify tuples from the binary input */
++static int tplg_verify_tuples(snd_tplg_t *tplg, size_t pos,
++ const void *bin, size_t size)
++{
++ const struct snd_soc_tplg_vendor_array *va;
++ int err;
++
++ if (size < sizeof(*va)) {
++ tplg_dv(tplg, pos, "tuples: small size %d", size);
++ return -EINVAL;
++ }
++
++next:
++ va = bin;
++ if (size < sizeof(*va)) {
++ tplg_dv(tplg, pos, "tuples: unexpected vendor arry size %d", size);
++ return -EINVAL;
++ }
++
++ err = tplg_verify_tuple_set(tplg, pos, va, va->size);
++ if (err < 0)
++ return err;
++
++ bin += va->size;
++ size -= va->size;
++ pos += va->size;
++ if (size > 0)
++ goto next;
++
++ return 0;
++}
++
++/* add tuples from the binary input */
++static int tplg_decode_tuples(snd_tplg_t *tplg,
++ size_t pos,
++ struct tplg_elem *parent,
++ struct tplg_vendor_tuples *tuples,
++ const void *bin, size_t size)
++{
++ const struct snd_soc_tplg_vendor_array *va;
++ struct tplg_tuple_set *set;
++ int err;
++
++ if (size < sizeof(*va)) {
++ SNDERR("tuples: small size %d", size);
++ return -EINVAL;
++ }
++
++next:
++ va = bin;
++ if (size < sizeof(*va)) {
++ SNDERR("tuples: unexpected vendor arry size %d", size);
++ return -EINVAL;
++ }
++
++ if (tuples->num_sets >= tuples->alloc_sets) {
++ SNDERR("tuples: index overflow (%d)", tuples->num_sets);
++ return -EINVAL;
++ }
++
++ err = tplg_decode_tuple_set(tplg, pos, parent, &set, va, va->size);
++ if (err < 0)
++ return err;
++ tuples->set[tuples->num_sets++] = set;
++
++ bin += va->size;
++ size -= va->size;
++ pos += va->size;
++ if (size > 0)
++ goto next;
++
++ return 0;
++}
++
++/* decode private data */
++int tplg_add_data(snd_tplg_t *tplg,
++ struct tplg_elem *parent,
++ const void *bin, size_t size)
++{
++ const struct snd_soc_tplg_private *tp;
++ const struct snd_soc_tplg_vendor_array *va;
++ struct tplg_elem *elem = NULL, *elem2 = NULL;
++ struct tplg_vendor_tuples *tuples = NULL;
++ char id[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
++ char suffix[16];
++ size_t pos = 0, off;
++ int err, num_tuples = 0, block = 0;
++
++ if (size == 0)
++ return 0;
++
++ off = offsetof(struct snd_soc_tplg_private, array);
++
++next:
++ tp = bin;
++ if (off + size < tp->size) {
++ SNDERR("data: unexpected element size %d", size);
++ return -EINVAL;
++ }
++
++ if (tplg_verify_tuples(tplg, pos, tp->array, tp->size) < 0) {
++ if (tuples) {
++ err = tplg_ref_add(elem, SND_TPLG_TYPE_TOKEN, parent->id);
++ if (err < 0)
++ return err;
++ err = tplg_ref_add(elem2, SND_TPLG_TYPE_TUPLE, id);
++ if (err < 0)
++ return err;
++ err = tplg_ref_add(parent, SND_TPLG_TYPE_DATA, id);
++ if (err < 0)
++ return err;
++ tuples = NULL;
++ }
++ tplg_dv(tplg, pos, "add bytes: size %d", tp->size);
++ snprintf(suffix, sizeof(suffix), "data%u", block++);
++ err = tplg_add_data_bytes(tplg, parent, suffix, tp->array, tp->size);
++ } else {
++ if (!tuples) {
++ snprintf(id, sizeof(id), "%.30s:tuple%d", parent->id, (block++) & 0xffff);
++ elem = tplg_elem_new_common(tplg, NULL, id, SND_TPLG_TYPE_TUPLE);
++ if (!elem)
++ return -ENOMEM;
++
++ elem2 = tplg_elem_new_common(tplg, NULL, id, SND_TPLG_TYPE_DATA);
++ if (!elem2)
++ return -ENOMEM;
++
++ tuples = calloc(1, sizeof(*tuples));
++ if (!tuples)
++ return -ENOMEM;
++ elem->tuples = tuples;
++
++ tuples->alloc_sets = (size / sizeof(*va)) + 1;
++ tuples->set = calloc(1, tuples->alloc_sets * sizeof(void *));
++ if (!tuples->set) {
++ tuples->alloc_sets = 0;
++ return -ENOMEM;
++ }
++ }
++ tplg_dv(tplg, pos, "decode tuples: size %d", tp->size);
++ err = tplg_decode_tuples(tplg, pos, parent, tuples, tp->array, tp->size);
++ num_tuples++;
++ }
++ if (err < 0)
++ return err;
++
++ bin += off + tp->size;
++ size -= off + tp->size;
++ pos += off + tp->size;
++ if (size > 0)
++ goto next;
++
++ if (tuples && elem && elem2) {
++ err = tplg_ref_add(elem, SND_TPLG_TYPE_TOKEN, parent->id);
++ if (err < 0)
++ return err;
++ err = tplg_ref_add(elem2, SND_TPLG_TYPE_TUPLE, id);
++ if (err < 0)
++ return err;
++ err = tplg_ref_add(parent, SND_TPLG_TYPE_DATA, id);
++ if (err < 0)
++ return err;
++ }
++
++ return 0;
++}
++
++/* add private data - bytes */
++int tplg_add_data_bytes(snd_tplg_t *tplg, struct tplg_elem *parent,
++ const char *suffix, const void *bin, size_t size)
++{
++ struct snd_soc_tplg_private *priv;
++ struct tplg_elem *elem;
++ char id[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
++
++ if (suffix)
++ snprintf(id, sizeof(id), "%.30s:%.12s", parent->id, suffix);
++ else
++ snd_strlcpy(id, parent->id, sizeof(id));
++ elem = tplg_elem_new_common(tplg, NULL, id, SND_TPLG_TYPE_DATA);
++ if (!elem)
++ return -ENOMEM;
++
++ priv = malloc(sizeof(*priv) + size);
++ if (!priv)
++ return -ENOMEM;
++ memcpy(priv->data, bin, size);
++ priv->size = size;
++ elem->data = priv;
++
++ return tplg_ref_add(parent, SND_TPLG_TYPE_DATA, id);
++}
++
++/* decode data from the binary input */
++int tplg_decode_data(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
++ size_t pos ATTRIBUTE_UNUSED,
++ struct snd_soc_tplg_hdr *hdr ATTRIBUTE_UNUSED,
++ void *bin ATTRIBUTE_UNUSED,
++ size_t size ATTRIBUTE_UNUSED)
++{
++ SNDERR("data type not expected");
++ return -EINVAL;
++}
+diff --git a/src/topology/decoder.c b/src/topology/decoder.c
+new file mode 100644
+index 000000000000..2d6a8969756a
+--- /dev/null
++++ b/src/topology/decoder.c
+@@ -0,0 +1,136 @@
++/*
++ Copyright (c) 2019 Red Hat Inc.
++ All rights reserved.
++
++ This library is free software; you can redistribute it and/or modify
++ it under the terms of the GNU Lesser General Public License as
++ published by the Free Software Foundation; either version 2.1 of
++ the License, or (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU Lesser General Public License for more details.
++
++ Authors: Jaroslav Kysela
++*/
++
++#include "list.h"
++#include "tplg_local.h"
++
++/* verbose output detailing each object size and file position */
++void tplg_dv(snd_tplg_t *tplg, size_t pos, const char *fmt, ...)
++{
++ va_list va;
++
++ if (!tplg->verbose)
++ return;
++
++ va_start(va, fmt);
++ fprintf(stdout, "D0x%6.6zx/%6.6zd - ", pos, pos);
++ vfprintf(stdout, fmt, va);
++ va_end(va);
++ putc('\n', stdout);
++}
++
++int tplg_decode_template(snd_tplg_t *tplg,
++ size_t pos,
++ struct snd_soc_tplg_hdr *hdr,
++ snd_tplg_obj_template_t *t)
++{
++ int type;
++
++ type = tplg_get_type(hdr->type);
++ tplg_dv(tplg, pos, "template: asoc type %d library type %d", hdr->type, type);
++ if (type < 0)
++ return type;
++
++ memset(t, 0, sizeof(*t));
++ t->type = type;
++ t->index = hdr->index;
++ t->version = hdr->version;
++ t->vendor_type = hdr->vendor_type;
++ tplg_dv(tplg, pos, "template: index %d version %d vendor_type %d",
++ hdr->index, hdr->version, hdr->vendor_type);
++ return 0;
++}
++
++int snd_tplg_decode(snd_tplg_t *tplg, void *bin, size_t size, int dflags)
++{
++ struct snd_soc_tplg_hdr *hdr;
++ struct tplg_table *tptr;
++ size_t pos;
++ void *b = bin;
++ unsigned int index;
++ int err;
++
++ if (dflags != 0)
++ return -EINVAL;
++ if (tplg == NULL || bin == NULL)
++ return -EINVAL;
++ while (1) {
++ pos = b - bin;
++ if (size == pos) {
++ tplg_dv(tplg, pos, "block: success (total %zd)", size);
++ return 0;
++ }
++ if (size - pos < sizeof(*hdr)) {
++ tplg_dv(tplg, pos, "block: small size");
++ SNDERR("incomplete header data to decode");
++ return -EINVAL;
++ }
++ hdr = b;
++ if (hdr->magic != SND_SOC_TPLG_MAGIC) {
++ SNDERR("bad block magic %08x", hdr->magic);
++ return -EINVAL;
++ }
++
++ tplg_dv(tplg, pos, "block: abi %d size %d payload size %d",
++ hdr->abi, hdr->size, hdr->payload_size);
++ if (hdr->abi != SND_SOC_TPLG_ABI_VERSION) {
++ SNDERR("unsupported ABI version %d", hdr->abi);
++ return -EINVAL;
++ }
++ if (hdr->size != sizeof(*hdr)) {
++ SNDERR("header size mismatch");
++ return -EINVAL;
++ }
++
++ if (size - pos < hdr->size + hdr->payload_size) {
++ SNDERR("incomplete payload data to decode");
++ return -EINVAL;
++ }
++
++ if (hdr->payload_size < 8) {
++ SNDERR("wrong payload size %d", hdr->payload_size);
++ return -EINVAL;
++ }
++
++ /* first block must be manifest */
++ if (b == bin) {
++ if (hdr->type != SND_SOC_TPLG_TYPE_MANIFEST) {
++ SNDERR("first block must be manifest (value %d)", hdr->type);
++ return -EINVAL;
++ }
++ err = snd_tplg_set_version(tplg, hdr->version);
++ if (err < 0)
++ return err;
++ }
++
++ pos += hdr->size;
++ for (index = 0; index < tplg_table_items; index++) {
++ tptr = &tplg_table[index];
++ if (tptr->tsoc == (int)hdr->type)
++ break;
++ }
++ if (index >= tplg_table_items || tptr->decod == NULL) {
++ SNDERR("unknown block type %d", hdr->type);
++ return -EINVAL;
++ }
++ tplg_dv(tplg, pos, "block: type %d - %s", hdr->type, tptr->name);
++ err = tptr->decod(tplg, pos, hdr, b + hdr->size, hdr->payload_size);
++ if (err < 0)
++ return err;
++ b += hdr->size + hdr->payload_size;
++ }
++}
+diff --git a/src/topology/elem.c b/src/topology/elem.c
+index 89aed1fc0449..ed5b5f13054f 100644
+--- a/src/topology/elem.c
++++ b/src/topology/elem.c
+@@ -31,6 +31,7 @@ struct tplg_table tplg_table[] = {
+ .enew = 1,
+ .parse = tplg_parse_manifest_data,
+ .save = tplg_save_manifest_data,
++ .decod = tplg_decode_manifest_data,
+ },
+ {
+ .name = "control mixer",
+@@ -43,6 +44,7 @@ struct tplg_table tplg_table[] = {
+ .enew = 1,
+ .parse = tplg_parse_control_mixer,
+ .save = tplg_save_control_mixer,
++ .decod = tplg_decode_control_mixer,
+ },
+ {
+ .name = "control enum",
+@@ -55,6 +57,7 @@ struct tplg_table tplg_table[] = {
+ .enew = 1,
+ .parse = tplg_parse_control_enum,
+ .save = tplg_save_control_enum,
++ .decod = tplg_decode_control_enum,
+ },
+ {
+ .name = "control extended (bytes)",
+@@ -67,6 +70,7 @@ struct tplg_table tplg_table[] = {
+ .enew = 1,
+ .parse = tplg_parse_control_bytes,
+ .save = tplg_save_control_bytes,
++ .decod = tplg_decode_control_bytes,
+ },
+ {
+ .name = "dapm widget",
+@@ -79,6 +83,7 @@ struct tplg_table tplg_table[] = {
+ .enew = 1,
+ .parse = tplg_parse_dapm_widget,
+ .save = tplg_save_dapm_widget,
++ .decod = tplg_decode_dapm_widget,
+ },
+ {
+ .name = "pcm",
+@@ -91,6 +96,7 @@ struct tplg_table tplg_table[] = {
+ .enew = 1,
+ .parse = tplg_parse_pcm,
+ .save = tplg_save_pcm,
++ .decod = tplg_decode_pcm,
+ },
+ {
+ .name = "physical dai",
+@@ -103,6 +109,7 @@ struct tplg_table tplg_table[] = {
+ .enew = 1,
+ .parse = tplg_parse_dai,
+ .save = tplg_save_dai,
++ .decod = tplg_decode_dai,
+ },
+ {
+ .name = "be",
+@@ -116,6 +123,7 @@ struct tplg_table tplg_table[] = {
+ .enew = 1,
+ .parse = tplg_parse_link,
+ .save = tplg_save_link,
++ .decod = tplg_decode_link,
+ },
+ {
+ .name = "cc",
+@@ -128,6 +136,7 @@ struct tplg_table tplg_table[] = {
+ .enew = 1,
+ .parse = tplg_parse_cc,
+ .save = tplg_save_cc,
++ .decod = tplg_decode_cc,
+ },
+ {
+ .name = "route (dapm graph)",
+@@ -138,6 +147,7 @@ struct tplg_table tplg_table[] = {
+ .build = 1,
+ .parse = tplg_parse_dapm_graph,
+ .gsave = tplg_save_dapm_graph,
++ .decod = tplg_decode_dapm_graph,
+ },
+ {
+ .name = "private data",
+@@ -149,6 +159,7 @@ struct tplg_table tplg_table[] = {
+ .enew = 1,
+ .parse = tplg_parse_data,
+ .save = tplg_save_data,
++ .decod = tplg_decode_data,
+ },
+ {
+ .name = "text",
+@@ -220,6 +231,17 @@ struct tplg_table tplg_table[] = {
+
+ unsigned int tplg_table_items = ARRAY_SIZE(tplg_table);
+
++int tplg_get_type(int asoc_type)
++{
++ unsigned int index;
++
++ for (index = 0; index < tplg_table_items; index++)
++ if (tplg_table[index].tsoc == asoc_type)
++ return tplg_table[index].type;
++ SNDERR("uknown asoc type %d", asoc_type);
++ return -EINVAL;
++}
++
+ int tplg_ref_add(struct tplg_elem *elem, int type, const char* id)
+ {
+ struct tplg_ref *ref;
+@@ -331,6 +353,36 @@ struct tplg_elem *tplg_elem_lookup(struct list_head *base, const char* id,
+ return NULL;
+ }
+
++/* find an element by type */
++struct tplg_elem *tplg_elem_type_lookup(snd_tplg_t *tplg,
++ enum snd_tplg_type type)
++{
++ struct tplg_table *tptr;
++ struct list_head *pos, *list;
++ struct tplg_elem *elem;
++ unsigned int index;
++
++ for (index = 0; index < tplg_table_items; index++) {
++ tptr = &tplg_table[index];
++ if (!tptr->enew)
++ continue;
++ if ((int)type != tptr->type)
++ continue;
++ break;
++ }
++ if (index >= tplg_table_items)
++ return NULL;
++
++ list = (struct list_head *)((void *)tplg + tptr->loff);
++
++ /* return only first element */
++ list_for_each(pos, list) {
++ elem = list_entry(pos, struct tplg_elem, list);
++ return elem;
++ }
++ return NULL;
++}
++
+ /* insert a new element into list in the ascending order of index value */
+ void tplg_elem_insert(struct tplg_elem *elem_p, struct list_head *list)
+ {
+@@ -428,3 +480,31 @@ struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg,
+ elem->type = type;
+ return elem;
+ }
++
++struct tplg_alloc {
++ struct list_head list;
++ void *data[0];
++};
++
++void *tplg_calloc(struct list_head *heap, size_t size)
++{
++ struct tplg_alloc *a;
++
++ a = calloc(1, sizeof(*a) + size);
++ if (a == NULL)
++ return NULL;
++ list_add_tail(&a->list, heap);
++ return a->data;
++}
++
++void tplg_free(struct list_head *heap)
++{
++ struct list_head *pos, *npos;
++ struct tplg_alloc *a;
++
++ list_for_each_safe(pos, npos, heap) {
++ a = list_entry(pos, struct tplg_alloc, list);
++ list_del(&a->list);
++ free(a);
++ }
++}
+diff --git a/src/topology/parser.c b/src/topology/parser.c
+index 5a5dd14f2bdd..d7783a93ab6b 100644
+--- a/src/topology/parser.c
++++ b/src/topology/parser.c
+@@ -396,17 +396,21 @@ int snd_tplg_build_bin(snd_tplg_t *tplg,
+
+ int snd_tplg_set_manifest_data(snd_tplg_t *tplg, const void *data, int len)
+ {
++ struct tplg_elem *elem;
++
++ elem = tplg_elem_type_lookup(tplg, SND_TPLG_TYPE_MANIFEST);
++ if (elem == NULL) {
++ elem = tplg_elem_new_common(tplg, NULL, "manifest",
++ SND_TPLG_TYPE_MANIFEST);
++ if (!elem)
++ return -ENOMEM;
++ tplg->manifest.size = elem->size;
++ }
++
+ if (len <= 0)
+ return 0;
+
+- tplg->manifest.priv.size = len;
+-
+- tplg->manifest_pdata = malloc(len);
+- if (!tplg->manifest_pdata)
+- return -ENOMEM;
+-
+- memcpy(tplg->manifest_pdata, data, len);
+- return 0;
++ return tplg_add_data_bytes(tplg, elem, NULL, data, len);
+ }
+
+ int snd_tplg_set_version(snd_tplg_t *tplg, unsigned int version)
+diff --git a/src/topology/pcm.c b/src/topology/pcm.c
+index bd72895971f5..4e04a6bc3d78 100644
+--- a/src/topology/pcm.c
++++ b/src/topology/pcm.c
+@@ -1679,11 +1679,20 @@ static void tplg_add_stream_object(struct snd_soc_tplg_stream *strm,
+ strm->channels = strm_tpl->channels;
+ }
+
+-static void tplg_add_stream_caps(struct snd_soc_tplg_stream_caps *caps,
+- struct snd_tplg_stream_caps_template *caps_tpl)
++static int tplg_add_stream_caps(snd_tplg_t *tplg,
++ struct snd_tplg_stream_caps_template *caps_tpl)
+ {
+- snd_strlcpy(caps->name, caps_tpl->name,
+- SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
++ struct snd_soc_tplg_stream_caps *caps;
++ struct tplg_elem *elem;
++
++ elem = tplg_elem_new_common(tplg, NULL, caps_tpl->name,
++ SND_TPLG_TYPE_STREAM_CAPS);
++ if (!elem)
++ return -ENOMEM;
++
++ caps = elem->stream_caps;
++
++ snd_strlcpy(caps->name, caps_tpl->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+
+ caps->formats = caps_tpl->formats;
+ caps->rates = caps_tpl->rates;
+@@ -1698,15 +1707,17 @@ static void tplg_add_stream_caps(struct snd_soc_tplg_stream_caps *caps,
+ caps->buffer_size_min = caps_tpl->buffer_size_min;
+ caps->buffer_size_max = caps_tpl->buffer_size_max;
+ caps->sig_bits = caps_tpl->sig_bits;
++ return 0;
+ }
+
+ /* Add a PCM element (FE DAI & DAI link) from C API */
+ int tplg_add_pcm_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
+ {
+ struct snd_tplg_pcm_template *pcm_tpl = t->pcm;
+- struct snd_soc_tplg_pcm *pcm, *_pcm;
++ struct snd_soc_tplg_private *priv;
++ struct snd_soc_tplg_pcm *pcm;
+ struct tplg_elem *elem;
+- int i;
++ int ret, i;
+
+ tplg_dbg("PCM: %s, DAI %s\n", pcm_tpl->pcm_name, pcm_tpl->dai_name);
+
+@@ -1732,8 +1743,13 @@ int tplg_add_pcm_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
+ pcm->compress = pcm_tpl->compress;
+
+ for (i = 0; i < 2; i++) {
+- if (pcm_tpl->caps[i])
+- tplg_add_stream_caps(&pcm->caps[i], pcm_tpl->caps[i]);
++ if (!pcm_tpl->caps[i] || !pcm_tpl->caps[i]->name)
++ continue;
++ ret = tplg_add_stream_caps(tplg, pcm_tpl->caps[i]);
++ if (ret < 0)
++ return ret;
++ snd_strlcpy(pcm->caps[i].name, pcm_tpl->caps[i]->name,
++ sizeof(pcm->caps[i].name));
+ }
+
+ pcm->flag_mask = pcm_tpl->flag_mask;
+@@ -1744,22 +1760,12 @@ int tplg_add_pcm_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
+ tplg_add_stream_object(&pcm->stream[i], &pcm_tpl->stream[i]);
+
+ /* private data */
+- if (pcm_tpl->priv != NULL && pcm_tpl->priv->size) {
+- tplg_dbg("\t priv data size %d\n", pcm_tpl->priv->size);
+- _pcm = realloc(pcm,
+- elem->size + pcm_tpl->priv->size);
+- if (!_pcm) {
+- tplg_elem_free(elem);
+- return -ENOMEM;
+- }
+-
+- pcm = _pcm;
+- elem->pcm = pcm;
+- elem->size += pcm_tpl->priv->size;
+-
+- memcpy(pcm->priv.data, pcm_tpl->priv->data,
+- pcm_tpl->priv->size);
+- pcm->priv.size = pcm_tpl->priv->size;
++ priv = pcm_tpl->priv;
++ if (priv && priv->size > 0) {
++ ret = tplg_add_data(tplg, elem, priv,
++ sizeof(*priv) + priv->size);
++ if (ret < 0)
++ return ret;
+ }
+
+ return 0;
+@@ -1810,9 +1816,11 @@ static int set_link_hw_config(struct snd_soc_tplg_hw_config *cfg,
+ int tplg_add_link_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
+ {
+ struct snd_tplg_link_template *link_tpl = t->link;
+- struct snd_soc_tplg_link_config *link, *_link;
++ struct snd_soc_tplg_link_config *link;
++ struct snd_soc_tplg_private *priv;
+ struct tplg_elem *elem;
+ unsigned int i;
++ int ret;
+
+ if (t->type != SND_TPLG_TYPE_LINK && t->type != SND_TPLG_TYPE_BE
+ && t->type != SND_TPLG_TYPE_CC)
+@@ -1854,21 +1862,12 @@ int tplg_add_link_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
+ link->flags = link_tpl->flags;
+
+ /* private data */
+- if (link_tpl->priv != NULL && link_tpl->priv->size) {
+- _link = realloc(link,
+- elem->size + link_tpl->priv->size);
+- if (!_link) {
+- tplg_elem_free(elem);
+- return -ENOMEM;
+- }
+-
+- link = _link;
+- elem->link = link;
+- elem->size += link_tpl->priv->size;
+-
+- memcpy(link->priv.data, link_tpl->priv->data,
+- link_tpl->priv->size);
+- link->priv.size = link_tpl->priv->size;
++ priv = link_tpl->priv;
++ if (priv && priv->size > 0) {
++ ret = tplg_add_data(tplg, elem, priv,
++ sizeof(*priv) + priv->size);
++ if (ret < 0)
++ return ret;
+ }
+
+ return 0;
+@@ -1877,14 +1876,15 @@ int tplg_add_link_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
+ int tplg_add_dai_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
+ {
+ struct snd_tplg_dai_template *dai_tpl = t->dai;
+- struct snd_soc_tplg_dai *dai, *_dai;
++ struct snd_soc_tplg_dai *dai;
++ struct snd_soc_tplg_private *priv;
+ struct tplg_elem *elem;
+- int i;
++ int ret, i;
+
+ tplg_dbg("DAI %s\n", dai_tpl->dai_name);
+
+ elem = tplg_elem_new_common(tplg, NULL, dai_tpl->dai_name,
+- SND_TPLG_TYPE_DAI);
++ SND_TPLG_TYPE_DAI);
+ if (!elem)
+ return -ENOMEM;
+
+@@ -1900,8 +1900,13 @@ int tplg_add_dai_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
+ dai->capture = dai_tpl->capture;
+
+ for (i = 0; i < 2; i++) {
+- if (dai_tpl->caps[i])
+- tplg_add_stream_caps(&dai->caps[i], dai_tpl->caps[i]);
++ if (!dai_tpl->caps[i] || !dai_tpl->caps[i]->name)
++ continue;
++ ret = tplg_add_stream_caps(tplg, dai_tpl->caps[i]);
++ if (ret < 0)
++ return ret;
++ snd_strlcpy(dai->caps[i].name, dai_tpl->caps[i]->name,
++ sizeof(dai->caps[i].name));
+ }
+
+ /* flags */
+@@ -1909,22 +1914,299 @@ int tplg_add_dai_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
+ dai->flags = dai_tpl->flags;
+
+ /* private data */
+- if (dai_tpl->priv != NULL) {
+- _dai = realloc(dai,
+- elem->size + dai_tpl->priv->size);
+- if (!_dai) {
+- tplg_elem_free(elem);
+- return -ENOMEM;
++ priv = dai_tpl->priv;
++ if (priv && priv->size > 0) {
++ ret = tplg_add_data(tplg, elem, priv,
++ sizeof(*priv) + priv->size);
++ if (ret < 0)
++ return ret;
++ }
++
++ return 0;
++}
++
++/* decode pcm from the binary input */
++int tplg_decode_pcm(snd_tplg_t *tplg,
++ size_t pos,
++ struct snd_soc_tplg_hdr *hdr,
++ void *bin, size_t size)
++{
++ struct snd_soc_tplg_pcm *pcm;
++ snd_tplg_obj_template_t t;
++ struct snd_tplg_pcm_template *pt;
++ struct snd_tplg_stream_caps_template caps[2], *cap;
++ struct snd_tplg_stream_template *stream;
++ unsigned int i;
++ size_t asize;
++ int err;
++
++ err = tplg_decode_template(tplg, pos, hdr, &t);
++ if (err < 0)
++ return err;
++
++ asize = sizeof(*pt) + SND_SOC_TPLG_STREAM_CONFIG_MAX * sizeof(*stream);
++ pt = alloca(asize);
++
++next:
++ memset(pt, 0, asize);
++ pcm = bin;
++
++ if (size < sizeof(*pcm)) {
++ SNDERR("pcm: small size %d", size);
++ return -EINVAL;
++ }
++ if (sizeof(*pcm) != pcm->size) {
++ SNDERR("pcm: unknown element size %d (expected %zd)",
++ pcm->size, sizeof(*pcm));
++ return -EINVAL;
++ }
++ if (pcm->num_streams > SND_SOC_TPLG_STREAM_CONFIG_MAX) {
++ SNDERR("pcm: wrong number of streams %d", pcm->num_streams);
++ return -EINVAL;
++ }
++ if (sizeof(*pcm) + pcm->priv.size > size) {
++ SNDERR("pcm: wrong private data size %d", pcm->priv.size);
++ return -EINVAL;
++ }
++
++ tplg_dv(tplg, pos, "pcm: size %d private size %d streams %d",
++ pcm->size, pcm->priv.size, pcm->num_streams);
++
++ pt->pcm_name = pcm->pcm_name;
++ tplg_dv(tplg, pos, "pcm: pcm_name '%s'", pt->pcm_name);
++ pt->dai_name = pcm->dai_name;
++ tplg_dv(tplg, pos, "pcm: dai_name '%s'", pt->dai_name);
++ pt->pcm_id = pcm->pcm_id;
++ pt->dai_id = pcm->dai_id;
++ tplg_dv(tplg, pos, "pcm: pcm_id %d dai_id %d", pt->pcm_id, pt->dai_id);
++ pt->playback = pcm->playback;
++ pt->capture = pcm->capture;
++ pt->compress = pcm->compress;
++ tplg_dv(tplg, pos, "pcm: playback %d capture %d compress",
++ pt->playback, pt->capture, pt->compress);
++ pt->num_streams = pcm->num_streams;
++ pt->flag_mask = pcm->flag_mask;
++ pt->flags = pcm->flags;
++ for (i = 0; i < pcm->num_streams; i++) {
++ stream = &pt->stream[i];
++ if (pcm->stream[i].size != sizeof(pcm->stream[0])) {
++ SNDERR("pcm: unknown stream structure size %d",
++ pcm->stream[i].size);
++ return -EINVAL;
++ }
++ stream->name = pcm->stream[i].name;
++ tplg_dv(tplg, pos + offsetof(struct snd_soc_tplg_pcm, stream[i]),
++ "stream %d: '%s'", i, stream->name);
++ stream->format = pcm->stream[i].format;
++ stream->rate = pcm->stream[i].rate;
++ stream->period_bytes = pcm->stream[i].period_bytes;
++ stream->buffer_bytes = pcm->stream[i].buffer_bytes;
++ stream->channels = pcm->stream[i].channels;
++ }
++ for (i = 0; i < 2; i++) {
++ if (i == 0 && !pcm->playback)
++ continue;
++ if (i == 1 && !pcm->capture)
++ continue;
++ cap = &caps[i];
++ pt->caps[i] = cap;
++ if (pcm->caps[i].size != sizeof(pcm->caps[0])) {
++ SNDERR("pcm: unknown caps structure size %d",
++ pcm->caps[i].size);
++ return -EINVAL;
+ }
++ cap->name = pcm->caps[i].name;
++ tplg_dv(tplg, pos + offsetof(struct snd_soc_tplg_pcm, caps[i]),
++ "caps %d: '%s'", i, cap->name);
++ cap->formats = pcm->caps[i].formats;
++ cap->rates = pcm->caps[i].rates;
++ cap->rate_min = pcm->caps[i].rate_min;
++ cap->rate_max = pcm->caps[i].rate_max;
++ cap->channels_min = pcm->caps[i].channels_min;
++ cap->channels_max = pcm->caps[i].channels_max;
++ cap->periods_min = pcm->caps[i].periods_min;
++ cap->periods_max = pcm->caps[i].periods_max;
++ cap->period_size_min = pcm->caps[i].period_size_min;
++ cap->period_size_max = pcm->caps[i].period_size_max;
++ cap->buffer_size_min = pcm->caps[i].buffer_size_min;
++ cap->buffer_size_max = pcm->caps[i].buffer_size_max;
++ cap->sig_bits = pcm->caps[i].sig_bits;
++ }
++
++ tplg_dv(tplg, pos + offsetof(struct snd_soc_tplg_pcm, priv),
++ "pcm: private start");
++ pt->priv = &pcm->priv;
++
++ bin += sizeof(*pcm) + pcm->priv.size;
++ size -= sizeof(*pcm) + pcm->priv.size;
++ pos += sizeof(*pcm) + pcm->priv.size;
++
++ t.pcm = pt;
++ err = snd_tplg_add_object(tplg, &t);
++ if (err < 0)
++ return err;
++
++ if (size > 0)
++ goto next;
++
++ return 0;
++}
++
++/* decode dai from the binary input */
++int tplg_decode_dai(snd_tplg_t *tplg,
++ size_t pos,
++ struct snd_soc_tplg_hdr *hdr,
++ void *bin, size_t size)
++{
++ SNDERR("not implemented");
++ return -ENXIO;
++}
++
++/* decode cc from the binary input */
++int tplg_decode_cc(snd_tplg_t *tplg,
++ size_t pos,
++ struct snd_soc_tplg_hdr *hdr,
++ void *bin, size_t size)
++{
++ SNDERR("not implemented");
++ return -ENXIO;
++}
++
++/* decode link from the binary input */
++int tplg_decode_link(snd_tplg_t *tplg,
++ size_t pos,
++ struct snd_soc_tplg_hdr *hdr,
++ void *bin, size_t size)
++{
++ struct snd_soc_tplg_link_config *link;
++ snd_tplg_obj_template_t t;
++ struct snd_tplg_link_template lt;
++ struct snd_tplg_stream_template streams[SND_SOC_TPLG_STREAM_CONFIG_MAX];
++ struct snd_tplg_stream_template *stream;
++ struct snd_tplg_hw_config_template hws[SND_SOC_TPLG_HW_CONFIG_MAX];
++ struct snd_tplg_hw_config_template *hw;
++ unsigned int i, j;
++ int err;
++
++ err = tplg_decode_template(tplg, pos, hdr, &t);
++ if (err < 0)
++ return err;
++
++next:
++ memset(<, 0, sizeof(lt));
++ memset(streams, 0, sizeof(streams));
++ memset(hws, 0, sizeof(hws));
++ link = bin;
+
+- dai = _dai;
+- dai->priv.size = dai_tpl->priv->size;
++ if (size < sizeof(*link)) {
++ SNDERR("link: small size %d", size);
++ return -EINVAL;
++ }
++ if (sizeof(*link) != link->size) {
++ SNDERR("link: unknown element size %d (expected %zd)",
++ link->size, sizeof(*link));
++ return -EINVAL;
++ }
++ if (link->num_streams > SND_SOC_TPLG_STREAM_CONFIG_MAX) {
++ SNDERR("link: wrong number of streams %d", link->num_streams);
++ return -EINVAL;
++ }
++ if (link->num_hw_configs > SND_SOC_TPLG_HW_CONFIG_MAX) {
++ SNDERR("link: wrong number of streams %d", link->num_streams);
++ return -EINVAL;
++ }
++ if (sizeof(*link) + link->priv.size > size) {
++ SNDERR("link: wrong private data size %d", link->priv.size);
++ return -EINVAL;
++ }
+
+- elem->dai = dai;
+- elem->size += dai->priv.size;
+- memcpy(dai->priv.data, dai_tpl->priv->data,
+- dai->priv.size);
++ tplg_dv(tplg, pos, "link: size %d private size %d streams %d "
++ "hw_configs %d",
++ link->size, link->priv.size, link->num_streams,
++ link->num_hw_configs);
++
++ lt.id = link->id;
++ lt.name = link->name;
++ tplg_dv(tplg, pos, "link: name '%s'", lt.name);
++ lt.stream_name = link->stream_name;
++ tplg_dv(tplg, pos, "link: stream_name '%s'", lt.stream_name);
++ lt.num_streams = link->num_streams;
++ lt.num_hw_configs = link->num_hw_configs;
++ lt.default_hw_config_id = link->default_hw_config_id;
++ lt.flag_mask = link->flag_mask;
++ lt.flags = link->flags;
++ for (i = 0; i < link->num_streams; i++) {
++ stream = &streams[i];
++ if (link->stream[i].size != sizeof(link->stream[0])) {
++ SNDERR("link: unknown stream structure size %d",
++ link->stream[i].size);
++ return -EINVAL;
++ }
++ stream->name = link->stream[i].name;
++ tplg_dv(tplg,
++ pos + offsetof(struct snd_soc_tplg_link_config, stream[i]),
++ "stream %d: '%s'", i, stream->name);
++ stream->format = link->stream[i].format;
++ stream->rate = link->stream[i].rate;
++ stream->period_bytes = link->stream[i].period_bytes;
++ stream->buffer_bytes = link->stream[i].buffer_bytes;
++ stream->channels = link->stream[i].channels;
+ }
++ lt.stream = streams;
++ for (i = 0; i < link->num_hw_configs; i++) {
++ hw = &hws[i];
++ if (link->hw_config[i].size != sizeof(link->hw_config[0])) {
++ SNDERR("link: unknown hw_config structure size %d",
++ link->hw_config[i].size);
++ return -EINVAL;
++ }
++ hw->id = link->hw_config[i].id;
++ hw->fmt = link->hw_config[i].fmt;
++ hw->clock_gated = link->hw_config[i].clock_gated;
++ hw->invert_bclk = link->hw_config[i].invert_bclk;
++ hw->invert_fsync = link->hw_config[i].invert_fsync;
++ hw->bclk_master = link->hw_config[i].bclk_master;
++ hw->fsync_master = link->hw_config[i].fsync_master;
++ hw->mclk_direction = link->hw_config[i].mclk_direction;
++ hw->mclk_rate = link->hw_config[i].mclk_rate;
++ hw->bclk_rate = link->hw_config[i].bclk_rate;
++ hw->fsync_rate = link->hw_config[i].fsync_rate;
++ hw->tdm_slots = link->hw_config[i].tdm_slots;
++ hw->tdm_slot_width = link->hw_config[i].tdm_slot_width;
++ hw->tx_slots = link->hw_config[i].tx_slots;
++ hw->rx_slots = link->hw_config[i].rx_slots;
++ hw->tx_channels = link->hw_config[i].tx_channels;
++ if (hw->tx_channels > SND_SOC_TPLG_MAX_CHAN) {
++ SNDERR("link: wrong tx channels %d", hw->tx_channels);
++ return -EINVAL;
++ }
++ for (j = 0; j < hw->tx_channels; j++)
++ hw->tx_chanmap[j] = link->hw_config[i].tx_chanmap[j];
++ hw->rx_channels = link->hw_config[i].rx_channels;
++ if (hw->rx_channels > SND_SOC_TPLG_MAX_CHAN) {
++ SNDERR("link: wrong rx channels %d", hw->tx_channels);
++ return -EINVAL;
++ }
++ for (j = 0; j < hw->rx_channels; j++)
++ hw->rx_chanmap[j] = link->hw_config[i].rx_chanmap[j];
++ }
++ lt.hw_config = hws;
++
++ tplg_dv(tplg, pos + offsetof(struct snd_soc_tplg_pcm, priv),
++ "link: private start");
++ lt.priv = &link->priv;
++
++ bin += sizeof(*link) + link->priv.size;
++ size -= sizeof(*link) + link->priv.size;
++ pos += sizeof(*link) + link->priv.size;
++
++ t.link = <
++ err = snd_tplg_add_object(tplg, &t);
++ if (err < 0)
++ return err;
++
++ if (size > 0)
++ goto next;
+
+ return 0;
+ }
+diff --git a/src/topology/save.c b/src/topology/save.c
+index 0498911f67d5..c6eabc49a7d5 100644
+--- a/src/topology/save.c
++++ b/src/topology/save.c
+@@ -577,6 +577,9 @@ int snd_tplg_save(snd_tplg_t *tplg, char **dst, int flags)
+ if (err < 0)
+ goto _err;
+
++ if (*dst == NULL)
++ return -EINVAL;
++
+ if (flags & SND_TPLG_SAVE_NOCHECK)
+ return 0;
+
+diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h
+index 74b3a55cada4..22fc5fba48b9 100644
+--- a/src/topology/tplg_local.h
++++ b/src/topology/tplg_local.h
+@@ -142,7 +142,8 @@ struct tplg_tuple_set {
+ };
+
+ struct tplg_vendor_tuples {
+- unsigned int num_sets;
++ unsigned int num_sets;
++ unsigned int alloc_sets;
+ struct tplg_tuple_set **set;
+ };
+
+@@ -217,11 +218,19 @@ struct tplg_table {
+ char **dst, const char *prefix);
+ int (*gsave)(snd_tplg_t *tplg, int index,
+ char **dst, const char *prefix);
++ int (*decod)(snd_tplg_t *tplg, size_t pos,
++ struct snd_soc_tplg_hdr *hdr,
++ void *bin, size_t size);
+ };
+
+ extern struct tplg_table tplg_table[];
+ extern unsigned int tplg_table_items;
+
++void *tplg_calloc(struct list_head *heap, size_t size);
++void tplg_free(struct list_head *heap);
++
++int tplg_get_type(int asoc_type);
++
+ int tplg_parse_compound(snd_tplg_t *tplg, snd_config_t *cfg,
+ int (*fcn)(snd_tplg_t *, snd_config_t *, void *),
+ void *private);
+@@ -246,6 +255,7 @@ int tplg_parse_link(snd_tplg_t *tplg, snd_config_t *cfg, void *priv);
+ int tplg_parse_cc(snd_tplg_t *tplg, snd_config_t *cfg, void *priv);
+ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg, void *priv);
+
++unsigned int tplg_get_tuple_size(int type);
+ void tplg_free_tuples(void *obj);
+
+ int tplg_build_data(snd_tplg_t *tplg);
+@@ -272,6 +282,8 @@ struct tplg_elem *tplg_elem_lookup(struct list_head *base,
+ const char* id,
+ unsigned int type,
+ int index);
++struct tplg_elem *tplg_elem_type_lookup(snd_tplg_t *tplg,
++ enum snd_tplg_type type);
+ struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg,
+ snd_config_t *cfg, const char *name, enum snd_tplg_type type);
+
+@@ -291,6 +303,10 @@ int tplg_parse_ext_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ struct tplg_elem *lookup_pcm_dai_stream(struct list_head *base,
+ const char* id);
+
++int tplg_add_data(snd_tplg_t *tplg, struct tplg_elem *parent,
++ const void *bin, size_t size);
++int tplg_add_data_bytes(snd_tplg_t *tplg, struct tplg_elem *parent,
++ const char *suffix, const void *bin, size_t size);
+ int tplg_add_mixer_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t);
+ int tplg_add_enum_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t);
+ int tplg_add_bytes_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t);
+@@ -356,3 +372,56 @@ int tplg_save_stream_caps(snd_tplg_t *tplg, struct tplg_elem *elem,
+ char **dst, const char *pfx);
+ int tplg_save_dai(snd_tplg_t *tplg, struct tplg_elem *elem,
+ char **dst, const char *pfx);
++
++void tplg_dv(snd_tplg_t *tplg, size_t pos, const char *fmt, ...);
++int tplg_decode_template(snd_tplg_t *tplg,
++ size_t pos,
++ struct snd_soc_tplg_hdr *hdr,
++ snd_tplg_obj_template_t *t);
++int tplg_decode_manifest_data(snd_tplg_t *tplg, size_t pos,
++ struct snd_soc_tplg_hdr *hdr,
++ void *bin, size_t size);
++int tplg_decode_control_mixer1(snd_tplg_t *tplg,
++ struct list_head *heap,
++ struct snd_tplg_mixer_template *mt,
++ size_t pos,
++ void *bin, size_t size);
++int tplg_decode_control_mixer(snd_tplg_t *tplg, size_t pos,
++ struct snd_soc_tplg_hdr *hdr,
++ void *bin, size_t size);
++int tplg_decode_control_enum1(snd_tplg_t *tplg,
++ struct list_head *heap,
++ struct snd_tplg_enum_template *et,
++ size_t pos,
++ void *bin, size_t size);
++int tplg_decode_control_enum(snd_tplg_t *tplg, size_t pos,
++ struct snd_soc_tplg_hdr *hdr,
++ void *bin, size_t size);
++int tplg_decode_control_bytes1(snd_tplg_t *tplg,
++ struct snd_tplg_bytes_template *bt,
++ size_t pos,
++ void *bin, size_t size);
++int tplg_decode_control_bytes(snd_tplg_t *tplg, size_t pos,
++ struct snd_soc_tplg_hdr *hdr,
++ void *bin, size_t size);
++int tplg_decode_data(snd_tplg_t *tplg, size_t pos,
++ struct snd_soc_tplg_hdr *hdr,
++ void *bin, size_t size);
++int tplg_decode_dapm_graph(snd_tplg_t *tplg, size_t pos,
++ struct snd_soc_tplg_hdr *hdr,
++ void *bin, size_t size);
++int tplg_decode_dapm_widget(snd_tplg_t *tplg, size_t pos,
++ struct snd_soc_tplg_hdr *hdr,
++ void *bin, size_t size);
++int tplg_decode_link(snd_tplg_t *tplg, size_t pos,
++ struct snd_soc_tplg_hdr *hdr,
++ void *bin, size_t size);
++int tplg_decode_cc(snd_tplg_t *tplg, size_t pos,
++ struct snd_soc_tplg_hdr *hdr,
++ void *bin, size_t size);
++int tplg_decode_pcm(snd_tplg_t *tplg, size_t pos,
++ struct snd_soc_tplg_hdr *hdr,
++ void *bin, size_t size);
++int tplg_decode_dai(snd_tplg_t *tplg, size_t pos,
++ struct snd_soc_tplg_hdr *hdr,
++ void *bin, size_t size);
+--
+2.16.4
+
diff --git a/0054-topology-move-the-elem-list-delete-to-tplg_elem_free.patch b/0054-topology-move-the-elem-list-delete-to-tplg_elem_free.patch
new file mode 100644
index 0000000..7c58e09
--- /dev/null
+++ b/0054-topology-move-the-elem-list-delete-to-tplg_elem_free.patch
@@ -0,0 +1,39 @@
+From cb88813ff71226af5d16f8853d186ff7c572dbe0 Mon Sep 17 00:00:00 2001
+From: Jaroslav Kysela
+Date: Fri, 3 Jan 2020 22:07:11 +0100
+Subject: [PATCH 54/63] topology: move the elem->list delete to
+ tplg_elem_free()
+
+The tplg_elem_free() is called in the error path from many places
+and it is expected that the element object will be unregistered
+from the tplg structure, too.
+
+Signed-off-by: Jaroslav Kysela
+---
+ src/topology/elem.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/topology/elem.c b/src/topology/elem.c
+index ed5b5f13054f..cbd7f4b636c1 100644
+--- a/src/topology/elem.c
++++ b/src/topology/elem.c
+@@ -301,6 +301,8 @@ struct tplg_elem *tplg_elem_new(void)
+
+ void tplg_elem_free(struct tplg_elem *elem)
+ {
++ list_del(&elem->list);
++
+ tplg_ref_free_list(&elem->ref_list);
+
+ /* free struct snd_tplg_ object,
+@@ -323,7 +325,6 @@ void tplg_elem_free_list(struct list_head *base)
+
+ list_for_each_safe(pos, npos, base) {
+ elem = list_entry(pos, struct tplg_elem, list);
+- list_del(&elem->list);
+ tplg_elem_free(elem);
+ }
+ }
+--
+2.16.4
+
diff --git a/0055-topology-unify-the-log-mechanism.patch b/0055-topology-unify-the-log-mechanism.patch
new file mode 100644
index 0000000..155957c
--- /dev/null
+++ b/0055-topology-unify-the-log-mechanism.patch
@@ -0,0 +1,654 @@
+From ae6522e10621839a15f5a439091af7542d84d3e5 Mon Sep 17 00:00:00 2001
+From: Jaroslav Kysela
+Date: Fri, 3 Jan 2020 22:31:27 +0100
+Subject: [PATCH 55/63] topology: unify the log mechanism
+
+Signed-off-by: Jaroslav Kysela
+---
+ src/topology/Makefile.am | 3 ++-
+ src/topology/builder.c | 54 +++++++++++++++++++++--------------------------
+ src/topology/ctl.c | 30 +++++++++++++-------------
+ src/topology/dapm.c | 19 +++++++++--------
+ src/topology/data.c | 26 +++++++++++------------
+ src/topology/decoder.c | 32 ++++++++--------------------
+ src/topology/log.c | 34 +++++++++++++++++++++++++++++
+ src/topology/pcm.c | 48 ++++++++++++++++++++---------------------
+ src/topology/tplg_local.h | 8 ++++++-
+ 9 files changed, 138 insertions(+), 116 deletions(-)
+ create mode 100644 src/topology/log.c
+
+diff --git a/src/topology/Makefile.am b/src/topology/Makefile.am
+index 12d1d4455883..9f48891f5c28 100644
+--- a/src/topology/Makefile.am
++++ b/src/topology/Makefile.am
+@@ -29,7 +29,8 @@ libatopology_la_SOURCES =\
+ ops.c \
+ elem.c \
+ save.c \
+- decoder.c
++ decoder.c \
++ log.c
+
+ noinst_HEADERS = tplg_local.h
+
+diff --git a/src/topology/builder.c b/src/topology/builder.c
+index 74c44405e653..7af5de084d84 100644
+--- a/src/topology/builder.c
++++ b/src/topology/builder.c
+@@ -20,20 +20,6 @@
+ #include "list.h"
+ #include "tplg_local.h"
+
+-/* verbose output detailing each object size and file position */
+-static void verbose(snd_tplg_t *tplg, const char *fmt, ...)
+-{
+- va_list va;
+-
+- if (!tplg->verbose)
+- return;
+-
+- va_start(va, fmt);
+- fprintf(stdout, "0x%6.6zx/%6.6zd - ", tplg->bin_pos, tplg->bin_pos);
+- vfprintf(stdout, fmt, va);
+- va_end(va);
+-}
+-
+ /* write a block, track the position */
+ static ssize_t twrite(snd_tplg_t *tplg, void *data, size_t data_size)
+ {
+@@ -73,10 +59,11 @@ static ssize_t write_block_header(snd_tplg_t *tplg, unsigned int type,
+ return -EINVAL;
+ }
+
+- verbose(tplg, "header index %d type %d count %d size 0x%lx/%ld vendor %d "
+- "version %d\n", index, type, count,
+- (long unsigned int)payload_size, (long int)payload_size,
+- vendor_type, version);
++ tplg_log(tplg, 'B', tplg->bin_pos,
++ "header index %d type %d count %d size 0x%lx/%ld vendor %d "
++ "version %d", index, type, count,
++ (long unsigned int)payload_size, (long int)payload_size,
++ vendor_type, version);
+
+ tplg->next_hdr_pos += hdr.payload_size + sizeof(hdr);
+
+@@ -121,13 +108,15 @@ static int write_elem_block(snd_tplg_t *tplg,
+ continue;
+
+ if (elem->type != SND_TPLG_TYPE_DAPM_GRAPH)
+- verbose(tplg, "%s '%s': write %d bytes\n",
+- obj_name, elem->id, elem->size);
++ tplg_log(tplg, 'B', tplg->bin_pos,
++ "%s '%s': write %d bytes",
++ obj_name, elem->id, elem->size);
+ else
+- verbose(tplg, "%s '%s -> %s -> %s': write %d bytes\n",
+- obj_name, elem->route->source,
+- elem->route->control,
+- elem->route->sink, elem->size);
++ tplg_log(tplg, 'B', tplg->bin_pos,
++ "%s '%s -> %s -> %s': write %d bytes",
++ obj_name, elem->route->source,
++ elem->route->control,
++ elem->route->sink, elem->size);
+
+ wsize = twrite(tplg, elem->obj, elem->size);
+ if (wsize < 0)
+@@ -225,10 +214,13 @@ static ssize_t write_manifest_data(snd_tplg_t *tplg)
+ return ret;
+ }
+
+- verbose(tplg, "manifest: write %d bytes\n", sizeof(tplg->manifest));
++ tplg_log(tplg, 'B', tplg->bin_pos, "manifest: write %d bytes",
++ sizeof(tplg->manifest));
+ ret = twrite(tplg, &tplg->manifest, sizeof(tplg->manifest));
+ if (ret >= 0) {
+- verbose(tplg, "manifest: write %d priv bytes\n", tplg->manifest.priv.size);
++ tplg_log(tplg, 'B', tplg->bin_pos,
++ "manifest: write %d priv bytes",
++ tplg->manifest.priv.size);
+ ret = twrite(tplg, tplg->manifest_pdata, tplg->manifest.priv.size);
+ }
+ return ret;
+@@ -280,9 +272,10 @@ int tplg_write_data(snd_tplg_t *tplg)
+ size = calc_block_size(list);
+ if (size == 0)
+ continue;
+- verbose(tplg, "block size for type %s (%d:%d) is 0x%zx/%zd\n",
+- tptr->name, tptr->type,
+- tptr->tsoc, size, size);
++ tplg_log(tplg, 'B', tplg->bin_pos,
++ "block size for type %s (%d:%d) is 0x%zx/%zd",
++ tptr->name, tptr->type,
++ tptr->tsoc, size, size);
+ ret = write_elem_block(tplg, list, size,
+ tptr->tsoc, tptr->name);
+ if (ret < 0) {
+@@ -292,7 +285,8 @@ int tplg_write_data(snd_tplg_t *tplg)
+ }
+ }
+
+- verbose(tplg, "total size is 0x%zx/%zd\n", tplg->bin_pos, tplg->bin_pos);
++ tplg_log(tplg, 'B', tplg->bin_pos, "total size is 0x%zx/%zd",
++ tplg->bin_pos, tplg->bin_pos);
+
+ if (total_size != tplg->bin_pos) {
+ SNDERR("total size mismatch (%zd != %zd)",
+diff --git a/src/topology/ctl.c b/src/topology/ctl.c
+index 24d437aa4a2e..a5a81148d1eb 100644
+--- a/src/topology/ctl.c
++++ b/src/topology/ctl.c
+@@ -1216,8 +1216,8 @@ int tplg_decode_control_mixer1(snd_tplg_t *tplg,
+ return -EINVAL;
+ }
+
+- tplg_dv(tplg, pos, "mixer: size %d TLV size %d private size %d",
+- mc->size, mc->hdr.tlv.size, mc->priv.size);
++ tplg_log(tplg, 'D', pos, "mixer: size %d TLV size %d private size %d",
++ mc->size, mc->hdr.tlv.size, mc->priv.size);
+ if (size != mc->size + mc->priv.size) {
+ SNDERR("mixer: unexpected element size %d", size);
+ return -EINVAL;
+@@ -1233,7 +1233,7 @@ int tplg_decode_control_mixer1(snd_tplg_t *tplg,
+ mt->min = mc->min;
+ mt->max = mc->max;
+ mt->platform_max = mc->platform_max;
+- tplg_dv(tplg, pos, "mixer: name '%s' access 0x%x",
++ tplg_log(tplg, 'D', pos, "mixer: name '%s' access 0x%x",
+ mt->hdr.name, mt->hdr.access);
+ if (mc->num_channels > 0) {
+ map = tplg_calloc(heap, sizeof(*map));
+@@ -1261,16 +1261,16 @@ int tplg_decode_control_mixer1(snd_tplg_t *tplg,
+ db->min = mc->hdr.tlv.scale.min;
+ db->step = mc->hdr.tlv.scale.step;
+ db->mute = mc->hdr.tlv.scale.mute;
+- tplg_dv(tplg, pos, "mixer: dB scale TLV: min %d step %d mute %d",
+- db->min, db->step, db->mute);
++ tplg_log(tplg, 'D', pos, "mixer: dB scale TLV: min %d step %d mute %d",
++ db->min, db->step, db->mute);
+ } else {
+ SNDERR("mixer: wrong TLV size %d", mc->hdr.tlv.size);
+ return -EINVAL;
+ }
+
+ mt->priv = &mc->priv;
+- tplg_dv(tplg, pos + offsetof(struct snd_soc_tplg_mixer_control, priv),
+- "mixer: private start");
++ tplg_log(tplg, 'D', pos + offsetof(struct snd_soc_tplg_mixer_control, priv),
++ "mixer: private start");
+ return 0;
+ }
+
+@@ -1338,8 +1338,8 @@ int tplg_decode_control_enum1(snd_tplg_t *tplg,
+ return -EINVAL;
+ }
+
+- tplg_dv(tplg, pos, "enum: size %d private size %d",
+- ec->size, ec->priv.size);
++ tplg_log(tplg, 'D', pos, "enum: size %d private size %d",
++ ec->size, ec->priv.size);
+ if (size != ec->size + ec->priv.size) {
+ SNDERR("enum: unexpected element size %d", size);
+ return -EINVAL;
+@@ -1380,8 +1380,8 @@ int tplg_decode_control_enum1(snd_tplg_t *tplg,
+ cmt.num_channels = ec->num_channels;
+ for (i = 0; i < cmt.num_channels; i++) {
+ struct snd_tplg_channel_elem *channel = &cmt.channel[i];
+- tplg_dv(tplg, pos + ((void *)&ec->channel[i] - (void *)ec),
+- "enum: channel size %d", ec->channel[i].size);
++ tplg_log(tplg, 'D', pos + ((void *)&ec->channel[i] - (void *)ec),
++ "enum: channel size %d", ec->channel[i].size);
+ channel->reg = ec->channel[i].reg;
+ channel->shift = ec->channel[i].shift;
+ channel->id = ec->channel[i].id;
+@@ -1452,8 +1452,8 @@ int tplg_decode_control_bytes1(snd_tplg_t *tplg,
+ return -EINVAL;
+ }
+
+- tplg_dv(tplg, pos, "control bytes: size %d private size %d",
+- bc->size, bc->priv.size);
++ tplg_log(tplg, 'D', pos, "control bytes: size %d private size %d",
++ bc->size, bc->priv.size);
+ if (size != bc->size + bc->priv.size) {
+ SNDERR("bytes: unexpected element size %d", size);
+ return -EINVAL;
+@@ -1473,8 +1473,8 @@ int tplg_decode_control_bytes1(snd_tplg_t *tplg,
+ bt->ext_ops.get = bc->ext_ops.get;
+ bt->ext_ops.put = bc->ext_ops.put;
+ bt->ext_ops.info = bc->ext_ops.info;
+- tplg_dv(tplg, pos, "control bytes: name '%s' access 0x%x",
+- bt->hdr.name, bt->hdr.access);
++ tplg_log(tplg, 'D', pos, "control bytes: name '%s' access 0x%x",
++ bt->hdr.name, bt->hdr.access);
+
+ bt->priv = &bc->priv;
+ return 0;
+diff --git a/src/topology/dapm.c b/src/topology/dapm.c
+index 9fab2d92c150..0bf64833c11f 100644
+--- a/src/topology/dapm.c
++++ b/src/topology/dapm.c
+@@ -895,8 +895,8 @@ next:
+ return -EINVAL;
+ }
+
+- tplg_dv(tplg, pos, "dapm widget: size %d private size %d kcontrols %d",
+- w->size, w->priv.size, w->num_kcontrols);
++ tplg_log(tplg, 'D', pos, "dapm widget: size %d private size %d kcontrols %d",
++ w->size, w->priv.size, w->num_kcontrols);
+
+ wt = tplg_calloc(&heap, sizeof(*wt) + sizeof(void *) * w->num_kcontrols);
+ if (wt == NULL)
+@@ -913,7 +913,8 @@ next:
+ wt->event_flags = w->event_flags;
+ wt->event_type = w->event_type;
+
+- tplg_dv(tplg, pos, "dapm widget: name '%s' sname '%s'", wt->name, wt->sname);
++ tplg_log(tplg, 'D', pos, "dapm widget: name '%s' sname '%s'",
++ wt->name, wt->sname);
+
+ if (sizeof(*w) + w->priv.size > size) {
+ SNDERR("dapm widget: wrong private data size %d",
+@@ -921,8 +922,8 @@ next:
+ return -EINVAL;
+ }
+
+- tplg_dv(tplg, pos + offsetof(struct snd_soc_tplg_dapm_widget, priv),
+- "dapm widget: private start");
++ tplg_log(tplg, 'D', pos + offsetof(struct snd_soc_tplg_dapm_widget, priv),
++ "dapm widget: private start");
+
+ wt->priv = &w->priv;
+ bin += sizeof(*w) + w->priv.size;
+@@ -942,7 +943,7 @@ next:
+ wt->num_ctls++;
+ mc = bin;
+ size2 = mc->size + mc->priv.size;
+- tplg_dv(tplg, pos, "kcontrol mixer size %zd", size2);
++ tplg_log(tplg, 'D', pos, "kcontrol mixer size %zd", size2);
+ if (size2 > size) {
+ SNDERR("dapm widget: small mixer size %d",
+ size2);
+@@ -962,7 +963,7 @@ next:
+ wt->num_ctls++;
+ ec = bin;
+ size2 = ec->size + ec->priv.size;
+- tplg_dv(tplg, pos, "kcontrol enum size %zd", size2);
++ tplg_log(tplg, 'D', pos, "kcontrol enum size %zd", size2);
+ if (size2 > size) {
+ SNDERR("dapm widget: small enum size %d",
+ size2);
+@@ -982,7 +983,7 @@ next:
+ wt->num_ctls++;
+ bc = bin;
+ size2 = bc->size + bc->priv.size;
+- tplg_dv(tplg, pos, "kcontrol bytes size %zd", size2);
++ tplg_log(tplg, 'D', pos, "kcontrol bytes size %zd", size2);
+ if (size2 > size) {
+ SNDERR("dapm widget: small bytes size %d",
+ size2);
+@@ -1049,7 +1050,7 @@ int tplg_decode_dapm_graph(snd_tplg_t *tplg,
+ ge->ctl = g->control;
+ ge->sink = g->sink;
+ gt->count++;
+- tplg_dv(tplg, pos, "dapm graph: src='%s' ctl='%s' sink='%s'",
++ tplg_log(tplg, 'D', pos, "dapm graph: src='%s' ctl='%s' sink='%s'",
+ ge->src, ge->ctl, ge->sink);
+ bin += sizeof(*g);
+ size -= sizeof(*g);
+diff --git a/src/topology/data.c b/src/topology/data.c
+index 64563920a4e2..0b513428da80 100644
+--- a/src/topology/data.c
++++ b/src/topology/data.c
+@@ -1604,7 +1604,7 @@ int tplg_decode_manifest_data(snd_tplg_t *tplg,
+ if (!elem)
+ return -ENOMEM;
+
+- tplg_dv(tplg, pos, "manifest: private size %d", size);
++ tplg_log(tplg, 'D', pos, "manifest: private size %d", size);
+ return tplg_add_data(tplg, elem, bin, size);
+ }
+
+@@ -1664,7 +1664,7 @@ static int tplg_verify_tuple_set(snd_tplg_t *tplg, size_t pos,
+
+ va = bin;
+ if (size < sizeof(*va) || size < va->size) {
+- tplg_dv(tplg, pos, "tuple set verify: wrong size %d", size);
++ tplg_log(tplg, 'A', pos, "tuple set verify: wrong size %d", size);
+ return -EINVAL;
+ }
+
+@@ -1677,20 +1677,20 @@ static int tplg_verify_tuple_set(snd_tplg_t *tplg, size_t pos,
+ case SND_SOC_TPLG_TUPLE_TYPE_SHORT:
+ break;
+ default:
+- tplg_dv(tplg, pos, "tuple set verify: unknown array type %d", va->type);
++ tplg_log(tplg, 'A', pos, "tuple set verify: unknown array type %d", va->type);
+ return -EINVAL;
+ }
+
+ j = tplg_get_tuple_size(va->type) * va->num_elems;
+ if (j + sizeof(*va) != va->size) {
+- tplg_dv(tplg, pos, "tuple set verify: wrong vendor array size %d "
+- "(expected %d for %d count %d)",
+- va->size, j + sizeof(*va), va->type, va->num_elems);
++ tplg_log(tplg, 'A', pos, "tuple set verify: wrong vendor array size %d "
++ "(expected %d for %d count %d)",
++ va->size, j + sizeof(*va), va->type, va->num_elems);
+ return -EINVAL;
+ }
+
+ if (va->num_elems > 4096) {
+- tplg_dv(tplg, pos, "tuple set verify: tuples overflow %d", va->num_elems);
++ tplg_log(tplg, 'A', pos, "tuple set verify: tuples overflow %d", va->num_elems);
+ return -EINVAL;
+ }
+
+@@ -1748,8 +1748,8 @@ static int tplg_decode_tuple_set(snd_tplg_t *tplg,
+ set->type = va->type;
+ set->num_tuples = va->num_elems;
+
+- tplg_dv(tplg, pos, "tuple set: type %d (%s) tuples %d size %d", set->type,
+- get_tuple_type_name(set->type), set->num_tuples, va->size);
++ tplg_log(tplg, 'A', pos, "tuple set: type %d (%s) tuples %d size %d", set->type,
++ get_tuple_type_name(set->type), set->num_tuples, va->size);
+ for (j = 0; j < set->num_tuples; j++) {
+ tuple = &set->tuple[j];
+ switch (va->type) {
+@@ -1798,14 +1798,14 @@ static int tplg_verify_tuples(snd_tplg_t *tplg, size_t pos,
+ int err;
+
+ if (size < sizeof(*va)) {
+- tplg_dv(tplg, pos, "tuples: small size %d", size);
++ tplg_log(tplg, 'A', pos, "tuples: small size %d", size);
+ return -EINVAL;
+ }
+
+ next:
+ va = bin;
+ if (size < sizeof(*va)) {
+- tplg_dv(tplg, pos, "tuples: unexpected vendor arry size %d", size);
++ tplg_log(tplg, 'A', pos, "tuples: unexpected vendor arry size %d", size);
+ return -EINVAL;
+ }
+
+@@ -1903,7 +1903,7 @@ next:
+ return err;
+ tuples = NULL;
+ }
+- tplg_dv(tplg, pos, "add bytes: size %d", tp->size);
++ tplg_log(tplg, 'A', pos, "add bytes: size %d", tp->size);
+ snprintf(suffix, sizeof(suffix), "data%u", block++);
+ err = tplg_add_data_bytes(tplg, parent, suffix, tp->array, tp->size);
+ } else {
+@@ -1929,7 +1929,7 @@ next:
+ return -ENOMEM;
+ }
+ }
+- tplg_dv(tplg, pos, "decode tuples: size %d", tp->size);
++ tplg_log(tplg, 'A', pos, "decode tuples: size %d", tp->size);
+ err = tplg_decode_tuples(tplg, pos, parent, tuples, tp->array, tp->size);
+ num_tuples++;
+ }
+diff --git a/src/topology/decoder.c b/src/topology/decoder.c
+index 2d6a8969756a..66ebe5d8a668 100644
+--- a/src/topology/decoder.c
++++ b/src/topology/decoder.c
+@@ -18,21 +18,6 @@
+ #include "list.h"
+ #include "tplg_local.h"
+
+-/* verbose output detailing each object size and file position */
+-void tplg_dv(snd_tplg_t *tplg, size_t pos, const char *fmt, ...)
+-{
+- va_list va;
+-
+- if (!tplg->verbose)
+- return;
+-
+- va_start(va, fmt);
+- fprintf(stdout, "D0x%6.6zx/%6.6zd - ", pos, pos);
+- vfprintf(stdout, fmt, va);
+- va_end(va);
+- putc('\n', stdout);
+-}
+-
+ int tplg_decode_template(snd_tplg_t *tplg,
+ size_t pos,
+ struct snd_soc_tplg_hdr *hdr,
+@@ -41,7 +26,8 @@ int tplg_decode_template(snd_tplg_t *tplg,
+ int type;
+
+ type = tplg_get_type(hdr->type);
+- tplg_dv(tplg, pos, "template: asoc type %d library type %d", hdr->type, type);
++ tplg_log(tplg, 'D', pos, "template: asoc type %d library type %d",
++ hdr->type, type);
+ if (type < 0)
+ return type;
+
+@@ -50,8 +36,8 @@ int tplg_decode_template(snd_tplg_t *tplg,
+ t->index = hdr->index;
+ t->version = hdr->version;
+ t->vendor_type = hdr->vendor_type;
+- tplg_dv(tplg, pos, "template: index %d version %d vendor_type %d",
+- hdr->index, hdr->version, hdr->vendor_type);
++ tplg_log(tplg, 'D', pos, "template: index %d version %d vendor_type %d",
++ hdr->index, hdr->version, hdr->vendor_type);
+ return 0;
+ }
+
+@@ -71,11 +57,11 @@ int snd_tplg_decode(snd_tplg_t *tplg, void *bin, size_t size, int dflags)
+ while (1) {
+ pos = b - bin;
+ if (size == pos) {
+- tplg_dv(tplg, pos, "block: success (total %zd)", size);
++ tplg_log(tplg, 'D', pos, "block: success (total %zd)", size);
+ return 0;
+ }
+ if (size - pos < sizeof(*hdr)) {
+- tplg_dv(tplg, pos, "block: small size");
++ tplg_log(tplg, 'D', pos, "block: small size");
+ SNDERR("incomplete header data to decode");
+ return -EINVAL;
+ }
+@@ -85,8 +71,8 @@ int snd_tplg_decode(snd_tplg_t *tplg, void *bin, size_t size, int dflags)
+ return -EINVAL;
+ }
+
+- tplg_dv(tplg, pos, "block: abi %d size %d payload size %d",
+- hdr->abi, hdr->size, hdr->payload_size);
++ tplg_log(tplg, 'D', pos, "block: abi %d size %d payload size %d",
++ hdr->abi, hdr->size, hdr->payload_size);
+ if (hdr->abi != SND_SOC_TPLG_ABI_VERSION) {
+ SNDERR("unsupported ABI version %d", hdr->abi);
+ return -EINVAL;
+@@ -127,7 +113,7 @@ int snd_tplg_decode(snd_tplg_t *tplg, void *bin, size_t size, int dflags)
+ SNDERR("unknown block type %d", hdr->type);
+ return -EINVAL;
+ }
+- tplg_dv(tplg, pos, "block: type %d - %s", hdr->type, tptr->name);
++ tplg_log(tplg, 'D', pos, "block: type %d - %s", hdr->type, tptr->name);
+ err = tptr->decod(tplg, pos, hdr, b + hdr->size, hdr->payload_size);
+ if (err < 0)
+ return err;
+diff --git a/src/topology/log.c b/src/topology/log.c
+new file mode 100644
+index 000000000000..1ca365287c22
+--- /dev/null
++++ b/src/topology/log.c
+@@ -0,0 +1,34 @@
++/*
++ Copyright (c) 2019 Red Hat Inc.
++ All rights reserved.
++
++ This library is free software; you can redistribute it and/or modify
++ it under the terms of the GNU Lesser General Public License as
++ published by the Free Software Foundation; either version 2.1 of
++ the License, or (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU Lesser General Public License for more details.
++
++ Authors: Jaroslav Kysela
++*/
++
++#include "list.h"
++#include "tplg_local.h"
++
++/* verbose output detailing each object size and file position */
++void tplg_log_(snd_tplg_t *tplg, char type, size_t pos, const char *fmt, ...)
++{
++ va_list va;
++
++ if (!tplg->verbose)
++ return;
++
++ va_start(va, fmt);
++ fprintf(stdout, "%c0x%6.6zx/%6.6zd - ", type, pos, pos);
++ vfprintf(stdout, fmt, va);
++ va_end(va);
++ putc('\n', stdout);
++}
+diff --git a/src/topology/pcm.c b/src/topology/pcm.c
+index 4e04a6bc3d78..918e3e9a957f 100644
+--- a/src/topology/pcm.c
++++ b/src/topology/pcm.c
+@@ -1969,21 +1969,21 @@ next:
+ return -EINVAL;
+ }
+
+- tplg_dv(tplg, pos, "pcm: size %d private size %d streams %d",
+- pcm->size, pcm->priv.size, pcm->num_streams);
++ tplg_log(tplg, 'D', pos, "pcm: size %d private size %d streams %d",
++ pcm->size, pcm->priv.size, pcm->num_streams);
+
+ pt->pcm_name = pcm->pcm_name;
+- tplg_dv(tplg, pos, "pcm: pcm_name '%s'", pt->pcm_name);
++ tplg_log(tplg, 'D', pos, "pcm: pcm_name '%s'", pt->pcm_name);
+ pt->dai_name = pcm->dai_name;
+- tplg_dv(tplg, pos, "pcm: dai_name '%s'", pt->dai_name);
++ tplg_log(tplg, 'D', pos, "pcm: dai_name '%s'", pt->dai_name);
+ pt->pcm_id = pcm->pcm_id;
+ pt->dai_id = pcm->dai_id;
+- tplg_dv(tplg, pos, "pcm: pcm_id %d dai_id %d", pt->pcm_id, pt->dai_id);
++ tplg_log(tplg, 'D', pos, "pcm: pcm_id %d dai_id %d", pt->pcm_id, pt->dai_id);
+ pt->playback = pcm->playback;
+ pt->capture = pcm->capture;
+ pt->compress = pcm->compress;
+- tplg_dv(tplg, pos, "pcm: playback %d capture %d compress",
+- pt->playback, pt->capture, pt->compress);
++ tplg_log(tplg, 'D', pos, "pcm: playback %d capture %d compress",
++ pt->playback, pt->capture, pt->compress);
+ pt->num_streams = pcm->num_streams;
+ pt->flag_mask = pcm->flag_mask;
+ pt->flags = pcm->flags;
+@@ -1995,8 +1995,8 @@ next:
+ return -EINVAL;
+ }
+ stream->name = pcm->stream[i].name;
+- tplg_dv(tplg, pos + offsetof(struct snd_soc_tplg_pcm, stream[i]),
+- "stream %d: '%s'", i, stream->name);
++ tplg_log(tplg, 'D', pos + offsetof(struct snd_soc_tplg_pcm, stream[i]),
++ "stream %d: '%s'", i, stream->name);
+ stream->format = pcm->stream[i].format;
+ stream->rate = pcm->stream[i].rate;
+ stream->period_bytes = pcm->stream[i].period_bytes;
+@@ -2016,8 +2016,8 @@ next:
+ return -EINVAL;
+ }
+ cap->name = pcm->caps[i].name;
+- tplg_dv(tplg, pos + offsetof(struct snd_soc_tplg_pcm, caps[i]),
+- "caps %d: '%s'", i, cap->name);
++ tplg_log(tplg, 'D', pos + offsetof(struct snd_soc_tplg_pcm, caps[i]),
++ "caps %d: '%s'", i, cap->name);
+ cap->formats = pcm->caps[i].formats;
+ cap->rates = pcm->caps[i].rates;
+ cap->rate_min = pcm->caps[i].rate_min;
+@@ -2033,8 +2033,8 @@ next:
+ cap->sig_bits = pcm->caps[i].sig_bits;
+ }
+
+- tplg_dv(tplg, pos + offsetof(struct snd_soc_tplg_pcm, priv),
+- "pcm: private start");
++ tplg_log(tplg, 'D', pos + offsetof(struct snd_soc_tplg_pcm, priv),
++ "pcm: private start");
+ pt->priv = &pcm->priv;
+
+ bin += sizeof(*pcm) + pcm->priv.size;
+@@ -2120,16 +2120,16 @@ next:
+ return -EINVAL;
+ }
+
+- tplg_dv(tplg, pos, "link: size %d private size %d streams %d "
+- "hw_configs %d",
+- link->size, link->priv.size, link->num_streams,
+- link->num_hw_configs);
++ tplg_log(tplg, 'D', pos, "link: size %d private size %d streams %d "
++ "hw_configs %d",
++ link->size, link->priv.size, link->num_streams,
++ link->num_hw_configs);
+
+ lt.id = link->id;
+ lt.name = link->name;
+- tplg_dv(tplg, pos, "link: name '%s'", lt.name);
++ tplg_log(tplg, 'D', pos, "link: name '%s'", lt.name);
+ lt.stream_name = link->stream_name;
+- tplg_dv(tplg, pos, "link: stream_name '%s'", lt.stream_name);
++ tplg_log(tplg, 'D', pos, "link: stream_name '%s'", lt.stream_name);
+ lt.num_streams = link->num_streams;
+ lt.num_hw_configs = link->num_hw_configs;
+ lt.default_hw_config_id = link->default_hw_config_id;
+@@ -2143,9 +2143,9 @@ next:
+ return -EINVAL;
+ }
+ stream->name = link->stream[i].name;
+- tplg_dv(tplg,
+- pos + offsetof(struct snd_soc_tplg_link_config, stream[i]),
+- "stream %d: '%s'", i, stream->name);
++ tplg_log(tplg, 'D',
++ pos + offsetof(struct snd_soc_tplg_link_config, stream[i]),
++ "stream %d: '%s'", i, stream->name);
+ stream->format = link->stream[i].format;
+ stream->rate = link->stream[i].rate;
+ stream->period_bytes = link->stream[i].period_bytes;
+@@ -2192,8 +2192,8 @@ next:
+ }
+ lt.hw_config = hws;
+
+- tplg_dv(tplg, pos + offsetof(struct snd_soc_tplg_pcm, priv),
+- "link: private start");
++ tplg_log(tplg, 'D', pos + offsetof(struct snd_soc_tplg_pcm, priv),
++ "link: private start");
+ lt.priv = &link->priv;
+
+ bin += sizeof(*link) + link->priv.size;
+diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h
+index 22fc5fba48b9..e061db755ed2 100644
+--- a/src/topology/tplg_local.h
++++ b/src/topology/tplg_local.h
+@@ -226,6 +226,13 @@ struct tplg_table {
+ extern struct tplg_table tplg_table[];
+ extern unsigned int tplg_table_items;
+
++#define tplg_log(tplg, type, pos, fmt, args...) do { \
++ if ((tplg)->verbose) \
++ tplg_log_((tplg), (type), (pos), (fmt), ##args); \
++} while (0)
++
++void tplg_log_(snd_tplg_t *tplg, char type, size_t pos, const char *fmt, ...);
++
+ void *tplg_calloc(struct list_head *heap, size_t size);
+ void tplg_free(struct list_head *heap);
+
+@@ -373,7 +380,6 @@ int tplg_save_stream_caps(snd_tplg_t *tplg, struct tplg_elem *elem,
+ int tplg_save_dai(snd_tplg_t *tplg, struct tplg_elem *elem,
+ char **dst, const char *pfx);
+
+-void tplg_dv(snd_tplg_t *tplg, size_t pos, const char *fmt, ...);
+ int tplg_decode_template(snd_tplg_t *tplg,
+ size_t pos,
+ struct snd_soc_tplg_hdr *hdr,
+--
+2.16.4
+
diff --git a/0056-topology-tplg_dbg-cleanups.patch b/0056-topology-tplg_dbg-cleanups.patch
new file mode 100644
index 0000000..ffcad02
--- /dev/null
+++ b/0056-topology-tplg_dbg-cleanups.patch
@@ -0,0 +1,784 @@
+From d768da27e7f9f56328c61ed5d37c17ce075cbc6e Mon Sep 17 00:00:00 2001
+From: Jaroslav Kysela
+Date: Fri, 3 Jan 2020 22:44:15 +0100
+Subject: [PATCH 56/63] topology: tplg_dbg() cleanups
+
+- remove newline at the end
+
+Signed-off-by: Jaroslav Kysela
+---
+ src/topology/channel.c | 6 +++---
+ src/topology/ctl.c | 40 ++++++++++++++++++++--------------------
+ src/topology/dapm.c | 32 ++++++++++++++++----------------
+ src/topology/data.c | 48 +++++++++++++++++++++++++++---------------------
+ src/topology/ops.c | 8 ++++----
+ src/topology/pcm.c | 36 ++++++++++++++++++------------------
+ src/topology/text.c | 6 +++---
+ 7 files changed, 91 insertions(+), 85 deletions(-)
+
+diff --git a/src/topology/channel.c b/src/topology/channel.c
+index 390c3f164e97..47d5ea4c419a 100644
+--- a/src/topology/channel.c
++++ b/src/topology/channel.c
+@@ -100,7 +100,7 @@ int tplg_parse_channel(snd_tplg_t *tplg, snd_config_t *cfg,
+
+ channel += tplg->channel_idx;
+ snd_config_get_id(cfg, &id);
+- tplg_dbg("\tChannel %s at index %d\n", id, tplg->channel_idx);
++ tplg_dbg("\tChannel %s at index %d", id, tplg->channel_idx);
+
+ channel_id = lookup_channel(id);
+ if (channel_id < 0) {
+@@ -110,7 +110,7 @@ int tplg_parse_channel(snd_tplg_t *tplg, snd_config_t *cfg,
+
+ channel->id = channel_id;
+ channel->size = sizeof(*channel);
+- tplg_dbg("\tChan %s = %d\n", id, channel->id);
++ tplg_dbg("\tChan %s = %d", id, channel->id);
+
+ snd_config_for_each(i, next, cfg) {
+
+@@ -129,7 +129,7 @@ int tplg_parse_channel(snd_tplg_t *tplg, snd_config_t *cfg,
+ else if (strcmp(id, "shift") == 0)
+ channel->shift = value;
+
+- tplg_dbg("\t\t%s = %d\n", id, value);
++ tplg_dbg("\t\t%s = %d", id, value);
+ }
+
+ tplg->channel_idx++;
+diff --git a/src/topology/ctl.c b/src/topology/ctl.c
+index a5a81148d1eb..41dc2ddbb826 100644
+--- a/src/topology/ctl.c
++++ b/src/topology/ctl.c
+@@ -54,7 +54,7 @@ static int parse_access_values(snd_config_t *cfg,
+ const char *value = NULL;
+ unsigned int j;
+
+- tplg_dbg(" Access:\n");
++ tplg_dbg(" Access:");
+
+ snd_config_for_each(i, next, cfg) {
+ n = snd_config_iterator_entry(i);
+@@ -67,7 +67,7 @@ static int parse_access_values(snd_config_t *cfg,
+ for (j = 0; j < ARRAY_SIZE(ctl_access); j++) {
+ if (strcmp(value, ctl_access[j].name) == 0) {
+ hdr->access |= ctl_access[j].value;
+- tplg_dbg("\t%s\n", value);
++ tplg_dbg("\t%s", value);
+ break;
+ }
+ }
+@@ -150,7 +150,7 @@ static int copy_tlv(struct tplg_elem *elem, struct tplg_elem *ref)
+ struct snd_soc_tplg_mixer_control *mixer_ctrl = elem->mixer_ctrl;
+ struct snd_soc_tplg_ctl_tlv *tlv = ref->tlv;
+
+- tplg_dbg("TLV '%s' used by '%s\n", ref->id, elem->id);
++ tplg_dbg("TLV '%s' used by '%s", ref->id, elem->id);
+
+ /* TLV has a fixed size */
+ mixer_ctrl->hdr.tlv = *tlv;
+@@ -330,7 +330,7 @@ static int tplg_parse_tlv_dbscale(snd_config_t *cfg, struct tplg_elem *elem)
+ const char *id = NULL;
+ int val;
+
+- tplg_dbg(" scale: %s\n", elem->id);
++ tplg_dbg(" scale: %s", elem->id);
+
+ tplg_tlv->size = sizeof(struct snd_soc_tplg_ctl_tlv);
+ tplg_tlv->type = SNDRV_CTL_TLVT_DB_SCALE;
+@@ -348,7 +348,7 @@ static int tplg_parse_tlv_dbscale(snd_config_t *cfg, struct tplg_elem *elem)
+ if (tplg_get_integer(n, &val, 0))
+ continue;
+
+- tplg_dbg("\t%s = %i\n", id, val);
++ tplg_dbg("\t%s = %i", id, val);
+
+ /* get TLV data */
+ if (strcmp(id, "min") == 0)
+@@ -450,7 +450,7 @@ int tplg_parse_control_bytes(snd_tplg_t *tplg,
+ snd_strlcpy(be->hdr.name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+ be->hdr.type = SND_SOC_TPLG_TYPE_BYTES;
+
+- tplg_dbg(" Control Bytes: %s\n", elem->id);
++ tplg_dbg(" Control Bytes: %s", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+ n = snd_config_iterator_entry(i);
+@@ -468,7 +468,7 @@ int tplg_parse_control_bytes(snd_tplg_t *tplg,
+ return -EINVAL;
+
+ be->base = ival;
+- tplg_dbg("\t%s: %d\n", id, be->base);
++ tplg_dbg("\t%s: %d", id, be->base);
+ continue;
+ }
+
+@@ -477,7 +477,7 @@ int tplg_parse_control_bytes(snd_tplg_t *tplg,
+ return -EINVAL;
+
+ be->num_regs = ival;
+- tplg_dbg("\t%s: %d\n", id, be->num_regs);
++ tplg_dbg("\t%s: %d", id, be->num_regs);
+ continue;
+ }
+
+@@ -486,7 +486,7 @@ int tplg_parse_control_bytes(snd_tplg_t *tplg,
+ return -EINVAL;
+
+ be->max = ival;
+- tplg_dbg("\t%s: %d\n", id, be->max);
++ tplg_dbg("\t%s: %d", id, be->max);
+ continue;
+ }
+
+@@ -495,7 +495,7 @@ int tplg_parse_control_bytes(snd_tplg_t *tplg,
+ return -EINVAL;
+
+ be->mask = ival;
+- tplg_dbg("\t%s: %d\n", id, be->mask);
++ tplg_dbg("\t%s: %d", id, be->mask);
+ continue;
+ }
+
+@@ -515,7 +515,7 @@ int tplg_parse_control_bytes(snd_tplg_t *tplg,
+ return err;
+
+ tlv_set = true;
+- tplg_dbg("\t%s: %s\n", id, val);
++ tplg_dbg("\t%s: %s", id, val);
+ continue;
+ }
+
+@@ -625,7 +625,7 @@ int tplg_parse_control_enum(snd_tplg_t *tplg, snd_config_t *cfg,
+ ec->channel[j].reg = -1;
+ }
+
+- tplg_dbg(" Control Enum: %s\n", elem->id);
++ tplg_dbg(" Control Enum: %s", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+
+@@ -644,7 +644,7 @@ int tplg_parse_control_enum(snd_tplg_t *tplg, snd_config_t *cfg,
+ return -EINVAL;
+
+ tplg_ref_add(elem, SND_TPLG_TYPE_TEXT, val);
+- tplg_dbg("\t%s: %s\n", id, val);
++ tplg_dbg("\t%s: %s", id, val);
+ continue;
+ }
+
+@@ -761,7 +761,7 @@ int tplg_parse_control_mixer(snd_tplg_t *tplg,
+ for (j = 0; j < SND_SOC_TPLG_MAX_CHAN; j++)
+ mc->channel[j].reg = -1;
+
+- tplg_dbg(" Control Mixer: %s\n", elem->id);
++ tplg_dbg(" Control Mixer: %s", elem->id);
+
+ /* giterate trough each mixer elment */
+ snd_config_for_each(i, next, cfg) {
+@@ -795,7 +795,7 @@ int tplg_parse_control_mixer(snd_tplg_t *tplg,
+ return -EINVAL;
+
+ mc->max = ival;
+- tplg_dbg("\t%s: %d\n", id, mc->max);
++ tplg_dbg("\t%s: %d", id, mc->max);
+ continue;
+ }
+
+@@ -805,7 +805,7 @@ int tplg_parse_control_mixer(snd_tplg_t *tplg,
+ return -EINVAL;
+ mc->invert = ival;
+
+- tplg_dbg("\t%s: %d\n", id, mc->invert);
++ tplg_dbg("\t%s: %d", id, mc->invert);
+ continue;
+ }
+
+@@ -826,7 +826,7 @@ int tplg_parse_control_mixer(snd_tplg_t *tplg,
+ return err;
+
+ tlv_set = true;
+- tplg_dbg("\t%s: %s\n", id, val);
++ tplg_dbg("\t%s: %s", id, val);
+ continue;
+ }
+
+@@ -981,7 +981,7 @@ int tplg_add_mixer(snd_tplg_t *tplg, struct snd_tplg_mixer_template *mixer,
+ struct tplg_elem *elem;
+ int ret, i, num_channels;
+
+- tplg_dbg(" Control Mixer: %s\n", mixer->hdr.name);
++ tplg_dbg(" Control Mixer: %s", mixer->hdr.name);
+
+ if (mixer->hdr.type != SND_SOC_TPLG_TYPE_MIXER) {
+ SNDERR("invalid mixer type %d", mixer->hdr.type);
+@@ -1045,7 +1045,7 @@ int tplg_add_enum(snd_tplg_t *tplg, struct snd_tplg_enum_template *enum_ctl,
+ struct tplg_elem *elem;
+ int ret, i, num_items, num_channels;
+
+- tplg_dbg(" Control Enum: %s\n", enum_ctl->hdr.name);
++ tplg_dbg(" Control Enum: %s", enum_ctl->hdr.name);
+
+ if (enum_ctl->hdr.type != SND_SOC_TPLG_TYPE_ENUM) {
+ SNDERR("invalid enum type %d", enum_ctl->hdr.type);
+@@ -1128,7 +1128,7 @@ int tplg_add_bytes(snd_tplg_t *tplg, struct snd_tplg_bytes_template *bytes_ctl,
+ struct tplg_elem *elem;
+ int ret;
+
+- tplg_dbg(" Control Bytes: %s\n", bytes_ctl->hdr.name);
++ tplg_dbg(" Control Bytes: %s", bytes_ctl->hdr.name);
+
+ if (bytes_ctl->hdr.type != SND_SOC_TPLG_TYPE_BYTES) {
+ SNDERR("invalid bytes type %d", bytes_ctl->hdr.type);
+diff --git a/src/topology/dapm.c b/src/topology/dapm.c
+index 0bf64833c11f..cb85e66734f6 100644
+--- a/src/topology/dapm.c
++++ b/src/topology/dapm.c
+@@ -76,8 +76,8 @@ static int copy_dapm_control(struct tplg_elem *elem, struct tplg_elem *ref)
+ {
+ struct snd_soc_tplg_dapm_widget *widget = elem->widget;
+
+- tplg_dbg("Control '%s' used by '%s'\n", ref->id, elem->id);
+- tplg_dbg("\tparent size: %d + %d -> %d, priv size -> %d\n",
++ tplg_dbg("Control '%s' used by '%s'", ref->id, elem->id);
++ tplg_dbg("\tparent size: %d + %d -> %d, priv size -> %d",
+ elem->size, ref->size, elem->size + ref->size,
+ widget->priv.size);
+
+@@ -210,8 +210,8 @@ int tplg_build_routes(snd_tplg_t *tplg)
+ }
+
+ route = elem->route;
+- tplg_dbg("\nCheck route: sink '%s', control '%s', source '%s'\n",
+- route->sink, route->control, route->source);
++ tplg_dbg("Check route: sink '%s', control '%s', source '%s'",
++ route->sink, route->control, route->source);
+
+ /* validate sink */
+ if (strlen(route->sink) <= 0) {
+@@ -357,7 +357,7 @@ static int tplg_parse_routes(snd_tplg_t *tplg, snd_config_t *cfg, int index)
+ if (err < 0)
+ return err;
+
+- tplg_dbg("route: sink '%s', control '%s', source '%s'\n",
++ tplg_dbg("route: sink '%s', control '%s', source '%s'",
+ line->sink, line->control, line->source);
+ }
+
+@@ -520,7 +520,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg,
+ if (!elem)
+ return -ENOMEM;
+
+- tplg_dbg(" Widget: %s\n", elem->id);
++ tplg_dbg(" Widget: %s", elem->id);
+
+ widget = elem->widget;
+ snd_strlcpy(widget->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+@@ -550,7 +550,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg,
+ }
+
+ widget->id = widget_type;
+- tplg_dbg("\t%s: %s\n", id, val);
++ tplg_dbg("\t%s: %s", id, val);
+ continue;
+ }
+
+@@ -560,7 +560,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg,
+
+ snd_strlcpy(widget->sname, val,
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+- tplg_dbg("\t%s: %s\n", id, val);
++ tplg_dbg("\t%s: %s", id, val);
+ continue;
+ }
+
+@@ -571,7 +571,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg,
+
+ widget->reg = ival ? -1 : 0;
+
+- tplg_dbg("\t%s: %s\n", id, val);
++ tplg_dbg("\t%s: %s", id, val);
+ continue;
+ }
+
+@@ -580,7 +580,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg,
+ return -EINVAL;
+
+ widget->shift = ival;
+- tplg_dbg("\t%s: %d\n", id, widget->shift);
++ tplg_dbg("\t%s: %d", id, widget->shift);
+ continue;
+ }
+
+@@ -589,7 +589,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg,
+ return -EINVAL;
+
+ widget->reg = ival;
+- tplg_dbg("\t%s: %d\n", id, widget->reg);
++ tplg_dbg("\t%s: %d", id, widget->reg);
+ continue;
+ }
+
+@@ -598,7 +598,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg,
+ return -EINVAL;
+
+ widget->invert = ival;
+- tplg_dbg("\t%s: %d\n", id, widget->invert);
++ tplg_dbg("\t%s: %d", id, widget->invert);
+ continue;
+ }
+
+@@ -607,7 +607,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg,
+ return -EINVAL;
+
+ widget->subseq = ival;
+- tplg_dbg("\t%s: %d\n", id, widget->subseq);
++ tplg_dbg("\t%s: %d", id, widget->subseq);
+ continue;
+ }
+
+@@ -616,7 +616,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg,
+ return -EINVAL;
+
+ widget->event_type = ival;
+- tplg_dbg("\t%s: %d\n", id, widget->event_type);
++ tplg_dbg("\t%s: %d", id, widget->event_type);
+ continue;
+ }
+
+@@ -625,7 +625,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg,
+ return -EINVAL;
+
+ widget->event_flags = ival;
+- tplg_dbg("\t%s: %d\n", id, widget->event_flags);
++ tplg_dbg("\t%s: %d", id, widget->event_flags);
+ continue;
+ }
+
+@@ -767,7 +767,7 @@ int tplg_add_widget_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
+ struct tplg_elem *elem;
+ int i, ret = 0;
+
+- tplg_dbg("Widget: %s\n", wt->name);
++ tplg_dbg("Widget: %s", wt->name);
+
+ elem = tplg_elem_new_common(tplg, NULL, wt->name,
+ SND_TPLG_TYPE_DAPM_WIDGET);
+diff --git a/src/topology/data.c b/src/topology/data.c
+index 0b513428da80..37c4591987db 100644
+--- a/src/topology/data.c
++++ b/src/topology/data.c
+@@ -87,7 +87,7 @@ int tplg_parse_refs(snd_config_t *cfg, struct tplg_elem *elem,
+ if (snd_config_get_string(cfg, &val) < 0)
+ return -EINVAL;
+
+- tplg_dbg("\tref data: %s\n", val);
++ tplg_dbg("\tref data: %s", val);
+ err = tplg_ref_add(elem, type, val);
+ if (err < 0)
+ return err;
+@@ -108,7 +108,7 @@ int tplg_parse_refs(snd_config_t *cfg, struct tplg_elem *elem,
+ if (snd_config_get_string(n, &val) < 0)
+ continue;
+
+- tplg_dbg("\tref data: %s\n", val);
++ tplg_dbg("\tref data: %s", val);
+ err = tplg_ref_add(elem, type, val);
+ if (err < 0)
+ return err;
+@@ -169,7 +169,7 @@ static int tplg_parse_data_file(snd_config_t *cfg, struct tplg_elem *elem)
+ size_t size, bytes_read;
+ int ret = 0;
+
+- tplg_dbg("data DataFile: %s\n", elem->id);
++ tplg_dbg("data DataFile: %s", elem->id);
+
+ if (snd_config_get_string(cfg, &value) < 0)
+ return -EINVAL;
+@@ -235,19 +235,25 @@ static void dump_priv_data(struct tplg_elem *elem ATTRIBUTE_UNUSED)
+ #ifdef TPLG_DEBUG
+ struct snd_soc_tplg_private *priv = elem->data;
+ unsigned char *p = (unsigned char *)priv->data;
++ char buf[128], buf2[8];
+ unsigned int i;
+
+- tplg_dbg(" elem size = %d, priv data size = %d\n",
++ tplg_dbg(" elem size = %d, priv data size = %d",
+ elem->size, priv->size);
+
++ buf[0] = '\0';
+ for (i = 0; i < priv->size; i++) {
+- if (i > 0 && (i % 16) == 0)
+- tplg_dbg("\n");
++ if (i > 0 && (i % 16) == 0) {
++ tplg_dbg("%s", buf);
++ buf[0] = '\0';
++ }
+
+- tplg_dbg(" %02x:", *p++);
++ snprintf(buf2, sizeof(buf2), " %02x", *p++);
++ strcat(buf, buf2);
+ }
+
+- tplg_dbg("\n\n");
++ if (buf[0])
++ tplg_dbg("%s", buf);
+ #endif
+ }
+
+@@ -466,7 +472,7 @@ static int tplg_parse_data_hex(snd_config_t *cfg, struct tplg_elem *elem,
+ int size, esize, off, num;
+ int ret;
+
+- tplg_dbg(" data: %s\n", elem->id);
++ tplg_dbg(" data: %s", elem->id);
+
+ if (snd_config_get_string(cfg, &value) < 0)
+ return -EINVAL;
+@@ -683,7 +689,7 @@ static int build_tuples(snd_tplg_t *tplg, struct tplg_elem *elem)
+ if (ref->type != SND_TPLG_TYPE_TUPLE)
+ continue;
+
+- tplg_dbg("tuples '%s' used by data '%s'\n", ref->id, elem->id);
++ tplg_dbg("tuples '%s' used by data '%s'", ref->id, elem->id);
+
+ if (!ref->elem)
+ ref->elem = tplg_elem_lookup(&tplg->tuple_list,
+@@ -799,7 +805,7 @@ static int parse_tuple_set(snd_config_t *cfg,
+ if (!num_tuples)
+ return 0;
+
+- tplg_dbg("\t %d %s tuples:\n", num_tuples, id);
++ tplg_dbg("\t %d %s tuples:", num_tuples, id);
+ set = calloc(1, sizeof(*set) + num_tuples * sizeof(struct tplg_tuple));
+ if (!set)
+ return -ENOMEM;
+@@ -831,7 +837,7 @@ static int parse_tuple_set(snd_config_t *cfg,
+ continue;
+ snd_strlcpy(tuple->string, value,
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+- tplg_dbg("\t\t%s = %s\n", tuple->token, tuple->string);
++ tplg_dbg("\t\t%s = %s", tuple->token, tuple->string);
+ break;
+
+ case SND_SOC_TPLG_TUPLE_TYPE_BOOL:
+@@ -839,7 +845,7 @@ static int parse_tuple_set(snd_config_t *cfg,
+ if (ival < 0)
+ continue;
+ tuple->value = ival;
+- tplg_dbg("\t\t%s = %d\n", tuple->token, tuple->value);
++ tplg_dbg("\t\t%s = %d", tuple->token, tuple->value);
+ break;
+
+ case SND_SOC_TPLG_TUPLE_TYPE_BYTE:
+@@ -862,7 +868,7 @@ static int parse_tuple_set(snd_config_t *cfg,
+ }
+
+ tuple->value = tuple_val;
+- tplg_dbg("\t\t%s = 0x%x\n", tuple->token, tuple->value);
++ tplg_dbg("\t\t%s = 0x%x", tuple->token, tuple->value);
+ break;
+
+ default:
+@@ -1047,7 +1053,7 @@ int tplg_parse_tokens(snd_tplg_t *tplg, snd_config_t *cfg,
+ if (!num_tokens)
+ return 0;
+
+- tplg_dbg(" Vendor tokens: %s, %d tokens\n", elem->id, num_tokens);
++ tplg_dbg(" Vendor tokens: %s, %d tokens", elem->id, num_tokens);
+
+ tokens = calloc(1, sizeof(*tokens)
+ + num_tokens * sizeof(struct tplg_token));
+@@ -1067,7 +1073,7 @@ int tplg_parse_tokens(snd_tplg_t *tplg, snd_config_t *cfg,
+ snd_strlcpy(tokens->token[tokens->num_tokens].id, id,
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+ tokens->token[tokens->num_tokens].value = value;
+- tplg_dbg("\t\t %s : %d\n", tokens->token[tokens->num_tokens].id,
++ tplg_dbg("\t\t %s : %d", tokens->token[tokens->num_tokens].id,
+ tokens->token[tokens->num_tokens].value);
+ tokens->num_tokens++;
+ }
+@@ -1116,7 +1122,7 @@ int tplg_parse_tuples(snd_tplg_t *tplg, snd_config_t *cfg,
+ if (!elem)
+ return -ENOMEM;
+
+- tplg_dbg(" Vendor Tuples: %s\n", elem->id);
++ tplg_dbg(" Vendor Tuples: %s", elem->id);
+
+ tuples = calloc(1, sizeof(*tuples));
+ if (!tuples)
+@@ -1133,7 +1139,7 @@ int tplg_parse_tuples(snd_tplg_t *tplg, snd_config_t *cfg,
+ if (snd_config_get_string(n, &value) < 0)
+ return -EINVAL;
+ tplg_ref_add(elem, SND_TPLG_TYPE_TOKEN, value);
+- tplg_dbg("\t refer to vendor tokens: %s\n", value);
++ tplg_dbg("\t refer to vendor tokens: %s", value);
+ }
+
+ if (strcmp(id, "tuples") == 0) {
+@@ -1208,7 +1214,7 @@ int tplg_parse_manifest_data(snd_tplg_t *tplg, snd_config_t *cfg,
+ manifest = elem->manifest;
+ manifest->size = elem->size;
+
+- tplg_dbg(" Manifest: %s\n", elem->id);
++ tplg_dbg(" Manifest: %s", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+ n = snd_config_iterator_entry(i);
+@@ -1402,7 +1408,7 @@ int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg,
+ return -EINVAL;
+
+ elem->vendor_type = ival;
+- tplg_dbg("\t%s: %d\n", id, elem->index);
++ tplg_dbg("\t%s: %d", id, elem->index);
+ continue;
+ }
+ }
+@@ -1506,7 +1512,7 @@ int tplg_copy_data(snd_tplg_t *tplg, struct tplg_elem *elem,
+ return -EINVAL;
+ }
+
+- tplg_dbg("Data '%s' used by '%s'\n", ref->id, elem->id);
++ tplg_dbg("Data '%s' used by '%s'", ref->id, elem->id);
+ /* overlook empty private data */
+ if (!ref_elem->data || !ref_elem->data->size) {
+ ref->elem = ref_elem;
+diff --git a/src/topology/ops.c b/src/topology/ops.c
+index 2885c7814604..110eef58851d 100644
+--- a/src/topology/ops.c
++++ b/src/topology/ops.c
+@@ -69,7 +69,7 @@ int tplg_parse_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED, snd_config_t *cfg,
+ const char *id, *value;
+ int ival;
+
+- tplg_dbg("\tOps\n");
++ tplg_dbg("\tOps");
+ hdr->size = sizeof(*hdr);
+
+ snd_config_for_each(i, next, cfg) {
+@@ -97,7 +97,7 @@ int tplg_parse_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED, snd_config_t *cfg,
+ else if (strcmp(id, "get") == 0)
+ hdr->ops.get = ival;
+
+- tplg_dbg("\t\t%s = %d\n", id, ival);
++ tplg_dbg("\t\t%s = %d", id, ival);
+ }
+
+ return 0;
+@@ -155,7 +155,7 @@ int tplg_parse_ext_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ const char *id, *value;
+ int ival;
+
+- tplg_dbg("\tExt Ops\n");
++ tplg_dbg("\tExt Ops");
+
+ snd_config_for_each(i, next, cfg) {
+
+@@ -182,7 +182,7 @@ int tplg_parse_ext_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ else if (strcmp(id, "get") == 0)
+ be->ext_ops.get = ival;
+
+- tplg_dbg("\t\t%s = %s\n", id, value);
++ tplg_dbg("\t\t%s = %s", id, value);
+ }
+
+ return 0;
+diff --git a/src/topology/pcm.c b/src/topology/pcm.c
+index 918e3e9a957f..da88783f2b09 100644
+--- a/src/topology/pcm.c
++++ b/src/topology/pcm.c
+@@ -68,7 +68,7 @@ static void copy_stream_caps(const char *id ATTRIBUTE_UNUSED,
+ {
+ struct snd_soc_tplg_stream_caps *ref_caps = ref_elem->stream_caps;
+
+- tplg_dbg("Copy pcm caps (%ld bytes) from '%s' to '%s' \n",
++ tplg_dbg("Copy pcm caps (%ld bytes) from '%s' to '%s'",
+ sizeof(*caps), ref_elem->id, id);
+
+ *caps = *ref_caps;
+@@ -388,7 +388,7 @@ static int parse_unsigned(snd_config_t *n, unsigned int *dst)
+ {
+ const char *id;
+ if (snd_config_get_id(n, &id) >= 0)
+- tplg_dbg("\t\t%s: %d\n", id, *dst);
++ tplg_dbg("\t\t%s: %d", id, *dst);
+ }
+ #endif
+ return 0;
+@@ -415,7 +415,7 @@ int tplg_parse_stream_caps(snd_tplg_t *tplg,
+ sc->size = elem->size;
+ snd_strlcpy(sc->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+
+- tplg_dbg(" PCM Capabilities: %s\n", elem->id);
++ tplg_dbg(" PCM Capabilities: %s", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+ n = snd_config_iterator_entry(i);
+@@ -442,7 +442,7 @@ int tplg_parse_stream_caps(snd_tplg_t *tplg,
+ if (err < 0)
+ return err;
+
+- tplg_dbg("\t\t%s: %s\n", id, val);
++ tplg_dbg("\t\t%s: %s", id, val);
+ continue;
+ }
+
+@@ -460,7 +460,7 @@ int tplg_parse_stream_caps(snd_tplg_t *tplg,
+ if (err < 0)
+ return err;
+
+- tplg_dbg("\t\t%s: %s\n", id, val);
++ tplg_dbg("\t\t%s: %s", id, val);
+ continue;
+ }
+
+@@ -625,7 +625,7 @@ static int tplg_parse_streams(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+
+ snd_config_get_id(cfg, &id);
+
+- tplg_dbg("\t%s:\n", id);
++ tplg_dbg("\t%s:", id);
+
+ switch (elem->type) {
+ case SND_TPLG_TYPE_PCM:
+@@ -672,7 +672,7 @@ static int tplg_parse_streams(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ snd_strlcpy(caps[stream].name, value,
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+
+- tplg_dbg("\t\t%s\n\t\t\t%s\n", id, value);
++ tplg_dbg("\t\t%s\n\t\t\t%s", id, value);
+ continue;
+ }
+ }
+@@ -746,7 +746,7 @@ static int tplg_parse_fe_dai(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ const char *id;
+
+ snd_config_get_id(cfg, &id);
+- tplg_dbg("\t\tFE DAI %s:\n", id);
++ tplg_dbg("\t\tFE DAI %s:", id);
+ snd_strlcpy(pcm->dai_name, id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+
+ snd_config_for_each(i, next, cfg) {
+@@ -763,7 +763,7 @@ static int tplg_parse_fe_dai(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ return -EINVAL;
+ }
+
+- tplg_dbg("\t\t\tindex: %d\n", pcm->dai_id);
++ tplg_dbg("\t\t\tindex: %d", pcm->dai_id);
+ }
+ }
+
+@@ -847,7 +847,7 @@ int tplg_parse_pcm(snd_tplg_t *tplg, snd_config_t *cfg,
+ pcm->size = elem->size;
+ snd_strlcpy(pcm->pcm_name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+
+- tplg_dbg(" PCM: %s\n", elem->id);
++ tplg_dbg(" PCM: %s", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+
+@@ -882,7 +882,7 @@ int tplg_parse_pcm(snd_tplg_t *tplg, snd_config_t *cfg,
+
+ pcm->compress = ival;
+
+- tplg_dbg("\t%s: %d\n", id, ival);
++ tplg_dbg("\t%s: %d", id, ival);
+ continue;
+ }
+
+@@ -988,7 +988,7 @@ int tplg_parse_dai(snd_tplg_t *tplg, snd_config_t *cfg,
+ snd_strlcpy(dai->dai_name, elem->id,
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+
+- tplg_dbg(" DAI: %s\n", elem->id);
++ tplg_dbg(" DAI: %s", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+
+@@ -1142,7 +1142,7 @@ int tplg_parse_link(snd_tplg_t *tplg,
+ link->size = elem->size;
+ snd_strlcpy(link->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+
+- tplg_dbg(" Link: %s\n", elem->id);
++ tplg_dbg(" Link: %s", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+
+@@ -1168,7 +1168,7 @@ int tplg_parse_link(snd_tplg_t *tplg,
+
+ snd_strlcpy(link->stream_name, val,
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+- tplg_dbg("\t%s: %s\n", id, val);
++ tplg_dbg("\t%s: %s", id, val);
+ continue;
+ }
+
+@@ -1280,7 +1280,7 @@ int tplg_parse_cc(snd_tplg_t *tplg,
+ link = elem->link;
+ link->size = elem->size;
+
+- tplg_dbg(" CC: %s\n", elem->id);
++ tplg_dbg(" CC: %s", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+
+@@ -1412,7 +1412,7 @@ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg,
+ hw_cfg = elem->hw_cfg;
+ hw_cfg->size = elem->size;
+
+- tplg_dbg(" Link HW config: %s\n", elem->id);
++ tplg_dbg(" Link HW config: %s", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+
+@@ -1719,7 +1719,7 @@ int tplg_add_pcm_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
+ struct tplg_elem *elem;
+ int ret, i;
+
+- tplg_dbg("PCM: %s, DAI %s\n", pcm_tpl->pcm_name, pcm_tpl->dai_name);
++ tplg_dbg("PCM: %s, DAI %s", pcm_tpl->pcm_name, pcm_tpl->dai_name);
+
+ if (pcm_tpl->num_streams > SND_SOC_TPLG_STREAM_CONFIG_MAX)
+ return -EINVAL;
+@@ -1881,7 +1881,7 @@ int tplg_add_dai_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
+ struct tplg_elem *elem;
+ int ret, i;
+
+- tplg_dbg("DAI %s\n", dai_tpl->dai_name);
++ tplg_dbg("DAI %s", dai_tpl->dai_name);
+
+ elem = tplg_elem_new_common(tplg, NULL, dai_tpl->dai_name,
+ SND_TPLG_TYPE_DAI);
+diff --git a/src/topology/text.c b/src/topology/text.c
+index 6dbf22305fd3..507c5450ab14 100644
+--- a/src/topology/text.c
++++ b/src/topology/text.c
+@@ -32,13 +32,13 @@ static int parse_text_values(snd_config_t *cfg, struct tplg_elem *elem)
+ const char *value = NULL;
+ int j = 0;
+
+- tplg_dbg(" Text Values: %s\n", elem->id);
++ tplg_dbg(" Text Values: %s", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+ n = snd_config_iterator_entry(i);
+
+ if (j == SND_SOC_TPLG_NUM_TEXTS) {
+- tplg_dbg("text string number exceeds %d\n", j);
++ tplg_dbg("text string number exceeds %d", j);
+ return -ENOMEM;
+ }
+
+@@ -48,7 +48,7 @@ static int parse_text_values(snd_config_t *cfg, struct tplg_elem *elem)
+
+ snd_strlcpy(&texts->items[j][0], value,
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+- tplg_dbg("\t%s\n", &texts->items[j][0]);
++ tplg_dbg("\t%s", &texts->items[j][0]);
+
+ j++;
+ }
+--
+2.16.4
+
diff --git a/0057-topology-cosmetic-changes-functions.patch b/0057-topology-cosmetic-changes-functions.patch
new file mode 100644
index 0000000..ff1376a
--- /dev/null
+++ b/0057-topology-cosmetic-changes-functions.patch
@@ -0,0 +1,201 @@
+From b20b400e2f598c86abaf697d66396cecd49b3e14 Mon Sep 17 00:00:00 2001
+From: Jaroslav Kysela
+Date: Fri, 3 Jan 2020 22:56:48 +0100
+Subject: [PATCH 57/63] topology: cosmetic changes (functions)
+
+Signed-off-by: Jaroslav Kysela
+---
+ src/topology/builder.c | 3 ++-
+ src/topology/ctl.c | 5 +++--
+ src/topology/data.c | 17 +++++++++--------
+ src/topology/parser.c | 4 ++--
+ src/topology/pcm.c | 16 ++++++++--------
+ 5 files changed, 24 insertions(+), 21 deletions(-)
+
+diff --git a/src/topology/builder.c b/src/topology/builder.c
+index 7af5de084d84..15757668d7d4 100644
+--- a/src/topology/builder.c
++++ b/src/topology/builder.c
+@@ -71,7 +71,8 @@ static ssize_t write_block_header(snd_tplg_t *tplg, unsigned int type,
+ }
+
+ static int write_elem_block(snd_tplg_t *tplg,
+- struct list_head *base, size_t size, int tplg_type, const char *obj_name)
++ struct list_head *base, size_t size,
++ int tplg_type, const char *obj_name)
+ {
+ struct list_head *pos, *sub_pos, *sub_base;
+ struct tplg_elem *elem, *elem_next;
+diff --git a/src/topology/ctl.c b/src/topology/ctl.c
+index 41dc2ddbb826..b78f1c54be05 100644
+--- a/src/topology/ctl.c
++++ b/src/topology/ctl.c
+@@ -600,7 +600,7 @@ int tplg_save_control_bytes(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+
+ /* Parse Control Enums. */
+ int tplg_parse_control_enum(snd_tplg_t *tplg, snd_config_t *cfg,
+- void *private ATTRIBUTE_UNUSED)
++ void *private ATTRIBUTE_UNUSED)
+ {
+ struct snd_soc_tplg_enum_control *ec;
+ struct tplg_elem *elem;
+@@ -736,7 +736,8 @@ int tplg_save_control_enum(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ * Mixer control. Supports multiple channels.
+ */
+ int tplg_parse_control_mixer(snd_tplg_t *tplg,
+- snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
++ snd_config_t *cfg,
++ void *private ATTRIBUTE_UNUSED)
+ {
+ struct snd_soc_tplg_mixer_control *mc;
+ struct tplg_elem *elem;
+diff --git a/src/topology/data.c b/src/topology/data.c
+index 37c4591987db..b63e98cd7282 100644
+--- a/src/topology/data.c
++++ b/src/topology/data.c
+@@ -516,7 +516,7 @@ static int tplg_parse_data_hex(snd_config_t *cfg, struct tplg_elem *elem,
+
+ /* get the token integer value from its id */
+ static int get_token_value(const char *token_id,
+- struct tplg_vendor_tokens *tokens)
++ struct tplg_vendor_tokens *tokens)
+ {
+ unsigned int i;
+
+@@ -589,7 +589,8 @@ unsigned int tplg_get_tuple_size(int type)
+
+ /* Add a tuples object to the private buffer of its parent data element */
+ static int copy_tuples(struct tplg_elem *elem,
+- struct tplg_vendor_tuples *tuples, struct tplg_vendor_tokens *tokens)
++ struct tplg_vendor_tuples *tuples,
++ struct tplg_vendor_tokens *tokens)
+ {
+ struct snd_soc_tplg_private *priv = elem->data, *priv2;
+ struct tplg_tuple_set *tuple_set;
+@@ -781,7 +782,7 @@ static const char *get_tuple_type_name(unsigned int type)
+ }
+
+ static int parse_tuple_set(snd_config_t *cfg,
+- struct tplg_tuple_set **s)
++ struct tplg_tuple_set **s)
+ {
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+@@ -965,7 +966,7 @@ static int tplg_save_tuple_set(struct tplg_vendor_tuples *tuples,
+ }
+
+ static int parse_tuple_sets(snd_config_t *cfg,
+- struct tplg_vendor_tuples *tuples)
++ struct tplg_vendor_tuples *tuples)
+ {
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+@@ -1033,7 +1034,7 @@ int tplg_save_tuple_sets(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ /* Parse vendor tokens
+ */
+ int tplg_parse_tokens(snd_tplg_t *tplg, snd_config_t *cfg,
+- void *private ATTRIBUTE_UNUSED)
++ void *private ATTRIBUTE_UNUSED)
+ {
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+@@ -1109,7 +1110,7 @@ int tplg_save_tokens(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ /* Parse vendor tuples.
+ */
+ int tplg_parse_tuples(snd_tplg_t *tplg, snd_config_t *cfg,
+- void *private ATTRIBUTE_UNUSED)
++ void *private ATTRIBUTE_UNUSED)
+ {
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+@@ -1193,7 +1194,7 @@ void tplg_free_tuples(void *obj)
+ /* Parse manifest's data references
+ */
+ int tplg_parse_manifest_data(snd_tplg_t *tplg, snd_config_t *cfg,
+- void *private ATTRIBUTE_UNUSED)
++ void *private ATTRIBUTE_UNUSED)
+ {
+ struct snd_soc_tplg_manifest *manifest;
+ struct tplg_elem *elem;
+@@ -1341,7 +1342,7 @@ int tplg_build_manifest_data(snd_tplg_t *tplg)
+ * words, tuples.
+ */
+ int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg,
+- void *private ATTRIBUTE_UNUSED)
++ void *private ATTRIBUTE_UNUSED)
+ {
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+diff --git a/src/topology/parser.c b/src/topology/parser.c
+index d7783a93ab6b..436e48416a43 100644
+--- a/src/topology/parser.c
++++ b/src/topology/parser.c
+@@ -110,8 +110,8 @@ int tplg_get_unsigned(snd_config_t *n, unsigned *val, int base)
+ * Parse compound
+ */
+ int tplg_parse_compound(snd_tplg_t *tplg, snd_config_t *cfg,
+- int (*fcn)(snd_tplg_t *, snd_config_t *, void *),
+- void *private)
++ int (*fcn)(snd_tplg_t *, snd_config_t *, void *),
++ void *private)
+ {
+ const char *id;
+ snd_config_iterator_t i, next;
+diff --git a/src/topology/pcm.c b/src/topology/pcm.c
+index da88783f2b09..61159d33357c 100644
+--- a/src/topology/pcm.c
++++ b/src/topology/pcm.c
+@@ -1124,8 +1124,8 @@ static int parse_hw_config_refs(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ }
+
+ /* Parse a physical link element in text conf file */
+-int tplg_parse_link(snd_tplg_t *tplg,
+- snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
++int tplg_parse_link(snd_tplg_t *tplg, snd_config_t *cfg,
++ void *private ATTRIBUTE_UNUSED)
+ {
+ struct snd_soc_tplg_link_config *link;
+ struct tplg_elem *elem;
+@@ -1264,8 +1264,8 @@ int tplg_save_link(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ }
+
+ /* Parse cc */
+-int tplg_parse_cc(snd_tplg_t *tplg,
+- snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
++int tplg_parse_cc(snd_tplg_t *tplg, snd_config_t *cfg,
++ void *private ATTRIBUTE_UNUSED)
+ {
+ struct snd_soc_tplg_link_config *link;
+ struct tplg_elem *elem;
+@@ -1307,8 +1307,8 @@ int tplg_parse_cc(snd_tplg_t *tplg,
+
+ /* save CC */
+ int tplg_save_cc(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+- struct tplg_elem *elem,
+- char **dst, const char *pfx)
++ struct tplg_elem *elem,
++ char **dst, const char *pfx)
+ {
+ struct snd_soc_tplg_link_config *link = elem->link;
+ char pfx2[16];
+@@ -1668,7 +1668,7 @@ int tplg_save_hw_config(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+
+ /* copy stream object */
+ static void tplg_add_stream_object(struct snd_soc_tplg_stream *strm,
+- struct snd_tplg_stream_template *strm_tpl)
++ struct snd_tplg_stream_template *strm_tpl)
+ {
+ snd_strlcpy(strm->name, strm_tpl->name,
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+@@ -1773,7 +1773,7 @@ int tplg_add_pcm_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
+
+ /* Set link HW config from C API template */
+ static int set_link_hw_config(struct snd_soc_tplg_hw_config *cfg,
+- struct snd_tplg_hw_config_template *tpl)
++ struct snd_tplg_hw_config_template *tpl)
+ {
+ unsigned int i;
+
+--
+2.16.4
+
diff --git a/0058-mixer-Fix-memory-leak-for-more-than-16-file-descript.patch b/0058-mixer-Fix-memory-leak-for-more-than-16-file-descript.patch
new file mode 100644
index 0000000..3aed692
--- /dev/null
+++ b/0058-mixer-Fix-memory-leak-for-more-than-16-file-descript.patch
@@ -0,0 +1,29 @@
+From 14ad963e192f101246e9b559d3b3fd41268330fb Mon Sep 17 00:00:00 2001
+From: David Fries
+Date: Tue, 7 Jan 2020 09:18:10 -0600
+Subject: [PATCH 58/63] mixer: Fix memory leak for more than 16 file descriptor
+ case
+
+Signed-off-by: David Fries
+Reviewed-by: Takashi Iwai
+Signed-off-by: Takashi Iwai
+---
+ src/mixer/mixer.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/mixer/mixer.c b/src/mixer/mixer.c
+index 8205647449d2..b1af99456c8d 100644
+--- a/src/mixer/mixer.c
++++ b/src/mixer/mixer.c
+@@ -767,7 +767,7 @@ int snd_mixer_wait(snd_mixer_t *mixer, int timeout)
+ if (count < 0)
+ return count;
+ if ((unsigned int) count > sizeof(spfds) / sizeof(spfds[0])) {
+- pfds = malloc(count * sizeof(*pfds));
++ pfds = alloca(count * sizeof(*pfds));
+ if (!pfds)
+ return -ENOMEM;
+ err = snd_mixer_poll_descriptors(mixer, pfds,
+--
+2.16.4
+
diff --git a/0059-Quote-strings-containing-or-when-saving-an-alsa-conf.patch b/0059-Quote-strings-containing-or-when-saving-an-alsa-conf.patch
new file mode 100644
index 0000000..b5845ef
--- /dev/null
+++ b/0059-Quote-strings-containing-or-when-saving-an-alsa-conf.patch
@@ -0,0 +1,28 @@
+From a78dd1bd1e9b47c2aee9b1e066de515b6854695a Mon Sep 17 00:00:00 2001
+From: Bertware
+Date: Mon, 13 Jan 2020 10:21:33 +0100
+Subject: [PATCH 59/63] Quote strings containing [ or ] when saving an alsa
+ config
+
+Signed-off-by: Bert Marcelis
+Signed-off-by: Jaroslav Kysela
+---
+ src/conf.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/src/conf.c b/src/conf.c
+index c4db9f21a15e..50d0403452a0 100644
+--- a/src/conf.c
++++ b/src/conf.c
+@@ -1531,6 +1531,8 @@ static void string_print(char *str, int id, snd_output_t *out)
+ case '.':
+ case '{':
+ case '}':
++ case '[':
++ case ']':
+ case '\'':
+ case '"':
+ goto quoted;
+--
+2.16.4
+
diff --git a/0060-ucm-fix-the-configuration-directory-longname-for-ucm.patch b/0060-ucm-fix-the-configuration-directory-longname-for-ucm.patch
new file mode 100644
index 0000000..44c91a3
--- /dev/null
+++ b/0060-ucm-fix-the-configuration-directory-longname-for-ucm.patch
@@ -0,0 +1,32 @@
+From b34715004f4a1aabb85c5b9f03ba9f200638ee97 Mon Sep 17 00:00:00 2001
+From: Jaroslav Kysela
+Date: Wed, 15 Jan 2020 09:41:05 +0100
+Subject: [PATCH 60/63] ucm: fix the configuration directory (longname) for
+ ucm2
+
+The new ucm2 expects that the longname directory is shared with
+the driver directory. Fix that for 'Syntax 2'.
+
+Signed-off-by: Jaroslav Kysela
+---
+ src/ucm/parser.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/src/ucm/parser.c b/src/ucm/parser.c
+index d61124a343e1..b3970a13bf73 100644
+--- a/src/ucm/parser.c
++++ b/src/ucm/parser.c
+@@ -1649,7 +1649,9 @@ __longname:
+
+ if (err == 0) {
+ /* got device-specific file that matches the card long name */
+- snd_strlcpy(uc_mgr->conf_file_name, longname, sizeof(uc_mgr->conf_file_name));
++ if (uc_mgr->conf_format < 2)
++ snd_strlcpy(uc_mgr->conf_file_name, longname,
++ sizeof(uc_mgr->conf_file_name));
+ goto __parse;
+ }
+ }
+--
+2.16.4
+
diff --git a/0061-ucm-split-conf_file_name-and-conf_dir_name.patch b/0061-ucm-split-conf_file_name-and-conf_dir_name.patch
new file mode 100644
index 0000000..53cc941
--- /dev/null
+++ b/0061-ucm-split-conf_file_name-and-conf_dir_name.patch
@@ -0,0 +1,209 @@
+From fe6425af751a768a2ba6cf7d430a85553d3b84f6 Mon Sep 17 00:00:00 2001
+From: Jaroslav Kysela
+Date: Wed, 15 Jan 2020 10:31:56 +0100
+Subject: [PATCH 61/63] ucm: split conf_file_name and conf_dir_name
+
+With ucm2, the file name might differ from the directory
+name. Also, allocate those fields.
+
+Signed-off-by: Jaroslav Kysela
+---
+ src/ucm/main.c | 7 ++++---
+ src/ucm/parser.c | 37 ++++++++++++++++++++++++++-----------
+ src/ucm/ucm_local.h | 4 ++--
+ src/ucm/ucm_subs.c | 2 +-
+ src/ucm/utils.c | 4 ++++
+ 5 files changed, 37 insertions(+), 17 deletions(-)
+
+diff --git a/src/ucm/main.c b/src/ucm/main.c
+index 61922f10b7c7..23e15bd8dacb 100644
+--- a/src/ucm/main.c
++++ b/src/ucm/main.c
+@@ -1700,12 +1700,13 @@ int snd_use_case_get(snd_use_case_mgr_t *uc_mgr,
+ err = 0;
+ } else if (strcmp(identifier, "_file") == 0) {
+ /* get the conf file name of the opened card */
+- if ((uc_mgr->card_name == NULL)
+- || (uc_mgr->conf_file_name[0] == '\0')) {
++ if ((uc_mgr->card_name == NULL) ||
++ (uc_mgr->conf_file_name == NULL) ||
++ (uc_mgr->conf_file_name[0] == '\0')) {
+ err = -ENOENT;
+ goto __end;
+ }
+- *value = strndup(uc_mgr->conf_file_name, MAX_FILE);
++ *value = strdup(uc_mgr->conf_file_name);
+ if (*value == NULL) {
+ err = -ENOMEM;
+ goto __end;
+diff --git a/src/ucm/parser.c b/src/ucm/parser.c
+index b3970a13bf73..1bfde0be327d 100644
+--- a/src/ucm/parser.c
++++ b/src/ucm/parser.c
+@@ -124,6 +124,16 @@ static void configuration_filename(snd_use_case_mgr_t *uc_mgr,
+ configuration_filename2(fn, fn_len, 2, dir, file, suffix);
+ }
+
++/*
++ * Replace mallocated string
++ */
++static char *replace_string(char **dst, const char *value)
++{
++ free(*dst);
++ *dst = strdup(value);
++ return *dst;
++}
++
+ /*
+ * Parse string
+ */
+@@ -1186,7 +1196,7 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr,
+
+ /* open Verb file for reading */
+ configuration_filename(uc_mgr, filename, sizeof(filename),
+- uc_mgr->conf_file_name, file, "");
++ uc_mgr->conf_dir_name, file, "");
+ err = uc_mgr_config_load(uc_mgr->conf_format, filename, &cfg);
+ if (err < 0) {
+ uc_error("error: failed to open verb file %s : %d",
+@@ -1404,16 +1414,16 @@ static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
+ if (uc_mgr->conf_format >= 2) {
+ err = snd_config_search(cfg, "Syntax", &n);
+ if (err < 0) {
+- uc_error("Syntax field not found in %s", uc_mgr->conf_file_name);
++ uc_error("Syntax field not found in %s", uc_mgr->conf_dir_name);
+ return -EINVAL;
+ }
+ err = snd_config_get_integer(n, &l);
+ if (err < 0) {
+- uc_error("Syntax field is invalid in %s", uc_mgr->conf_file_name);
++ uc_error("Syntax field is invalid in %s", uc_mgr->conf_dir_name);
+ return err;
+ }
+ if (l < 2 || l > SYNTAX_VERSION_MAX) {
+- uc_error("Incompatible syntax %d in %s", l, uc_mgr->conf_file_name);
++ uc_error("Incompatible syntax %d in %s", l, uc_mgr->conf_dir_name);
+ return -EINVAL;
+ }
+ /* delete this field to avoid strcmp() call in the loop */
+@@ -1561,8 +1571,9 @@ static int get_by_card(snd_use_case_mgr_t *mgr, const char *ctl_name, char *long
+ return err;
+
+ _name = snd_ctl_card_info_get_name(info);
++ if (replace_string(&mgr->conf_dir_name, _name) == NULL)
++ return -ENOMEM;
+ _long_name = snd_ctl_card_info_get_longname(info);
+- snd_strlcpy(mgr->conf_file_name, _name, sizeof(mgr->conf_file_name));
+ snd_strlcpy(longname, _long_name, MAX_CARD_LONG_NAME);
+
+ return 0;
+@@ -1585,7 +1596,7 @@ static int load_master_config(snd_use_case_mgr_t *uc_mgr,
+ if (getenv(ALSA_CONFIG_UCM2_VAR) || !getenv(ALSA_CONFIG_UCM_VAR)) {
+ uc_mgr->conf_format = 2;
+ configuration_filename(uc_mgr, filename, sizeof(filename),
+- uc_mgr->conf_file_name, card_name, ".conf");
++ uc_mgr->conf_dir_name, card_name, ".conf");
+ if (access(filename, R_OK) == 0)
+ goto __load;
+ }
+@@ -1608,6 +1619,9 @@ __load:
+ return err;
+ }
+
++ if (replace_string(&uc_mgr->conf_file_name, card_name) == NULL)
++ return -ENOMEM;
++
+ return 0;
+ }
+
+@@ -1632,7 +1646,8 @@ int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr)
+ char longname[MAX_CARD_LONG_NAME];
+ int err;
+
+- snd_strlcpy(uc_mgr->conf_file_name, uc_mgr->card_name, sizeof(uc_mgr->conf_file_name));
++ if (replace_string(&uc_mgr->conf_dir_name, uc_mgr->card_name) == NULL)
++ return -ENOMEM;
+
+ if (strncmp(name, "hw:", 3) == 0) {
+ err = get_by_card(uc_mgr, name, longname);
+@@ -1650,14 +1665,14 @@ __longname:
+ if (err == 0) {
+ /* got device-specific file that matches the card long name */
+ if (uc_mgr->conf_format < 2)
+- snd_strlcpy(uc_mgr->conf_file_name, longname,
+- sizeof(uc_mgr->conf_file_name));
++ snd_strlcpy(uc_mgr->conf_dir_name, longname,
++ sizeof(uc_mgr->conf_dir_name));
+ goto __parse;
+ }
+ }
+
+ /* standard path */
+- err = load_master_config(uc_mgr, uc_mgr->conf_file_name, &cfg, 0);
++ err = load_master_config(uc_mgr, uc_mgr->conf_dir_name, &cfg, 0);
+ if (err < 0)
+ goto __error;
+
+@@ -1673,7 +1688,7 @@ __parse:
+
+ __error:
+ uc_mgr_free_ctl_list(uc_mgr);
+- uc_mgr->conf_file_name[0] = '\0';
++ uc_mgr->conf_dir_name[0] = '\0';
+ return err;
+ }
+
+diff --git a/src/ucm/ucm_local.h b/src/ucm/ucm_local.h
+index ba9615078ef8..63e0f3bf1697 100644
+--- a/src/ucm/ucm_local.h
++++ b/src/ucm/ucm_local.h
+@@ -187,7 +187,6 @@ struct use_case_verb {
+ /* verb transition list */
+ struct list_head transition_list;
+
+- /* hardware devices that can be used with this use case */
+ struct list_head device_list;
+
+ /* component device list */
+@@ -205,7 +204,8 @@ struct use_case_verb {
+ */
+ struct snd_use_case_mgr {
+ char *card_name;
+- char conf_file_name[MAX_CARD_LONG_NAME];
++ char *conf_file_name;
++ char *conf_dir_name;
+ char *comment;
+ int conf_format;
+
+diff --git a/src/ucm/ucm_subs.c b/src/ucm/ucm_subs.c
+index 90e395f0ab6f..d931f603ddd7 100644
+--- a/src/ucm/ucm_subs.c
++++ b/src/ucm/ucm_subs.c
+@@ -31,7 +31,7 @@
+
+ static char *rval_conf_name(snd_use_case_mgr_t *uc_mgr)
+ {
+- if (uc_mgr->conf_file_name[0])
++ if (uc_mgr->conf_file_name && uc_mgr->conf_file_name[0])
+ return strdup(uc_mgr->conf_file_name);
+ return NULL;
+ }
+diff --git a/src/ucm/utils.c b/src/ucm/utils.c
+index cde1d672e7b4..daa568c16a30 100644
+--- a/src/ucm/utils.c
++++ b/src/ucm/utils.c
+@@ -441,7 +441,11 @@ void uc_mgr_free_verb(snd_use_case_mgr_t *uc_mgr)
+ uc_mgr_free_sequence(&uc_mgr->default_list);
+ uc_mgr_free_value(&uc_mgr->value_list);
+ free(uc_mgr->comment);
++ free(uc_mgr->conf_dir_name);
++ free(uc_mgr->conf_file_name);
+ uc_mgr->comment = NULL;
++ uc_mgr->conf_dir_name = NULL;
++ uc_mgr->conf_file_name = NULL;
+ uc_mgr->active_verb = NULL;
+ INIT_LIST_HEAD(&uc_mgr->active_devices);
+ INIT_LIST_HEAD(&uc_mgr->active_modifiers);
+--
+2.16.4
+
diff --git a/0062-ucm-remove-MAX_FILE-definition-and-use-correct-PATH_.patch b/0062-ucm-remove-MAX_FILE-definition-and-use-correct-PATH_.patch
new file mode 100644
index 0000000..08ef9ba
--- /dev/null
+++ b/0062-ucm-remove-MAX_FILE-definition-and-use-correct-PATH_.patch
@@ -0,0 +1,58 @@
+From ebf5213cd61824b10bcaf67c570919e2a9ea0e1f Mon Sep 17 00:00:00 2001
+From: Jaroslav Kysela
+Date: Wed, 15 Jan 2020 10:40:01 +0100
+Subject: [PATCH 62/63] ucm: remove MAX_FILE definition and use correct
+ PATH_MAX
+
+Signed-off-by: Jaroslav Kysela
+---
+ src/ucm/parser.c | 6 +++---
+ src/ucm/ucm_local.h | 1 -
+ 2 files changed, 3 insertions(+), 4 deletions(-)
+
+diff --git a/src/ucm/parser.c b/src/ucm/parser.c
+index 1bfde0be327d..6c13fafdabbf 100644
+--- a/src/ucm/parser.c
++++ b/src/ucm/parser.c
+@@ -1167,7 +1167,7 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr,
+ snd_config_t *n;
+ struct use_case_verb *verb;
+ snd_config_t *cfg;
+- char filename[MAX_FILE];
++ char filename[PATH_MAX];
+ int err;
+
+ /* allocate verb */
+@@ -1582,7 +1582,7 @@ static int get_by_card(snd_use_case_mgr_t *mgr, const char *ctl_name, char *long
+ static int load_master_config(snd_use_case_mgr_t *uc_mgr,
+ const char *card_name, snd_config_t **cfg, int longname)
+ {
+- char filename[MAX_FILE];
++ char filename[PATH_MAX];
+ int err;
+
+ if (strnlen(card_name, MAX_CARD_LONG_NAME) == MAX_CARD_LONG_NAME) {
+@@ -1733,7 +1733,7 @@ static int is_component_directory(const char *dir)
+ */
+ int uc_mgr_scan_master_configs(const char **_list[])
+ {
+- char filename[MAX_FILE], dfl[MAX_FILE];
++ char filename[PATH_MAX], dfl[PATH_MAX];
+ char *env = getenv(ALSA_CONFIG_UCM2_VAR);
+ const char **list, *d_name;
+ snd_config_t *cfg, *c;
+diff --git a/src/ucm/ucm_local.h b/src/ucm/ucm_local.h
+index 63e0f3bf1697..fa9fc16661bb 100644
+--- a/src/ucm/ucm_local.h
++++ b/src/ucm/ucm_local.h
+@@ -42,7 +42,6 @@
+
+ #define SYNTAX_VERSION_MAX 2
+
+-#define MAX_FILE 256
+ #define MAX_CARD_SHORT_NAME 32
+ #define MAX_CARD_LONG_NAME 80
+
+--
+2.16.4
+
diff --git a/0063-topology-remove-MAX_FILE-definition-and-use-correct-.patch b/0063-topology-remove-MAX_FILE-definition-and-use-correct-.patch
new file mode 100644
index 0000000..9475da6
--- /dev/null
+++ b/0063-topology-remove-MAX_FILE-definition-and-use-correct-.patch
@@ -0,0 +1,40 @@
+From fbe1ac4a09933f527aff860aad035586367f21b4 Mon Sep 17 00:00:00 2001
+From: Jaroslav Kysela
+Date: Wed, 15 Jan 2020 10:41:35 +0100
+Subject: [PATCH 63/63] topology: remove MAX_FILE definition and use correct
+ PATH_MAX
+
+Signed-off-by: Jaroslav Kysela
+---
+ src/topology/data.c | 2 +-
+ src/topology/tplg_local.h | 1 -
+ 2 files changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/src/topology/data.c b/src/topology/data.c
+index b63e98cd7282..5742b35773f6 100644
+--- a/src/topology/data.c
++++ b/src/topology/data.c
+@@ -163,7 +163,7 @@ static int tplg_parse_data_file(snd_config_t *cfg, struct tplg_elem *elem)
+ {
+ struct snd_soc_tplg_private *priv = NULL;
+ const char *value = NULL;
+- char filename[MAX_FILE];
++ char filename[PATH_MAX];
+ char *env = getenv(ALSA_CONFIG_TPLG_VAR);
+ FILE *fp;
+ size_t size, bytes_read;
+diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h
+index e061db755ed2..5ace0d1919e1 100644
+--- a/src/topology/tplg_local.h
++++ b/src/topology/tplg_local.h
+@@ -29,7 +29,6 @@
+ #define tplg_dbg(fmt, arg...) do { } while (0)
+ #endif
+
+-#define MAX_FILE 256
+ #define TPLG_MAX_PRIV_SIZE (1024 * 128)
+
+ /** The name of the environment variable containing the tplg directory */
+--
+2.16.4
+
diff --git a/alsa.changes b/alsa.changes
index 7628da3..56bd2df 100644
--- a/alsa.changes
+++ b/alsa.changes
@@ -1,4 +1,44 @@
-------------------------------------------------------------------
+Tue Jan 21 15:49:49 CET 2020 - tiwai@suse.de
+
+- Backport upstream fixes:
+ more topology fixes, a memory leak fix in mixer API, alsactl
+ string handling fix, UCM config fixes:
+ 0032-Update-the-attributes.m4-macro-file-from-xine.patch
+ 0033-topology-avoid-to-use-the-atoi-directly-when-expecte.patch
+ 0034-topology-use-snd_config_get_bool-instead-own-impleme.patch
+ 0035-topology-fix-tplg_get_integer-handle-errno-ERANGE.patch
+ 0036-topology-add-tplg_get_unsigned-function.patch
+ 0037-topology-convert-builder-to-use-the-mallocated-memor.patch
+ 0038-topology-add-binary-output-from-the-builder.patch
+ 0039-topology-parser-recode-tplg_parse_config.patch
+ 0040-topology-add-snd_tplg_load-remove-snd_tplg_build_bin.patch
+ 0041-topology-move-the-topology-element-table-from-builde.patch
+ 0042-topology-add-parser-to-the-tplg_table.patch
+ 0043-topology-add-snd_tplg_save.patch
+ 0044-topology-add-snd_tplg_create-with-flags.patch
+ 0045-topology-add-snd_tplg_version-function.patch
+ 0046-topology-cleanup-the-SNDERR-calls.patch
+ 0047-topology-dapm-fix-the-SNDERR-Undefined.patch
+ 0048-topology-fix-the-unitialized-tuples.patch
+ 0049-topology-implement-shorter-hexa-uuid-00-00-parser.patch
+ 0050-topology-fix-the-TPLG_DEBUG-compilation.patch
+ 0051-topology-fix-the-ops-parser-accept-integer-hexa-valu.patch
+ 0052-topology-fix-the-wrong-memory-access-object-realloc.patch
+ 0053-topology-implement-snd_tplg_decode.patch
+ 0054-topology-move-the-elem-list-delete-to-tplg_elem_free.patch
+ 0055-topology-unify-the-log-mechanism.patch
+ 0056-topology-tplg_dbg-cleanups.patch
+ 0057-topology-cosmetic-changes-functions.patch
+ 0058-mixer-Fix-memory-leak-for-more-than-16-file-descript.patch
+ 0059-Quote-strings-containing-or-when-saving-an-alsa-conf.patch
+ 0060-ucm-fix-the-configuration-directory-longname-for-ucm.patch
+ 0061-ucm-split-conf_file_name-and-conf_dir_name.patch
+ 0062-ucm-remove-MAX_FILE-definition-and-use-correct-PATH_.patch
+ 0063-topology-remove-MAX_FILE-definition-and-use-correct-.patch
+- Remove INSTALL document, add NOTES instead
+
+-------------------------------------------------------------------
Fri Dec 20 16:54:25 CET 2019 - tiwai@suse.de
- Upstream fixes, including the alsa-tools build breakage:
diff --git a/alsa.spec b/alsa.spec
index d26a8d8..51ed618 100644
--- a/alsa.spec
+++ b/alsa.spec
@@ -1,7 +1,7 @@
#
# spec file for package alsa
#
-# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2020 SUSE LINUX GmbH, Nuernberg, Germany.
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -79,6 +79,38 @@ Patch28: 0028-topology-more-coding-fixes.patch
Patch29: 0029-Fix-alsa-sound-.h-for-external-programs.patch
Patch30: 0030-type_compat-Add-missing-__s64-and-__u64-definitions-.patch
Patch31: 0031-uapi-Move-typedefs-from-uapi-to-sound.patch
+Patch32: 0032-Update-the-attributes.m4-macro-file-from-xine.patch
+Patch33: 0033-topology-avoid-to-use-the-atoi-directly-when-expecte.patch
+Patch34: 0034-topology-use-snd_config_get_bool-instead-own-impleme.patch
+Patch35: 0035-topology-fix-tplg_get_integer-handle-errno-ERANGE.patch
+Patch36: 0036-topology-add-tplg_get_unsigned-function.patch
+Patch37: 0037-topology-convert-builder-to-use-the-mallocated-memor.patch
+Patch38: 0038-topology-add-binary-output-from-the-builder.patch
+Patch39: 0039-topology-parser-recode-tplg_parse_config.patch
+Patch40: 0040-topology-add-snd_tplg_load-remove-snd_tplg_build_bin.patch
+Patch41: 0041-topology-move-the-topology-element-table-from-builde.patch
+Patch42: 0042-topology-add-parser-to-the-tplg_table.patch
+Patch43: 0043-topology-add-snd_tplg_save.patch
+Patch44: 0044-topology-add-snd_tplg_create-with-flags.patch
+Patch45: 0045-topology-add-snd_tplg_version-function.patch
+Patch46: 0046-topology-cleanup-the-SNDERR-calls.patch
+Patch47: 0047-topology-dapm-fix-the-SNDERR-Undefined.patch
+Patch48: 0048-topology-fix-the-unitialized-tuples.patch
+Patch49: 0049-topology-implement-shorter-hexa-uuid-00-00-parser.patch
+Patch50: 0050-topology-fix-the-TPLG_DEBUG-compilation.patch
+Patch51: 0051-topology-fix-the-ops-parser-accept-integer-hexa-valu.patch
+Patch52: 0052-topology-fix-the-wrong-memory-access-object-realloc.patch
+Patch53: 0053-topology-implement-snd_tplg_decode.patch
+Patch54: 0054-topology-move-the-elem-list-delete-to-tplg_elem_free.patch
+Patch55: 0055-topology-unify-the-log-mechanism.patch
+Patch56: 0056-topology-tplg_dbg-cleanups.patch
+Patch57: 0057-topology-cosmetic-changes-functions.patch
+Patch58: 0058-mixer-Fix-memory-leak-for-more-than-16-file-descript.patch
+Patch59: 0059-Quote-strings-containing-or-when-saving-an-alsa-conf.patch
+Patch60: 0060-ucm-fix-the-configuration-directory-longname-for-ucm.patch
+Patch61: 0061-ucm-split-conf_file_name-and-conf_dir_name.patch
+Patch62: 0062-ucm-remove-MAX_FILE-definition-and-use-correct-PATH_.patch
+Patch63: 0063-topology-remove-MAX_FILE-definition-and-use-correct-.patch
# rest suse fixes
Patch101: alsa-lib-ignore-non-accessible-ALSA_CONFIG_PATH.patch
BuildRequires: doxygen
@@ -194,6 +226,38 @@ This package contains the library for ALSA topology support.
%patch29 -p1
%patch30 -p1
%patch31 -p1
+%patch32 -p1
+%patch33 -p1
+%patch34 -p1
+%patch35 -p1
+%patch36 -p1
+%patch37 -p1
+%patch38 -p1
+%patch39 -p1
+%patch40 -p1
+%patch41 -p1
+%patch42 -p1
+%patch43 -p1
+%patch44 -p1
+%patch45 -p1
+%patch46 -p1
+%patch47 -p1
+%patch48 -p1
+%patch49 -p1
+%patch50 -p1
+%patch51 -p1
+%patch52 -p1
+%patch53 -p1
+%patch54 -p1
+%patch55 -p1
+%patch56 -p1
+%patch57 -p1
+%patch58 -p1
+%patch59 -p1
+%patch60 -p1
+%patch61 -p1
+%patch62 -p1
+%patch63 -p1
%patch101 -p1
%build
@@ -279,7 +343,7 @@ cp %{_sourcedir}/README* %{buildroot}%{_docdir}/%{name}
cp COPYING %{buildroot}%{_docdir}/%{name}
%endif
mkdir -p %{buildroot}%{_docdir}/%{name}/alsa-lib
-cp ChangeLog INSTALL TODO MEMORY-LEAK %{buildroot}%{_docdir}/%{name}/alsa-lib
+cp ChangeLog TODO MEMORY-LEAK NOTES %{buildroot}%{_docdir}/%{name}/alsa-lib
cp doc/asoundrc.txt %{buildroot}%{_docdir}/%{name}/alsa-lib
%post