File 0036-topology-Add-topology-core-parser.patch of Package alsa

From 37692bb985bdaa95fbb23e4a12eb61f6a2c63ac0 Mon Sep 17 00:00:00 2001
From: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Date: Wed, 29 Jul 2015 17:45:14 +0100
Subject: [PATCH 36/49] topology: Add topology core parser.

The topology core parses the high level topology file and calls the
individual object parsers when any new object element is detected at
the high level.

Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 include/topology.h        | 497 ++++++++++++++++++++++++++++++++++++++++++++++
 src/topology/elem.c       | 187 +++++++++++++++++
 src/topology/parser.c     | 359 +++++++++++++++++++++++++++++++++
 src/topology/tplg_local.h | 234 ++++++++++++++++++++++
 4 files changed, 1277 insertions(+)
 create mode 100644 include/topology.h
 create mode 100644 src/topology/elem.c
 create mode 100644 src/topology/parser.c
 create mode 100644 src/topology/tplg_local.h

diff --git a/include/topology.h b/include/topology.h
new file mode 100644
index 000000000000..f604ed1450d3
--- /dev/null
+++ b/include/topology.h
@@ -0,0 +1,497 @@
+/*
+ *
+ *  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.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ *  Copyright (C) 2015 Intel Corporation
+ *
+ */
+
+#ifndef __ALSA_TOPOLOGY_H
+#define __ALSA_TOPOLOGY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \defgroup topology Topology Interface
+ * \{
+ */
+
+/*! \page topology ALSA Topology Interface
+ *
+ * The topology interface allows developers to define DSP topologies in a text
+ * file format and to convert the text topology to a binary topology
+ * representation that can be understood by the kernel. The topology core
+ * currently recognises the following object types :-
+ *
+ *  * Controls (mixer, enumerated and byte) including TLV data.
+ *  * PCMs (FE and BE configurations and capabilities)
+ *  * DAPM widgets
+ *  * DAPM graph elements.
+ *  * Private data for each object type.
+ *  * Manifest (containing count of each object type)
+ *
+ * <h3>Topology File Format</h3>
+ *
+ * The topology text format uses the standard ALSA configuration file format to
+ * describe each topology object type. This allows topology objects to include
+ * other topology objects as part of thier definition. i.e. a TLV data object
+ * can be shared amongst many control objects that use the same TLV data.
+ *
+ *
+ * <h4>Controls</h4>
+ * Topology audio controls can belong to three different types :-
+ *   * Mixer control
+ *   * Enumerated control
+ *   * Byte control
+ *
+ * Each control type can contain TLV data, private data, operations and also
+ * belong to widget objects.<br>
+ *
+ * <h5>Control Operations</h5>
+ * Driver Kcontrol callback info(), get() and put() operations are mapped with
+ * the CTL ops section in topology configuration files. The ctl ops section can
+ * assign operations using the standard names (listed below) for the standard
+ * kcontrol types or use ID numbers (>256) to map to bespoke driver controls.<br>
+ *
+ * <pre>
+ *
+ *	ops."ctl" {
+ *		info "volsw"
+ *		get "257"
+ *		put "257"
+ *	}
+ *
+ * </pre>
+ *
+ * This mapping shows info() using the standard "volsw" info callback whilst
+ * the get() and put() are mapped to bespoke driver callbacks. <br>
+ *
+ * The Standard operations names for control get(), put() and info calls
+ * are :-
+ *  * volsw
+ *  * volsw_sx
+ *  * volsw_xr_sx
+ *  * enum
+ *  * bytes
+ *  * enum_value
+ *  * range
+ *  * strobe
+ *
+ * <h5>Control TLV Data</h5>
+ * Controls can also use TLV data to represent dB information. This can be done
+ * by defining a TLV section and using the TLV section within the control.
+ * The TLV data for DBScale types are defined as follows :-
+ *
+ * <pre>
+ *	scale {
+ *		min "-9000"
+ *		step "300"
+ *		mute "1"
+ *	}
+ * </pre>
+ *
+ * Where the meanings and values for min, step and mute are exactly the same
+ * as defined in driver code.
+ *
+ * <h5>Control Channel Mapping</h5>
+ * Controls can also specify which channels they are mapped with. This is useful
+ * for userspace as it allows applications to determine the correct control
+ * channel for Left and Right etc. Channel maps are defined as follows :-
+ *
+ * <pre>
+ *	channel."name" {
+ *		reg "0"
+ *		shift "0"
+ *	}
+ * </pre>
+ *
+ * The channel map reg is the register offset for the control, shift is the
+ * bit shift within the register for the channel and the section name is the
+ * channel name and can be one of the following :-
+ *
+ * <pre>
+ *  * mono		# mono stream
+ *  * fl 		# front left
+ *  * fr		# front right
+ *  * rl		# rear left
+ *  * rr		# rear right
+ *  * fc		# front center
+ *  * lfe		# LFE
+ *  * sl		# side left
+ *  * sr		# side right
+ *  * rc		# rear center
+ *  * flc		# front left center
+ *  * frc		# front right center
+ *  * rlc		# rear left center
+ *  * rrc		# rear right center
+ *  * flw		# front left wide
+ *  * frw		# front right wide
+ *  * flh		# front left high
+ *  * fch		# front center high
+ *  * frh		# front right high
+ *  * tc		# top center
+ *  * tfl		# top front left
+ *  * tfr		# top front right
+ *  * tfc		# top front center
+ *  * trl		# top rear left
+ *  * trr		# top rear right
+ *  * trc		# top rear center
+ *  * tflc		# top front left center
+ *  * tfrc		# top front right center
+ *  * tsl		# top side left
+ *  * tsr		# top side right
+ *  * llfe		# left LFE
+ *  * rlfe		# right LFE
+ *  * bc		# bottom center
+ *  * blc		# bottom left center
+ *  * brc		# bottom right center
+ * </pre>
+ *
+ *  <h5>Control Private Data</h5>
+ * Controls can also have private data. This can be done by defining a private
+ * data section and including the section within the control. The private data
+ * section is defined as follows :-
+ *
+ * <pre>
+ * SectionData."pdata for EQU1" {
+ *	file "/path/to/file"
+ *	bytes "0x12,0x34,0x56,0x78"
+ *	shorts "0x1122,0x3344,0x5566,0x7788"
+ *	words "0xaabbccdd,0x11223344,0x66aa77bb,0xefef1234"
+ * };
+ * </pre>
+ * The file, bytes, shorts and words keywords are all mutulally exclusive as
+ * the private data should only be taken from one source.  The private data can
+ * either be read from a separate file or defined in the topology file using
+ * the bytes, shorts or words keywords.
+ *
+ * <h5>Mixer Controls</h5>
+ * A mixer control is defined as a new section that can include channel mapping,
+ * TLV data, callback operations and private data. The mixer section also
+ * includes a few other config options that are shown here :-
+ *
+ * <pre>
+ * SectionControlMixer."mixer name" {
+ *	comment "optional comments"
+ *
+ *	index "1"			# Index number
+ *
+ *	channel."name" {		# Channel maps
+ *	   ....
+ *	}
+ *
+ *	ops."ctl" {			# Ops callback functions
+ *	   ....
+ *	}
+ *
+ *	max "32"			# Max control value
+ *	invert "0"			# Whether control values are inverted
+ *
+ *	tlv "tld_data"			# optional TLV data
+ *
+ *	data "pdata for mixer1"		# optional private data
+ * }
+ * </pre>
+ *
+ * The section name is used to define the mixer name. The index number can be
+ * used to identify topology objects groups. This allows driver operations on
+ * objects with index number N and can be used to add/remove pipelines of
+ * objects whilst other objects are unaffected.
+ *
+ * <h5>Byte Controls</h5>
+ * A byte control is defined as a new section that can include channel mapping,
+ * TLV data, callback operations and private data. The bytes section also
+ * includes a few other config options that are shown here :-
+ *
+ * <pre>
+ * SectionControlBytes."name" {
+ *	comment "optional comments"
+ *
+ *	index "1"			# Index number
+ *
+ *	channel."name" {		# Channel maps
+ *	   ....
+ *	}
+ *
+ *	ops."ctl" {			# Ops callback functions
+ *	   ....
+ *	}
+ *
+ *	base "0"			# Register base
+ *	num_regs "16"			# Number of registers
+ *	mask "0xff"			# Mask
+ *	max "255"			# Maximum value
+ *
+ *	tlv "tld_data"			# optional TLV data
+ *
+ *	data "pdata for mixer1"		# optional private data
+ * }
+ * </pre>
+ *
+ * <h5>Enumerated Controls</h5>
+ * A enumerated control is defined as a new section (like mixer and byte) that
+ * can include channel mapping, callback operations, private data and
+ * text strings to represent the enumerated control options.<br>
+ *
+ * The text strings for the enumerated controls are defined in a seperate
+ * section as follows :-
+ *
+ * <pre>
+ * SectionText."name" {
+ *
+ *		Values [
+ *			"value1"
+ *			"value2"
+			"value3"
+ *		]
+ * }
+ * </pre>
+ *
+ * All the enumerated text values are listed in the values list.<br>
+ * The enumerated control is similar to the other controls and defined as
+ * follows :-
+ *
+ * <pre>
+ * SectionControlMixer."name" {
+ *	comment "optional comments"
+ *
+ *	index "1"			# Index number
+ *
+ *	texts "EQU1"			# Enumerated text items
+ *
+ *	channel."name" {		# Channel maps
+ *	   ....
+ *	}
+ *
+ *	ops."ctl" {			# Ops callback functions
+ *	   ....
+ *	}
+ *
+ *	data "pdata for mixer1"		# optional private data
+ * }
+ * </pre>
+ *
+ * <h4>DAPM Graph</h4>
+ * DAPM graphs can easily be defined using the topology file. The format is
+ * very similar to the DAPM graph kernel format. :-
+ *
+ * <pre>
+ * SectionGraph."dsp" {
+ *	index "1"			# Index number
+ *
+ *	lines [
+ *		"sink1, control, source1"
+ *		"sink2, , source2"
+ *	]
+ * }
+ * </pre>
+ *
+ * The lines in the graph are defined as a variable size list of sinks,
+ * controls and sources. The control name is optional as some graph lines have
+ * no associated controls. The section name can be used to differentiate the
+ * graph with other graphs, it's not used by the kernel atm.
+ *
+ * <h4>DAPM Widgets</h4>
+ * DAPM wigets are similar to controls in that they can include many other
+ * objects. Widgets can contain private data, mixer controls and enum controls.
+ *
+ * The following widget types are supported and match the driver types :-
+ *
+ *  * input
+ *  * output
+ *  * mux
+ *  * mixer
+ *  * pga
+ *  * out_drv
+ *  * adc
+ *  * dac
+ *  * switch
+ *  * pre
+ *  * post
+ *  * aif_in
+ *  * aif_out
+ *  * dai_in
+ *  * dai_out
+ *  * dai_link
+ *
+ * Widgets are defined as follows :-
+ *
+ * <pre>
+ * SectionWidget."name" {
+ *
+ *	index "1"			# Index number
+ *
+ *	type "aif_in"			# Widget type - detailed above
+ *
+ *	no_pm "true"			# No PM control bit.
+ *	reg "20"			# PM bit register offset
+ *	shift "0"			# PM bit register shift
+ *	invert "1			# PM bit is inverted
+ *	subseq "8"			# subsequence number
+ *
+ *	event_type "1"			# DAPM widget event type
+ *	event_flags "1"			# DAPM widget event flags
+ *
+ *	mixer "name"			# Optional Mixer Control
+ *	enum "name"			# Optional Enum Control
+ *
+ *	data "name"			# optional private data
+ * }
+ * </pre>
+ *
+ * The section name is the widget name. The mixer and enum fields are mutually
+ * exclusive and used to include controls into the widget. The index and data
+ * fields are the same for widgets as they are for controls whilst the other
+ * fields map on very closely to the driver widget fields.
+ *
+ * <h4>PCM Capabilities</h4>
+ * Topology can also define the capabilities of FE and BE PCMs. Capabilities
+ * can be defined with the following section :-
+ *
+ * <pre>
+ * SectionPCMCapabilities."name" {
+ *
+ *	formats "S24_LE,S16_LE"		# Supported formats
+ *	rate_min "48000"		# Max supported sample rate
+ *	rate_max "48000"		# Min suppoprted sample rate
+ *	channels_min "2"		# Min number of channels
+ *	channels_max "2"		# max number of channels
+ * }
+ * </pre>
+ * The supported formats use the same naming convention as the driver macros.
+ * The PCM capabilities name can be reffered to and included by BE, PCM and
+ * Codec <-> codec topology sections.
+ *
+ * <h4>PCM Configurations</h4>
+ * PCM runtime configurations can be defined for playback and capture stream
+ * directions with the following section  :-
+ *
+ * <pre>
+ * SectionPCMConfig."name" {
+ *
+ *	config."playback" {		# playback config
+ *		format "S16_LE"		# playback format
+ *		rate "48000"		# playback sample rate
+ *		channels "2"		# playback channels
+ *		tdm_slot "0xf"		# playback TDM slot
+ *	}
+ *
+ *	config."capture" {		# capture config
+ *		format "S16_LE"		# capture format
+ *		rate "48000"		# capture sample rate
+ *		channels "2"		# capture channels
+ *		tdm_slot "0xf"		# capture TDM slot
+ *	}
+ * }
+ * </pre>
+ *
+ * The supported formats use the same naming convention as the driver macros.
+ * The PCM configuration name can be reffered to and included by BE, PCM and
+ * Codec <-> codec topology sections.
+ *
+ * <h4>PCM Configurations</h4>
+ * PCM, BE and Codec to Codec link sections define the supported capabilities
+ * and configurations for supported playback and capture streams. The
+ * definitions and content for PCMs, BE and Codec links are the same with the
+ * exception of the section type :-
+ *
+ * <pre>
+ * SectionPCM."name" {
+ *	....
+ * }
+ * SectionBE."name" {
+ *	....
+ * }
+ * SectionCC."name" {
+ *	....
+ * }
+ * </pre>
+ *
+ * The section types above should be used for PCMs, Back Ends and Codec to Codec
+ * links respectively.<br>
+ *
+ * The data for each section is defined as follows :-
+ *
+ * <pre>
+ * SectionPCM."name" {
+ *
+ *	index "1"			# Index number
+ *
+ *	id "0"				# used for binding to the PCM
+ *
+ *	pcm."playback" {
+ *		capabilities "capabilities1"	# capbilities for playback
+ *
+ *		configs [		# supported configs for playback
+ *			"config1"
+ *			"config2"
+ *		]
+ *	}
+ *
+ *	pcm."capture" {
+ *		capabilities "capabilities2"	# capabilities for capture
+ *
+ *		configs [		# supported configs for capture
+ *			"config1"
+ *			"config2"
+ *			"config3"
+ *		]
+ *	}
+ * }
+ * </pre>
+ *
+ */
+
+/** Topology context */
+typedef struct snd_tplg snd_tplg_t;
+
+/**
+ * \brief Create a new topology parser instance.
+ * \return New topology parser instance
+ */
+snd_tplg_t *snd_tplg_new(void);
+
+/**
+ * \brief Free a topology parser instance.
+ * \param tplg Topology parser instance
+ */
+void snd_tplg_free(snd_tplg_t *tplg);
+
+/**
+ * \brief Parse and build topology text file into binary file.
+ * \param tplg Topology instance.
+ * \param infile Topology text input file to be parsed
+ * \param outfile Binary topology output file.
+ * \return Zero on sucess, otherwise a negative error code
+ */
+int snd_tplg_build_file(snd_tplg_t *tplg, const char *infile,
+	const char *outfile);
+
+/**
+ * \brief Enable verbose reporting of binary file output
+ * \param tplg Topology Instance
+ * \param verbose Enable verbose output level if non zero
+ */
+void snd_tplg_verbose(snd_tplg_t *tplg, int verbose);
+
+/* \} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ALSA_TOPOLOGY_H */
diff --git a/src/topology/elem.c b/src/topology/elem.c
new file mode 100644
index 000000000000..32ba2c12375b
--- /dev/null
+++ b/src/topology/elem.c
@@ -0,0 +1,187 @@
+/*
+  Copyright(c) 2014-2015 Intel Corporation
+  All rights reserved.
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of version 2 of the GNU General Public License as
+  published by the Free Software Foundation.
+
+  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
+  General Public License for more details.
+
+  Authors: Mengdong Lin <mengdong.lin@intel.com>
+           Yao Jin <yao.jin@intel.com>
+           Liam Girdwood <liam.r.girdwood@linux.intel.com>
+*/
+
+#include "list.h"
+#include "tplg_local.h"
+
+int tplg_ref_add(struct tplg_elem *elem, int type, const char* id)
+{
+	struct tplg_ref *ref;
+
+	ref = calloc(1, sizeof(*ref));
+	if (!ref)
+		return -ENOMEM;
+
+	strncpy(ref->id, id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+	ref->id[SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1] = 0;
+	ref->type = type;
+
+	list_add_tail(&ref->list, &elem->ref_list);
+	return 0;
+}
+
+void tplg_ref_free_list(struct list_head *base)
+{
+	struct list_head *pos, *npos;
+	struct tplg_ref *ref;
+
+	list_for_each_safe(pos, npos, base) {
+		ref = list_entry(pos, struct tplg_ref, list);
+		list_del(&ref->list);
+		free(ref);
+	}
+}
+
+struct tplg_elem *tplg_elem_new(void)
+{
+	struct tplg_elem *elem;
+
+	elem = calloc(1, sizeof(*elem));
+	if (!elem)
+		return NULL;
+
+	INIT_LIST_HEAD(&elem->ref_list);
+	return elem;
+}
+
+void tplg_elem_free(struct tplg_elem *elem)
+{
+	tplg_ref_free_list(&elem->ref_list);
+
+	/* free struct snd_tplg_ object,
+	 * the union pointers share the same address
+	 */
+	if (elem->mixer_ctrl)
+		free(elem->mixer_ctrl);
+
+	free(elem);
+}
+
+void tplg_elem_free_list(struct list_head *base)
+{
+	struct list_head *pos, *npos;
+	struct tplg_elem *elem;
+
+	list_for_each_safe(pos, npos, base) {
+		elem = list_entry(pos, struct tplg_elem, list);
+		list_del(&elem->list);
+		tplg_elem_free(elem);
+	}
+}
+
+struct tplg_elem *tplg_elem_lookup(struct list_head *base, const char* id,
+	unsigned int type)
+{
+	struct list_head *pos;
+	struct tplg_elem *elem;
+
+	list_for_each(pos, base) {
+
+		elem = list_entry(pos, struct tplg_elem, list);
+
+		if (!strcmp(elem->id, id) && elem->type == type)
+			return elem;
+	}
+
+	return NULL;
+}
+
+/* create a new common element and object */
+struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg,
+	snd_config_t *cfg, enum object_type type)
+{
+	struct tplg_elem *elem;
+	const char *id;
+	int obj_size = 0;
+	void *obj;
+
+	elem = tplg_elem_new();
+	if (!elem)
+		return NULL;
+
+	snd_config_get_id(cfg, &id);
+	strncpy(elem->id, id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+	elem->id[SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1] = 0;
+
+	switch (type) {
+	case OBJECT_TYPE_DATA:
+		list_add_tail(&elem->list, &tplg->pdata_list);
+		break;
+	case OBJECT_TYPE_TEXT:
+		list_add_tail(&elem->list, &tplg->text_list);
+		break;
+	case OBJECT_TYPE_TLV:
+		list_add_tail(&elem->list, &tplg->tlv_list);
+		elem->size = sizeof(struct snd_soc_tplg_ctl_tlv);
+		break;
+	case OBJECT_TYPE_BYTES:
+		list_add_tail(&elem->list, &tplg->bytes_ext_list);
+		obj_size = sizeof(struct snd_soc_tplg_bytes_control);
+		break;
+	case OBJECT_TYPE_ENUM:
+		list_add_tail(&elem->list, &tplg->enum_list);
+		obj_size = sizeof(struct snd_soc_tplg_enum_control);
+		break;
+	case SND_SOC_TPLG_TYPE_MIXER:
+		list_add_tail(&elem->list, &tplg->mixer_list);
+		obj_size = sizeof(struct snd_soc_tplg_mixer_control);
+		break;
+	case OBJECT_TYPE_DAPM_WIDGET:
+		list_add_tail(&elem->list, &tplg->widget_list);
+		obj_size = sizeof(struct snd_soc_tplg_dapm_widget);
+		break;
+	case OBJECT_TYPE_STREAM_CONFIG:
+		list_add_tail(&elem->list, &tplg->pcm_config_list);
+		obj_size = sizeof(struct snd_soc_tplg_stream_config);
+		break;
+	case OBJECT_TYPE_STREAM_CAPS:
+		list_add_tail(&elem->list, &tplg->pcm_caps_list);
+		obj_size = sizeof(struct snd_soc_tplg_stream_caps);
+		break;
+	case OBJECT_TYPE_PCM:
+		list_add_tail(&elem->list, &tplg->pcm_list);
+		obj_size = sizeof(struct snd_soc_tplg_pcm_dai);
+		break;
+	case OBJECT_TYPE_BE:
+		list_add_tail(&elem->list, &tplg->be_list);
+		obj_size = sizeof(struct snd_soc_tplg_pcm_dai);
+		break;
+	case OBJECT_TYPE_CC:
+		list_add_tail(&elem->list, &tplg->cc_list);
+		obj_size = sizeof(struct snd_soc_tplg_pcm_dai);
+		break;
+	default:
+		free(elem);
+		return NULL;
+	}
+
+	/* create new object too if required */
+	if (obj_size > 0) {
+		obj = calloc(1, obj_size);
+		if (obj == NULL) {
+			free(elem);
+			return NULL;
+		}
+
+		elem->obj = obj;
+		elem->size = obj_size;
+	}
+
+	elem->type = type;
+	return elem;
+}
diff --git a/src/topology/parser.c b/src/topology/parser.c
new file mode 100644
index 000000000000..ed25bb88d446
--- /dev/null
+++ b/src/topology/parser.c
@@ -0,0 +1,359 @@
+/*
+  Copyright(c) 2014-2015 Intel Corporation
+  All rights reserved.
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of version 2 of the GNU General Public License as
+  published by the Free Software Foundation.
+
+  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
+  General Public License for more details.
+
+  Authors: Mengdong Lin <mengdong.lin@intel.com>
+           Yao Jin <yao.jin@intel.com>
+           Liam Girdwood <liam.r.girdwood@linux.intel.com>
+*/
+
+#include "list.h"
+#include "tplg_local.h"
+
+/*
+ * 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)
+{
+	const char *id;
+	snd_config_iterator_t i, next;
+	snd_config_t *n;
+	int err = -EINVAL;
+
+	if (snd_config_get_id(cfg, &id) < 0)
+		return -EINVAL;
+
+	if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
+		SNDERR("error: compound type expected for %s", id);
+		return -EINVAL;
+	}
+
+	/* parse compound */
+	snd_config_for_each(i, next, 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",
+				id, snd_config_get_type(cfg));
+			return -EINVAL;
+		}
+
+		err = fcn(tplg, n, private);
+		if (err < 0)
+			return err;
+	}
+
+	return err;
+}
+
+static int tplg_parse_config(snd_tplg_t *tplg, snd_config_t *cfg)
+{
+	snd_config_iterator_t i, next;
+	snd_config_t *n;
+	const char *id;
+	int err;
+
+	if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
+		SNDERR("error: compound type expected at top level");
+		return -EINVAL;
+	}
+
+	/* parse topology config sections */
+	snd_config_for_each(i, next, cfg) {
+
+		n = snd_config_iterator_entry(i);
+		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;
+		}
+
+		if (strcmp(id, "SectionControlMixer") == 0) {
+			err = tplg_parse_compound(tplg, n,
+				tplg_parse_control_mixer, NULL);
+			if (err < 0)
+				return err;
+			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, "SectionPCMConfig") == 0) {
+			err = tplg_parse_compound(tplg, n,
+				tplg_parse_pcm_config, NULL);
+			if (err < 0)
+				return err;
+			continue;
+		}
+
+		if (strcmp(id, "SectionPCMCapabilities") == 0) {
+			err = tplg_parse_compound(tplg, n,
+				tplg_parse_pcm_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, "SectionBE") == 0) {
+			err = tplg_parse_compound(tplg, n, tplg_parse_be,
+				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;
+		}
+
+		SNDERR("error: unknown section %s\n", id);
+	}
+	return 0;
+}
+
+static int tplg_load_config(const char *file, snd_config_t **cfg)
+{
+	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) {
+		SNDERR("error: could not attach stdio %s", file);
+		goto err;
+	}
+	ret = snd_config_top(&top);
+	if (ret < 0)
+		goto err;
+
+	ret = snd_config_load(top, in);
+	if (ret < 0) {
+		SNDERR("error: could not load configuration file %s",
+			file);
+		goto err_load;
+	}
+
+	ret = snd_input_close(in);
+	if (ret < 0)
+		goto err_load;
+
+	*cfg = top;
+	return 0;
+
+err_load:
+	snd_config_delete(top);
+err:
+	fclose(fp);
+	return ret;
+}
+
+static int tplg_build_integ(snd_tplg_t *tplg)
+{
+	int err;
+
+	err = tplg_build_controls(tplg);
+	if (err <  0)
+		return err;
+
+	err = tplg_build_widgets(tplg);
+	if (err <  0)
+		return err;
+
+	err = tplg_build_pcm_dai(tplg, OBJECT_TYPE_PCM);
+	if (err <  0)
+		return err;
+
+	err = tplg_build_pcm_dai(tplg, OBJECT_TYPE_BE);
+	if (err <  0)
+		return err;
+
+	err = tplg_build_pcm_dai(tplg, OBJECT_TYPE_CC);
+	if (err <  0)
+		return err;
+
+	err = tplg_build_routes(tplg);
+	if (err <  0)
+		return err;
+
+	return err;
+}
+
+int snd_tplg_build_file(snd_tplg_t *tplg, const char *infile,
+	const char *outfile)
+{
+	snd_config_t *cfg = NULL;
+	int err = 0;
+
+	/* delete any old output files */
+	unlink(outfile);
+
+	tplg->out_fd =
+		open(outfile, O_RDWR | O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO);
+	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;
+	}
+
+	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;
+	}
+
+out:
+	snd_config_delete(cfg);
+out_close:
+	close(tplg->out_fd);
+	return err;
+}
+
+void snd_tplg_verbose(snd_tplg_t *tplg, int verbose)
+{
+	tplg->verbose = verbose;
+}
+
+snd_tplg_t *snd_tplg_new(void)
+{
+	snd_tplg_t *tplg;
+
+	tplg = calloc(1, sizeof(snd_tplg_t));
+	if (!tplg)
+		return NULL;
+
+	INIT_LIST_HEAD(&tplg->tlv_list);
+	INIT_LIST_HEAD(&tplg->widget_list);
+	INIT_LIST_HEAD(&tplg->pcm_list);
+	INIT_LIST_HEAD(&tplg->be_list);
+	INIT_LIST_HEAD(&tplg->cc_list);
+	INIT_LIST_HEAD(&tplg->route_list);
+	INIT_LIST_HEAD(&tplg->pdata_list);
+	INIT_LIST_HEAD(&tplg->text_list);
+	INIT_LIST_HEAD(&tplg->pcm_config_list);
+	INIT_LIST_HEAD(&tplg->pcm_caps_list);
+	INIT_LIST_HEAD(&tplg->mixer_list);
+	INIT_LIST_HEAD(&tplg->enum_list);
+	INIT_LIST_HEAD(&tplg->bytes_ext_list);
+
+	return tplg;
+}
+
+void snd_tplg_free(snd_tplg_t *tplg)
+{
+	tplg_elem_free_list(&tplg->tlv_list);
+	tplg_elem_free_list(&tplg->widget_list);
+	tplg_elem_free_list(&tplg->pcm_list);
+	tplg_elem_free_list(&tplg->be_list);
+	tplg_elem_free_list(&tplg->cc_list);
+	tplg_elem_free_list(&tplg->route_list);
+	tplg_elem_free_list(&tplg->pdata_list);
+	tplg_elem_free_list(&tplg->text_list);
+	tplg_elem_free_list(&tplg->pcm_config_list);
+	tplg_elem_free_list(&tplg->pcm_caps_list);
+	tplg_elem_free_list(&tplg->mixer_list);
+	tplg_elem_free_list(&tplg->enum_list);
+	tplg_elem_free_list(&tplg->bytes_ext_list);
+
+	free(tplg);
+}
diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h
new file mode 100644
index 000000000000..688c78f3a6a4
--- /dev/null
+++ b/src/topology/tplg_local.h
@@ -0,0 +1,234 @@
+/*
+ *  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 of the License, or (at your option) any later version.
+ *
+ *  This library 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.
+ */
+
+#include <limits.h>
+#include <stdint.h>
+#include <linux/types.h>
+
+#include "local.h"
+#include "list.h"
+#include "topology.h"
+
+#include <sound/asound.h>
+#include <sound/asoc.h>
+#include <sound/tlv.h>
+
+#define TPLG_DEBUG
+#ifdef TPLG_DEBUG
+#define tplg_dbg SNDERR
+#else
+#define tplg_dbg(fmt, arg...) do { } while (0)
+#endif
+
+#define MAX_FILE		256
+#define TPLG_MAX_PRIV_SIZE	(1024 * 128)
+#define ALSA_TPLG_DIR	ALSA_CONFIG_DIR "/topology"
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
+
+/** The name of the environment variable containing the tplg directory */
+#define ALSA_CONFIG_TPLG_VAR "ALSA_CONFIG_TPLG"
+
+struct tplg_ref;
+struct tplg_elem;
+
+/* internal topology object type not used by kernel */
+enum object_type {
+	OBJECT_TYPE_TLV = 0,
+	OBJECT_TYPE_MIXER,
+	OBJECT_TYPE_ENUM,
+	OBJECT_TYPE_TEXT,
+	OBJECT_TYPE_DATA,
+	OBJECT_TYPE_BYTES,
+	OBJECT_TYPE_STREAM_CONFIG,
+	OBJECT_TYPE_STREAM_CAPS,
+	OBJECT_TYPE_PCM,
+	OBJECT_TYPE_DAPM_WIDGET,
+	OBJECT_TYPE_DAPM_GRAPH,
+	OBJECT_TYPE_BE,
+	OBJECT_TYPE_CC,
+	OBJECT_TYPE_MANIFEST,
+};
+
+struct snd_tplg {
+
+	/* opaque vendor data */
+	int vendor_fd;
+	char *vendor_name;
+
+	/* out file */
+	int out_fd;
+
+	int verbose;
+	unsigned int version;
+
+	/* runtime state */
+	unsigned int next_hdr_pos;
+	int index;
+	int channel_idx;
+
+	/* manifest */
+	struct snd_soc_tplg_manifest manifest;
+
+	/* list of each element type */
+	struct list_head tlv_list;
+	struct list_head widget_list;
+	struct list_head pcm_list;
+	struct list_head be_list;
+	struct list_head cc_list;
+	struct list_head route_list;
+	struct list_head text_list;
+	struct list_head pdata_list;
+	struct list_head pcm_config_list;
+	struct list_head pcm_caps_list;
+
+	/* type-specific control lists */
+	struct list_head mixer_list;
+	struct list_head enum_list;
+	struct list_head bytes_ext_list;
+};
+
+/* object text references */
+struct tplg_ref {
+	unsigned int type;
+	struct tplg_elem *elem;
+	char id[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+	struct list_head list;
+};
+
+/* topology element */
+struct tplg_elem {
+
+	char id[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+
+	/* storage for texts and data if this is text or data elem*/
+	char texts[SND_SOC_TPLG_NUM_TEXTS][SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+
+	int index;
+	enum object_type type;
+
+	int size; /* total size of this object inc pdata and ref objects */
+	int compound_elem; /* dont write this element as individual elem */
+	int vendor_type; /* vendor type for private data */
+
+	/* UAPI object for this elem */
+	union {
+		void *obj;
+		struct snd_soc_tplg_mixer_control *mixer_ctrl;
+		struct snd_soc_tplg_enum_control *enum_ctrl;
+		struct snd_soc_tplg_bytes_control *bytes_ext;
+		struct snd_soc_tplg_dapm_widget *widget;
+		struct snd_soc_tplg_pcm_dai *pcm;
+		struct snd_soc_tplg_pcm_dai *be;
+		struct snd_soc_tplg_pcm_dai *cc;
+		struct snd_soc_tplg_dapm_graph_elem *route;
+		struct snd_soc_tplg_stream_config *stream_cfg;
+		struct snd_soc_tplg_stream_caps *stream_caps;
+
+		/* these do not map to UAPI structs but are internal only */
+		struct snd_soc_tplg_ctl_tlv *tlv;
+		struct snd_soc_tplg_private *data;
+	};
+
+	/* an element may refer to other elements:
+	 * a mixer control may refer to a tlv,
+	 * a widget may refer to a mixer control array,
+	 * a graph may refer to some widgets.
+	 */
+	struct list_head ref_list;
+	struct list_head list; /* list of all elements with same type */
+};
+
+struct map_elem {
+	const char *name;
+	int id;
+};
+
+int tplg_parse_compound(snd_tplg_t *tplg, snd_config_t *cfg,
+	int (*fcn)(snd_tplg_t *, snd_config_t *, void *),
+	void *private);
+
+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_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_pcm_config(snd_tplg_t *tplg,
+	snd_config_t *cfg, void *private ATTRIBUTE_UNUSED);
+
+int tplg_parse_pcm_caps(snd_tplg_t *tplg,
+	snd_config_t *cfg, void *private ATTRIBUTE_UNUSED);
+
+int tplg_parse_pcm_cap_cfg(snd_tplg_t *tplg, snd_config_t *cfg,
+	void *private);
+
+int tplg_parse_pcm(snd_tplg_t *tplg,
+	snd_config_t *cfg, void *private ATTRIBUTE_UNUSED);
+
+int tplg_parse_be(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_build_controls(snd_tplg_t *tplg);
+int tplg_build_widgets(snd_tplg_t *tplg);
+int tplg_build_routes(snd_tplg_t *tplg);
+int tplg_build_pcm_dai(snd_tplg_t *tplg, unsigned int type);
+
+int tplg_copy_data(struct tplg_elem *elem, struct tplg_elem *ref);
+
+int tplg_ref_add(struct tplg_elem *elem, int type, const char* id);
+
+struct tplg_elem *tplg_elem_new(void);
+void tplg_elem_free(struct tplg_elem *elem);
+void tplg_elem_free_list(struct list_head *base);
+struct tplg_elem *tplg_elem_lookup(struct list_head *base,
+				const char* id,
+				unsigned int type);
+struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg,
+	snd_config_t *cfg, enum object_type type);
+
+static inline void elem_copy_text(char *dest, const char *src, int len)
+{
+	strncpy(dest, src, len);
+	dest[len - 1] = 0;
+}
+
+int tplg_parse_channel(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+	snd_config_t *cfg, void *private);
+
+int tplg_parse_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+	snd_config_t *cfg, void *private);
+
+struct tplg_elem *lookup_pcm_dai_stream(struct list_head *base,
+	const char* id);
-- 
2.5.0

openSUSE Build Service is sponsored by