* [PATCH v3 00/13] alsa-lib topology support
@ 2015-07-29 16:45 Liam Girdwood
2015-07-29 16:45 ` [PATCH v3 01/13] topology: uapi: Add UAPI headers for topology ABI Liam Girdwood
` (13 more replies)
0 siblings, 14 replies; 17+ messages in thread
From: Liam Girdwood @ 2015-07-29 16:45 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, Liam Girdwood, Mark Brown
This series adds support for parsing topology data text files and converting
to the binary topology format accepted by the kernel.
Changes since V2 :-
o Added documentation in doxygen format describing API and text file format.
o use SNDERR macro for error reporting.
o Added text copy helper.
o Make all parsed keywords lowercase
o Assignment for some structure copies instead of memcpy().
o Fixed the private data parser to handle byte, short and word data.
Changes since V1 :-
o Terminate all strncpy() strings with NULL.
o Use snd_pcm_format_value() instead of local lookup.
o Set a maximum size for private data files.
o Set capture/playback stream direction for new PCMs
o Only set TLV access flags for TLV CTLs.
Liam Girdwood (13):
topology: uapi: Add UAPI headers for topology ABI
topology: Add topology core parser.
topology: Add text section parser.
topology: Add PCM parser.
topology: Add operations parser
topology: Add private data parser
topology: Add DAPM object parser
topology: Add CTL parser
topology: Add Channel map parser.
topology: Add binary file builder.
topology: autotools: Add build support for topology core
topology: doxygen: Add doxygen support for topology core.
conf: topology: Add topology file for broadwell audio DSP
configure.ac | 11 +-
doc/doxygen.cfg.in | 7 +-
doc/index.doxygen | 1 +
include/Makefile.am | 4 +
include/sound/Makefile.am | 3 +-
include/sound/asoc.h | 388 ++++++++++++++++++
include/sound/tlv.h | 23 ++
include/topology.h | 497 ++++++++++++++++++++++
src/Makefile.am | 7 +
src/conf/Makefile.am | 2 +-
src/conf/topology/Makefile.am | 1 +
src/conf/topology/broadwell/Makefile.am | 4 +
src/conf/topology/broadwell/broadwell.conf | 375 +++++++++++++++++
src/topology/Makefile.am | 19 +
src/topology/builder.c | 327 +++++++++++++++
src/topology/channel.c | 122 ++++++
src/topology/ctl.c | 613 +++++++++++++++++++++++++++
src/topology/dapm.c | 562 +++++++++++++++++++++++++
src/topology/data.c | 396 ++++++++++++++++++
src/topology/elem.c | 187 +++++++++
src/topology/ops.c | 84 ++++
src/topology/parser.c | 359 ++++++++++++++++
src/topology/pcm.c | 639 +++++++++++++++++++++++++++++
src/topology/text.c | 88 ++++
src/topology/tplg_local.h | 234 +++++++++++
25 files changed, 4948 insertions(+), 5 deletions(-)
create mode 100644 include/sound/asoc.h
create mode 100644 include/sound/tlv.h
create mode 100644 include/topology.h
create mode 100644 src/conf/topology/Makefile.am
create mode 100644 src/conf/topology/broadwell/Makefile.am
create mode 100644 src/conf/topology/broadwell/broadwell.conf
create mode 100644 src/topology/Makefile.am
create mode 100644 src/topology/builder.c
create mode 100644 src/topology/channel.c
create mode 100644 src/topology/ctl.c
create mode 100644 src/topology/dapm.c
create mode 100644 src/topology/data.c
create mode 100644 src/topology/elem.c
create mode 100644 src/topology/ops.c
create mode 100644 src/topology/parser.c
create mode 100644 src/topology/pcm.c
create mode 100644 src/topology/text.c
create mode 100644 src/topology/tplg_local.h
--
2.1.4
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v3 01/13] topology: uapi: Add UAPI headers for topology ABI
2015-07-29 16:45 [PATCH v3 00/13] alsa-lib topology support Liam Girdwood
@ 2015-07-29 16:45 ` Liam Girdwood
2015-07-29 16:45 ` [PATCH v3 02/13] topology: Add topology core parser Liam Girdwood
` (12 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Liam Girdwood @ 2015-07-29 16:45 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, Liam Girdwood, Mark Brown
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
---
include/sound/Makefile.am | 3 +-
include/sound/asoc.h | 388 ++++++++++++++++++++++++++++++++++++++++++++++
include/sound/tlv.h | 23 +++
3 files changed, 413 insertions(+), 1 deletion(-)
create mode 100644 include/sound/asoc.h
create mode 100644 include/sound/tlv.h
diff --git a/include/sound/Makefile.am b/include/sound/Makefile.am
index 31aa2db..b659985 100644
--- a/include/sound/Makefile.am
+++ b/include/sound/Makefile.am
@@ -1,6 +1,7 @@
alsasoundincludedir = ${includedir}/alsa/sound
alsasoundinclude_HEADERS = asound_fm.h hdsp.h hdspm.h sb16_csp.h \
- sscape_ioctl.h emu10k1.h type_compat.h
+ sscape_ioctl.h emu10k1.h type_compat.h \
+ asoc.h tlv.h
noinst_HEADERS = asound.h asoundef.h asequencer.h
diff --git a/include/sound/asoc.h b/include/sound/asoc.h
new file mode 100644
index 0000000..bb6dcf3
--- /dev/null
+++ b/include/sound/asoc.h
@@ -0,0 +1,388 @@
+/*
+ * uapi/sound/asoc.h -- ALSA SoC Firmware Controls and DAPM
+ *
+ * Copyright (C) 2012 Texas Instruments Inc.
+ * Copyright (C) 2015 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Simple file API to load FW that includes mixers, coefficients, DAPM graphs,
+ * algorithms, equalisers, DAIs, widgets etc.
+*/
+
+#ifndef __LINUX_UAPI_SND_ASOC_H
+#define __LINUX_UAPI_SND_ASOC_H
+
+/*
+ * Maximum number of channels topology kcontrol can represent.
+ */
+#define SND_SOC_TPLG_MAX_CHAN 8
+
+/*
+ * Maximum number of PCM formats capability
+ */
+#define SND_SOC_TPLG_MAX_FORMATS 16
+
+/*
+ * Maximum number of PCM stream configs
+ */
+#define SND_SOC_TPLG_STREAM_CONFIG_MAX 8
+
+/* individual kcontrol info types - can be mixed with other types */
+#define SND_SOC_TPLG_CTL_VOLSW 1
+#define SND_SOC_TPLG_CTL_VOLSW_SX 2
+#define SND_SOC_TPLG_CTL_VOLSW_XR_SX 3
+#define SND_SOC_TPLG_CTL_ENUM 4
+#define SND_SOC_TPLG_CTL_BYTES 5
+#define SND_SOC_TPLG_CTL_ENUM_VALUE 6
+#define SND_SOC_TPLG_CTL_RANGE 7
+#define SND_SOC_TPLG_CTL_STROBE 8
+
+
+/* individual widget kcontrol info types - can be mixed with other types */
+#define SND_SOC_TPLG_DAPM_CTL_VOLSW 64
+#define SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE 65
+#define SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT 66
+#define SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE 67
+#define SND_SOC_TPLG_DAPM_CTL_PIN 68
+
+/* DAPM widget types - add new items to the end */
+#define SND_SOC_TPLG_DAPM_INPUT 0
+#define SND_SOC_TPLG_DAPM_OUTPUT 1
+#define SND_SOC_TPLG_DAPM_MUX 2
+#define SND_SOC_TPLG_DAPM_MIXER 3
+#define SND_SOC_TPLG_DAPM_PGA 4
+#define SND_SOC_TPLG_DAPM_OUT_DRV 5
+#define SND_SOC_TPLG_DAPM_ADC 6
+#define SND_SOC_TPLG_DAPM_DAC 7
+#define SND_SOC_TPLG_DAPM_SWITCH 8
+#define SND_SOC_TPLG_DAPM_PRE 9
+#define SND_SOC_TPLG_DAPM_POST 10
+#define SND_SOC_TPLG_DAPM_AIF_IN 11
+#define SND_SOC_TPLG_DAPM_AIF_OUT 12
+#define SND_SOC_TPLG_DAPM_DAI_IN 13
+#define SND_SOC_TPLG_DAPM_DAI_OUT 14
+#define SND_SOC_TPLG_DAPM_DAI_LINK 15
+#define SND_SOC_TPLG_DAPM_LAST SND_SOC_TPLG_DAPM_DAI_LINK
+
+/* Header magic number and string sizes */
+#define SND_SOC_TPLG_MAGIC 0x41536F43 /* ASoC */
+
+/* string sizes */
+#define SND_SOC_TPLG_NUM_TEXTS 16
+
+/* ABI version */
+#define SND_SOC_TPLG_ABI_VERSION 0x3
+
+/* Max size of TLV data */
+#define SND_SOC_TPLG_TLV_SIZE 32
+
+/*
+ * File and Block header data types.
+ * Add new generic and vendor types to end of list.
+ * Generic types are handled by the core whilst vendors types are passed
+ * to the component drivers for handling.
+ */
+#define SND_SOC_TPLG_TYPE_MIXER 1
+#define SND_SOC_TPLG_TYPE_BYTES 2
+#define SND_SOC_TPLG_TYPE_ENUM 3
+#define SND_SOC_TPLG_TYPE_DAPM_GRAPH 4
+#define SND_SOC_TPLG_TYPE_DAPM_WIDGET 5
+#define SND_SOC_TPLG_TYPE_DAI_LINK 6
+#define SND_SOC_TPLG_TYPE_PCM 7
+#define SND_SOC_TPLG_TYPE_MANIFEST 8
+#define SND_SOC_TPLG_TYPE_CODEC_LINK 9
+#define SND_SOC_TPLG_TYPE_PDATA 10
+#define SND_SOC_TPLG_TYPE_MAX SND_SOC_TPLG_TYPE_PDATA
+
+/* vendor block IDs - please add new vendor types to end */
+#define SND_SOC_TPLG_TYPE_VENDOR_FW 1000
+#define SND_SOC_TPLG_TYPE_VENDOR_CONFIG 1001
+#define SND_SOC_TPLG_TYPE_VENDOR_COEFF 1002
+#define SND_SOC_TPLG_TYPEVENDOR_CODEC 1003
+
+#define SND_SOC_TPLG_STREAM_PLAYBACK 0
+#define SND_SOC_TPLG_STREAM_CAPTURE 1
+
+/*
+ * Block Header.
+ * This header precedes all object and object arrays below.
+ */
+struct snd_soc_tplg_hdr {
+ __le32 magic; /* magic number */
+ __le32 abi; /* ABI version */
+ __le32 version; /* optional vendor specific version details */
+ __le32 type; /* SND_SOC_TPLG_TYPE_ */
+ __le32 size; /* size of this structure */
+ __le32 vendor_type; /* optional vendor specific type info */
+ __le32 payload_size; /* data bytes, excluding this header */
+ __le32 index; /* identifier for block */
+ __le32 count; /* number of elements in block */
+} __attribute__((packed));
+
+/*
+ * Private data.
+ * All topology objects may have private data that can be used by the driver or
+ * firmware. Core will ignore this data.
+ */
+struct snd_soc_tplg_private {
+ __le32 size; /* in bytes of private data */
+ char data[0];
+} __attribute__((packed));
+
+/*
+ * Kcontrol TLV data.
+ */
+struct snd_soc_tplg_ctl_tlv {
+ __le32 size; /* in bytes aligned to 4 */
+ __le32 numid; /* control element numeric identification */
+ __le32 count; /* number of elem in data array */
+ __le32 data[SND_SOC_TPLG_TLV_SIZE];
+} __attribute__((packed));
+
+/*
+ * Kcontrol channel data
+ */
+struct snd_soc_tplg_channel {
+ __le32 size; /* in bytes of this structure */
+ __le32 reg;
+ __le32 shift;
+ __le32 id; /* ID maps to Left, Right, LFE etc */
+} __attribute__((packed));
+
+/*
+ * Kcontrol Operations IDs
+ */
+struct snd_soc_tplg_kcontrol_ops_id {
+ __le32 get;
+ __le32 put;
+ __le32 info;
+} __attribute__((packed));
+
+/*
+ * kcontrol header
+ */
+struct snd_soc_tplg_ctl_hdr {
+ __le32 size; /* in bytes of this structure */
+ __le32 type;
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ __le32 access;
+ struct snd_soc_tplg_kcontrol_ops_id ops;
+ __le32 tlv_size; /* non zero means control has TLV data */
+} __attribute__((packed));
+
+/*
+ * Stream Capabilities
+ */
+struct snd_soc_tplg_stream_caps {
+ __le32 size; /* in bytes of this structure */
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ __le64 formats[SND_SOC_TPLG_MAX_FORMATS]; /* supported formats SNDRV_PCM_FMTBIT_* */
+ __le32 rates; /* supported rates SNDRV_PCM_RATE_* */
+ __le32 rate_min; /* min rate */
+ __le32 rate_max; /* max rate */
+ __le32 channels_min; /* min channels */
+ __le32 channels_max; /* max channels */
+ __le32 periods_min; /* min number of periods */
+ __le32 periods_max; /* max number of periods */
+ __le32 period_size_min; /* min period size bytes */
+ __le32 period_size_max; /* max period size bytes */
+ __le32 buffer_size_min; /* min buffer size bytes */
+ __le32 buffer_size_max; /* max buffer size bytes */
+} __attribute__((packed));
+
+/*
+ * FE or BE Stream configuration supported by SW/FW
+ */
+struct snd_soc_tplg_stream {
+ __le32 size; /* in bytes of this structure */
+ __le64 format; /* SNDRV_PCM_FMTBIT_* */
+ __le32 rate; /* SNDRV_PCM_RATE_* */
+ __le32 period_bytes; /* size of period in bytes */
+ __le32 buffer_bytes; /* size of buffer in bytes */
+ __le32 channels; /* channels */
+ __le32 tdm_slot; /* optional BE bitmask of supported TDM slots */
+ __le32 dai_fmt; /* SND_SOC_DAIFMT_ */
+} __attribute__((packed));
+
+/*
+ * Duplex stream configuration supported by SW/FW.
+ */
+struct snd_soc_tplg_stream_config {
+ __le32 size; /* in bytes of this structure */
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ struct snd_soc_tplg_stream playback;
+ struct snd_soc_tplg_stream capture;
+} __attribute__((packed));
+
+/*
+ * Manifest. List totals for each payload type. Not used in parsing, but will
+ * be passed to the component driver before any other objects in order for any
+ * global component resource allocations.
+ *
+ * File block representation for manifest :-
+ * +-----------------------------------+----+
+ * | struct snd_soc_tplg_hdr | 1 |
+ * +-----------------------------------+----+
+ * | struct snd_soc_tplg_manifest | 1 |
+ * +-----------------------------------+----+
+ */
+struct snd_soc_tplg_manifest {
+ __le32 size; /* in bytes of this structure */
+ __le32 control_elems; /* number of control elements */
+ __le32 widget_elems; /* number of widget elements */
+ __le32 graph_elems; /* number of graph elements */
+ __le32 dai_elems; /* number of DAI elements */
+ __le32 dai_link_elems; /* number of DAI link elements */
+ struct snd_soc_tplg_private priv;
+} __attribute__((packed));
+
+/*
+ * Mixer kcontrol.
+ *
+ * File block representation for mixer kcontrol :-
+ * +-----------------------------------+----+
+ * | struct snd_soc_tplg_hdr | 1 |
+ * +-----------------------------------+----+
+ * | struct snd_soc_tplg_mixer_control | N |
+ * +-----------------------------------+----+
+ */
+struct snd_soc_tplg_mixer_control {
+ struct snd_soc_tplg_ctl_hdr hdr;
+ __le32 size; /* in bytes of this structure */
+ __le32 min;
+ __le32 max;
+ __le32 platform_max;
+ __le32 invert;
+ __le32 num_channels;
+ struct snd_soc_tplg_channel channel[SND_SOC_TPLG_MAX_CHAN];
+ struct snd_soc_tplg_ctl_tlv tlv;
+ struct snd_soc_tplg_private priv;
+} __attribute__((packed));
+
+/*
+ * Enumerated kcontrol
+ *
+ * File block representation for enum kcontrol :-
+ * +-----------------------------------+----+
+ * | struct snd_soc_tplg_hdr | 1 |
+ * +-----------------------------------+----+
+ * | struct snd_soc_tplg_enum_control | N |
+ * +-----------------------------------+----+
+ */
+struct snd_soc_tplg_enum_control {
+ struct snd_soc_tplg_ctl_hdr hdr;
+ __le32 size; /* in bytes of this structure */
+ __le32 num_channels;
+ struct snd_soc_tplg_channel channel[SND_SOC_TPLG_MAX_CHAN];
+ __le32 items;
+ __le32 mask;
+ __le32 count;
+ char texts[SND_SOC_TPLG_NUM_TEXTS][SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ __le32 values[SND_SOC_TPLG_NUM_TEXTS * SNDRV_CTL_ELEM_ID_NAME_MAXLEN / 4];
+ struct snd_soc_tplg_private priv;
+} __attribute__((packed));
+
+/*
+ * Bytes kcontrol
+ *
+ * File block representation for bytes kcontrol :-
+ * +-----------------------------------+----+
+ * | struct snd_soc_tplg_hdr | 1 |
+ * +-----------------------------------+----+
+ * | struct snd_soc_tplg_bytes_control | N |
+ * +-----------------------------------+----+
+ */
+struct snd_soc_tplg_bytes_control {
+ struct snd_soc_tplg_ctl_hdr hdr;
+ __le32 size; /* in bytes of this structure */
+ __le32 max;
+ __le32 mask;
+ __le32 base;
+ __le32 num_regs;
+ struct snd_soc_tplg_private priv;
+} __attribute__((packed));
+
+/*
+ * DAPM Graph Element
+ *
+ * File block representation for DAPM graph elements :-
+ * +-------------------------------------+----+
+ * | struct snd_soc_tplg_hdr | 1 |
+ * +-------------------------------------+----+
+ * | struct snd_soc_tplg_dapm_graph_elem | N |
+ * +-------------------------------------+----+
+ */
+struct snd_soc_tplg_dapm_graph_elem {
+ char sink[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ char control[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ char source[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+} __attribute__((packed));
+
+/*
+ * DAPM Widget.
+ *
+ * File block representation for DAPM widget :-
+ * +-------------------------------------+-----+
+ * | struct snd_soc_tplg_hdr | 1 |
+ * +-------------------------------------+-----+
+ * | struct snd_soc_tplg_dapm_widget | N |
+ * +-------------------------------------+-----+
+ * | struct snd_soc_tplg_enum_control | 0|1 |
+ * | struct snd_soc_tplg_mixer_control | 0|N |
+ * +-------------------------------------+-----+
+ *
+ * Optional enum or mixer control can be appended to the end of each widget
+ * in the block.
+ */
+struct snd_soc_tplg_dapm_widget {
+ __le32 size; /* in bytes of this structure */
+ __le32 id; /* SND_SOC_DAPM_CTL */
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ char sname[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+
+ __le32 reg; /* negative reg = no direct dapm */
+ __le32 shift; /* bits to shift */
+ __le32 mask; /* non-shifted mask */
+ __le32 subseq; /* sort within widget type */
+ __u32 invert; /* invert the power bit */
+ __u32 ignore_suspend; /* kept enabled over suspend */
+ __u16 event_flags;
+ __u16 event_type;
+ __u16 num_kcontrols;
+ struct snd_soc_tplg_private priv;
+ /*
+ * kcontrols that relate to this widget
+ * follow here after widget private data
+ */
+} __attribute__((packed));
+
+struct snd_soc_tplg_pcm_cfg_caps {
+ struct snd_soc_tplg_stream_caps caps;
+ struct snd_soc_tplg_stream_config configs[SND_SOC_TPLG_STREAM_CONFIG_MAX];
+ __le32 num_configs; /* number of configs */
+} __attribute__((packed));
+
+/*
+ * Describes SW/FW specific features of PCM or DAI link.
+ *
+ * File block representation for PCM/DAI-Link :-
+ * +-----------------------------------+-----+
+ * | struct snd_soc_tplg_hdr | 1 |
+ * +-----------------------------------+-----+
+ * | struct snd_soc_tplg_dapm_pcm_dai | N |
+ * +-----------------------------------+-----+
+ */
+struct snd_soc_tplg_pcm_dai {
+ __le32 size; /* in bytes of this structure */
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ __le32 id; /* unique ID - used to match */
+ __le32 playback; /* supports playback mode */
+ __le32 capture; /* supports capture mode */
+ __le32 compress; /* 1 = compressed; 0 = PCM */
+ struct snd_soc_tplg_pcm_cfg_caps capconf[2]; /* capabilities and configs */
+} __attribute__((packed));
+
+#endif
diff --git a/include/sound/tlv.h b/include/sound/tlv.h
new file mode 100644
index 0000000..33d747d
--- /dev/null
+++ b/include/sound/tlv.h
@@ -0,0 +1,23 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ */
+
+#ifndef __UAPI_SOUND_TLV_H
+#define __UAPI_SOUND_TLV_H
+
+#define SNDRV_CTL_TLVT_CONTAINER 0 /* one level down - group of TLVs */
+#define SNDRV_CTL_TLVT_DB_SCALE 1 /* dB scale */
+#define SNDRV_CTL_TLVT_DB_LINEAR 2 /* linear volume */
+#define SNDRV_CTL_TLVT_DB_RANGE 3 /* dB range container */
+#define SNDRV_CTL_TLVT_DB_MINMAX 4 /* dB scale with min/max */
+#define SNDRV_CTL_TLVT_DB_MINMAX_MUTE 5 /* dB scale with min/max with mute */
+
+#endif
--
2.1.4
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 02/13] topology: Add topology core parser.
2015-07-29 16:45 [PATCH v3 00/13] alsa-lib topology support Liam Girdwood
2015-07-29 16:45 ` [PATCH v3 01/13] topology: uapi: Add UAPI headers for topology ABI Liam Girdwood
@ 2015-07-29 16:45 ` Liam Girdwood
2015-07-29 16:45 ` [PATCH v3 03/13] topology: Add text section parser Liam Girdwood
` (11 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Liam Girdwood @ 2015-07-29 16:45 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, Liam Girdwood, Mark Brown
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>
---
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 0000000..f604ed1
--- /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 0000000..32ba2c1
--- /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 0000000..ed25bb8
--- /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 0000000..688c78f
--- /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.1.4
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 03/13] topology: Add text section parser.
2015-07-29 16:45 [PATCH v3 00/13] alsa-lib topology support Liam Girdwood
2015-07-29 16:45 ` [PATCH v3 01/13] topology: uapi: Add UAPI headers for topology ABI Liam Girdwood
2015-07-29 16:45 ` [PATCH v3 02/13] topology: Add topology core parser Liam Girdwood
@ 2015-07-29 16:45 ` Liam Girdwood
2015-07-29 16:45 ` [PATCH v3 04/13] topology: Add PCM parser Liam Girdwood
` (10 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Liam Girdwood @ 2015-07-29 16:45 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, Liam Girdwood, Mark Brown
Parse text lists (like enum values) and store for later attachment
to other objects.
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
---
src/topology/text.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 88 insertions(+)
create mode 100644 src/topology/text.c
diff --git a/src/topology/text.c b/src/topology/text.c
new file mode 100644
index 0000000..ebb6e38
--- /dev/null
+++ b/src/topology/text.c
@@ -0,0 +1,88 @@
+/*
+ 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"
+
+#define TEXT_SIZE_MAX \
+ (SND_SOC_TPLG_NUM_TEXTS * SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+
+static int parse_text_values(snd_config_t *cfg, struct tplg_elem *elem)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *value = NULL;
+ int j = 0;
+
+ tplg_dbg(" Text Values: %s\n", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+ n = snd_config_iterator_entry(i);
+
+ if (j == SND_SOC_TPLG_NUM_TEXTS) {
+ tplg_dbg("error: text string number exceeds %d\n", j);
+ return -ENOMEM;
+ }
+
+ /* get value */
+ if (snd_config_get_string(n, &value) < 0)
+ continue;
+
+ elem_copy_text(&elem->texts[j][0], value,
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+ tplg_dbg("\t%s\n", &elem->texts[j][0]);
+
+ j++;
+ }
+
+ return 0;
+}
+
+/* Parse Text data */
+int tplg_parse_text(snd_tplg_t *tplg, snd_config_t *cfg,
+ void *private ATTRIBUTE_UNUSED)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id;
+ int err = 0;
+ struct tplg_elem *elem;
+
+ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_TEXT);
+ if (!elem)
+ return -ENOMEM;
+
+ 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, "values") == 0) {
+ err = parse_text_values(n, elem);
+ if (err < 0) {
+ SNDERR("error: failed to parse text values");
+ return err;
+ }
+ continue;
+ }
+ }
+
+ return err;
+}
--
2.1.4
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 04/13] topology: Add PCM parser.
2015-07-29 16:45 [PATCH v3 00/13] alsa-lib topology support Liam Girdwood
` (2 preceding siblings ...)
2015-07-29 16:45 ` [PATCH v3 03/13] topology: Add text section parser Liam Girdwood
@ 2015-07-29 16:45 ` Liam Girdwood
2015-07-29 16:45 ` [PATCH v3 05/13] topology: Add operations parser Liam Girdwood
` (9 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Liam Girdwood @ 2015-07-29 16:45 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, Liam Girdwood, Mark Brown
Parse PCM configurations and capabilities. These can then be used to define
the capabilities and config for FE DAI links, PCM devices and
codec <-> codec style links.
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
---
src/topology/pcm.c | 639 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 639 insertions(+)
create mode 100644 src/topology/pcm.c
diff --git a/src/topology/pcm.c b/src/topology/pcm.c
new file mode 100644
index 0000000..8f23a6f
--- /dev/null
+++ b/src/topology/pcm.c
@@ -0,0 +1,639 @@
+/*
+ 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"
+
+struct tplg_elem *lookup_pcm_dai_stream(struct list_head *base, const char* id)
+{
+ struct list_head *pos;
+ struct tplg_elem *elem;
+ struct snd_soc_tplg_pcm_dai *pcm_dai;
+
+ list_for_each(pos, base) {
+
+ elem = list_entry(pos, struct tplg_elem, list);
+ if (elem->type != OBJECT_TYPE_PCM)
+ return NULL;
+
+ pcm_dai = elem->pcm;
+
+ if (pcm_dai && (!strcmp(pcm_dai->capconf[0].caps.name, id)
+ || !strcmp(pcm_dai->capconf[1].caps.name, id)))
+ return elem;
+ }
+
+ return NULL;
+}
+
+/* copy referenced caps to the pcm */
+static void copy_pcm_caps(const char *id, struct snd_soc_tplg_stream_caps *caps,
+ struct tplg_elem *ref_elem)
+{
+ struct snd_soc_tplg_stream_caps *ref_caps = ref_elem->stream_caps;
+
+ tplg_dbg("Copy pcm caps (%ld bytes) from '%s' to '%s' \n",
+ sizeof(*caps), ref_elem->id, id);
+
+ *caps = *ref_caps;
+}
+
+/* copy referenced config to the pcm */
+static void copy_pcm_config(const char *id,
+ struct snd_soc_tplg_stream_config *cfg, struct tplg_elem *ref_elem)
+{
+ struct snd_soc_tplg_stream_config *ref_cfg = ref_elem->stream_cfg;
+
+ tplg_dbg("Copy pcm config (%ld bytes) from '%s' to '%s' \n",
+ sizeof(*cfg), ref_elem->id, id);
+
+ *cfg = *ref_cfg;
+}
+
+/* check referenced config and caps for a pcm */
+static int tplg_build_pcm_cfg_caps(snd_tplg_t *tplg, struct tplg_elem *elem)
+{
+ struct tplg_elem *ref_elem = NULL;
+ struct snd_soc_tplg_pcm_cfg_caps *capconf;
+ struct snd_soc_tplg_pcm_dai *pcm_dai;
+ unsigned int i, j;
+
+ switch (elem->type) {
+ case OBJECT_TYPE_PCM:
+ pcm_dai = elem->pcm;
+ break;
+ case OBJECT_TYPE_BE:
+ pcm_dai = elem->be;
+ break;
+ case OBJECT_TYPE_CC:
+ pcm_dai = elem->cc;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (i = 0; i < 2; i++) {
+ capconf = &pcm_dai->capconf[i];
+
+ ref_elem = tplg_elem_lookup(&tplg->pcm_caps_list,
+ capconf->caps.name, OBJECT_TYPE_STREAM_CAPS);
+
+ if (ref_elem != NULL)
+ copy_pcm_caps(elem->id, &capconf->caps, ref_elem);
+
+ for (j = 0; j < capconf->num_configs; j++) {
+ ref_elem = tplg_elem_lookup(&tplg->pcm_config_list,
+ capconf->configs[j].name,
+ OBJECT_TYPE_STREAM_CONFIG);
+
+ if (ref_elem != NULL)
+ copy_pcm_config(elem->id,
+ &capconf->configs[j],
+ ref_elem);
+ }
+ }
+
+ return 0;
+}
+
+int tplg_build_pcm_dai(snd_tplg_t *tplg, unsigned int type)
+{
+ struct list_head *base, *pos;
+ struct tplg_elem *elem;
+ int err = 0;
+
+ switch (type) {
+ case OBJECT_TYPE_PCM:
+ base = &tplg->pcm_list;
+ break;
+ case OBJECT_TYPE_BE:
+ base = &tplg->be_list;
+ break;
+ case OBJECT_TYPE_CC:
+ base = &tplg->cc_list;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ list_for_each(pos, base) {
+
+ elem = list_entry(pos, struct tplg_elem, list);
+ if (elem->type != type) {
+ SNDERR("error: invalid elem '%s'\n", elem->id);
+ return -EINVAL;
+ }
+
+ err = tplg_build_pcm_cfg_caps(tplg, elem);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/* PCM stream configuration */
+static int tplg_parse_stream_cfg(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ snd_config_t *cfg, void *private)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ struct snd_soc_tplg_stream_config *sc = private;
+ struct snd_soc_tplg_stream *stream;
+ const char *id, *val;
+ snd_pcm_format_t format;
+ int ret;
+
+ snd_config_get_id(cfg, &id);
+
+ if (strcmp(id, "playback") == 0)
+ stream = &sc->playback;
+ else if (strcmp(id, "capture") == 0)
+ stream = &sc->capture;
+ else
+ return -EINVAL;
+
+ tplg_dbg("\t%s:\n", id);
+
+ stream->size = sizeof(*stream);
+
+ snd_config_for_each(i, next, cfg) {
+
+ n = snd_config_iterator_entry(i);
+
+ if (snd_config_get_id(n, &id) < 0)
+ return -EINVAL;
+
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ if (strcmp(id, "format") == 0) {
+ format = snd_pcm_format_value(val);
+ if (format == SND_PCM_FORMAT_UNKNOWN) {
+ SNDERR("error: unsupported stream format %s\n",
+ val);
+ return -EINVAL;
+ }
+
+ stream->format = format;
+ tplg_dbg("\t\t%s: %s\n", id, val);
+ continue;
+ }
+
+ if (strcmp(id, "rate") == 0) {
+ stream->rate = atoi(val);
+ tplg_dbg("\t\t%s: %d\n", id, stream->rate);
+ continue;
+ }
+
+ if (strcmp(id, "channels") == 0) {
+ stream->channels = atoi(val);
+ tplg_dbg("\t\t%s: %d\n", id, stream->channels);
+ continue;
+ }
+
+ if (strcmp(id, "tdm_slot") == 0) {
+ stream->tdm_slot = strtol(val, NULL, 16);
+ tplg_dbg("\t\t%s: 0x%x\n", id, stream->tdm_slot);
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/* Parse pcm configuration */
+int tplg_parse_pcm_config(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
+{
+ struct snd_soc_tplg_stream_config *sc;
+ struct tplg_elem *elem;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id;
+ int err;
+
+ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_STREAM_CONFIG);
+ if (!elem)
+ return -ENOMEM;
+
+ sc = elem->stream_cfg;
+ sc->size = elem->size;
+
+ tplg_dbg(" PCM Config: %s\n", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* skip comments */
+ if (strcmp(id, "comment") == 0)
+ continue;
+ if (id[0] == '#')
+ continue;
+
+ if (strcmp(id, "config") == 0) {
+ err = tplg_parse_compound(tplg, n,
+ tplg_parse_stream_cfg, sc);
+ if (err < 0)
+ return err;
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+static int split_format(struct snd_soc_tplg_stream_caps *caps, char *str)
+{
+ char *s = NULL;
+ snd_pcm_format_t format;
+ int i = 0, ret;
+
+ s = strtok(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);
+ return -EINVAL;
+ }
+
+ caps->formats[i] = format;
+ s = strtok(NULL, ", ");
+ i++;
+ }
+
+ return 0;
+}
+
+/* Parse pcm Capabilities */
+int tplg_parse_pcm_caps(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
+{
+ struct snd_soc_tplg_stream_caps *sc;
+ struct tplg_elem *elem;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id, *val;
+ char *s;
+ int err;
+
+ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_STREAM_CAPS);
+ if (!elem)
+ return -ENOMEM;
+
+ sc = elem->stream_caps;
+ sc->size = elem->size;
+ elem_copy_text(sc->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+
+ tplg_dbg(" PCM Capabilities: %s\n", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* skip comments */
+ if (strcmp(id, "comment") == 0)
+ continue;
+ if (id[0] == '#')
+ continue;
+
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ if (strcmp(id, "formats") == 0) {
+ s = strdup(val);
+ if (s == NULL)
+ return -ENOMEM;
+
+ err = split_format(sc, s);
+ free(s);
+
+ if (err < 0)
+ return err;
+
+ tplg_dbg("\t\t%s: %s\n", id, val);
+ continue;
+ }
+
+ if (strcmp(id, "rate_min") == 0) {
+ sc->rate_min = atoi(val);
+ tplg_dbg("\t\t%s: %d\n", id, sc->rate_min);
+ continue;
+ }
+
+ if (strcmp(id, "rate_max") == 0) {
+ sc->rate_max = atoi(val);
+ tplg_dbg("\t\t%s: %d\n", id, sc->rate_max);
+ continue;
+ }
+
+ if (strcmp(id, "channels_min") == 0) {
+ sc->channels_min = atoi(val);
+ tplg_dbg("\t\t%s: %d\n", id, sc->channels_min);
+ continue;
+ }
+
+ if (strcmp(id, "channels_max") == 0) {
+ sc->channels_max = atoi(val);
+ tplg_dbg("\t\t%s: %d\n", id, sc->channels_max);
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+static int tplg_parse_pcm_cfg(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ snd_config_t *cfg, void *private)
+{
+ struct snd_soc_tplg_pcm_cfg_caps *capconf = private;
+ struct snd_soc_tplg_stream_config *configs = capconf->configs;
+ unsigned int *num_configs = &capconf->num_configs;
+ const char *value;
+
+ if (*num_configs == SND_SOC_TPLG_STREAM_CONFIG_MAX)
+ return -EINVAL;
+
+ if (snd_config_get_string(cfg, &value) < 0)
+ return EINVAL;
+
+ elem_copy_text(configs[*num_configs].name, value,
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+
+ *num_configs += 1;
+
+ tplg_dbg("\t\t\t%s\n", value);
+
+ return 0;
+}
+
+/* Parse the cap and config of a pcm */
+int tplg_parse_pcm_cap_cfg(snd_tplg_t *tplg, snd_config_t *cfg,
+ void *private)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ struct tplg_elem *elem = private;
+ struct snd_soc_tplg_pcm_dai *pcm_dai;
+ const char *id, *value;
+ int err, stream;
+
+ if (elem->type == OBJECT_TYPE_PCM)
+ pcm_dai = elem->pcm;
+ else if (elem->type == OBJECT_TYPE_BE)
+ pcm_dai = elem->be;
+ else if (elem->type == OBJECT_TYPE_CC)
+ pcm_dai = elem->cc;
+ else
+ return -EINVAL;
+
+ snd_config_get_id(cfg, &id);
+
+ tplg_dbg("\t%s:\n", id);
+
+ if (strcmp(id, "playback") == 0) {
+ stream = SND_SOC_TPLG_STREAM_PLAYBACK;
+ pcm_dai->playback = 1;
+ } else if (strcmp(id, "capture") == 0) {
+ stream = SND_SOC_TPLG_STREAM_CAPTURE;
+ pcm_dai->capture = 1;
+ } else
+ return -EINVAL;
+
+ snd_config_for_each(i, next, cfg) {
+
+ n = snd_config_iterator_entry(i);
+
+ /* get id */
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ if (strcmp(id, "capabilities") == 0) {
+ if (snd_config_get_string(n, &value) < 0)
+ continue;
+
+ elem_copy_text(pcm_dai->capconf[stream].caps.name, value,
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+
+ tplg_dbg("\t\t%s\n\t\t\t%s\n", id, value);
+ continue;
+ }
+
+ if (strcmp(id, "configs") == 0) {
+ tplg_dbg("\t\tconfigs:\n");
+ err = tplg_parse_compound(tplg, n, tplg_parse_pcm_cfg,
+ &pcm_dai->capconf[stream]);
+ if (err < 0)
+ return err;
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/* Parse pcm */
+int tplg_parse_pcm(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
+{
+ struct snd_soc_tplg_pcm_dai *pcm_dai;
+ struct tplg_elem *elem;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id, *val = NULL;
+ int err;
+
+ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_PCM);
+ if (!elem)
+ return -ENOMEM;
+
+ pcm_dai = elem->pcm;
+ pcm_dai->size = elem->size;
+ elem_copy_text(pcm_dai->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+
+ tplg_dbg(" PCM: %s\n", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* skip comments */
+ if (strcmp(id, "comment") == 0)
+ continue;
+ if (id[0] == '#')
+ continue;
+
+ if (strcmp(id, "index") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ elem->index = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, elem->index);
+ continue;
+ }
+
+ if (strcmp(id, "id") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ pcm_dai->id = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, pcm_dai->id);
+ continue;
+ }
+
+ if (strcmp(id, "pcm") == 0) {
+ err = tplg_parse_compound(tplg, n,
+ tplg_parse_pcm_cap_cfg, elem);
+ if (err < 0)
+ return err;
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/* Parse be */
+int tplg_parse_be(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
+{
+ struct snd_soc_tplg_pcm_dai *pcm_dai;
+ struct tplg_elem *elem;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id, *val = NULL;
+ int err;
+
+ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_BE);
+ if (!elem)
+ return -ENOMEM;
+
+ pcm_dai = elem->be;
+ pcm_dai->size = elem->size;
+ elem_copy_text(pcm_dai->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+
+ tplg_dbg(" BE: %s\n", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* skip comments */
+ if (strcmp(id, "comment") == 0)
+ continue;
+ if (id[0] == '#')
+ continue;
+
+ if (strcmp(id, "index") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ elem->index = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, elem->index);
+ continue;
+ }
+
+ if (strcmp(id, "id") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ pcm_dai->id = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, pcm_dai->id);
+ continue;
+ }
+
+ if (strcmp(id, "be") == 0) {
+ err = tplg_parse_compound(tplg, n,
+ tplg_parse_pcm_cap_cfg, elem);
+ if (err < 0)
+ return err;
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/* Parse cc */
+int tplg_parse_cc(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
+{
+ struct snd_soc_tplg_pcm_dai *pcm_dai;
+ struct tplg_elem *elem;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id, *val = NULL;
+ int err;
+
+ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_CC);
+ if (!elem)
+ return -ENOMEM;
+
+ pcm_dai = elem->cc;
+ pcm_dai->size = elem->size;
+
+ tplg_dbg(" CC: %s\n", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* skip comments */
+ if (strcmp(id, "comment") == 0)
+ continue;
+ if (id[0] == '#')
+ continue;
+
+ if (strcmp(id, "index") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ elem->index = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, elem->index);
+ continue;
+ }
+
+ if (strcmp(id, "id") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ pcm_dai->id = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, pcm_dai->id);
+ continue;
+ }
+
+ if (strcmp(id, "cc") == 0) {
+ err = tplg_parse_compound(tplg, n,
+ tplg_parse_pcm_cap_cfg, elem);
+ if (err < 0)
+ return err;
+ continue;
+ }
+ }
+
+ return 0;
+}
--
2.1.4
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 05/13] topology: Add operations parser
2015-07-29 16:45 [PATCH v3 00/13] alsa-lib topology support Liam Girdwood
` (3 preceding siblings ...)
2015-07-29 16:45 ` [PATCH v3 04/13] topology: Add PCM parser Liam Girdwood
@ 2015-07-29 16:45 ` Liam Girdwood
2015-07-29 16:45 ` [PATCH v3 06/13] topology: Add private data parser Liam Girdwood
` (8 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Liam Girdwood @ 2015-07-29 16:45 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, Liam Girdwood, Mark Brown
Parse operations so we can bind them to kcontrols in the kernel.
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
---
src/topology/ops.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 84 insertions(+)
create mode 100644 src/topology/ops.c
diff --git a/src/topology/ops.c b/src/topology/ops.c
new file mode 100644
index 0000000..243d8c5
--- /dev/null
+++ b/src/topology/ops.c
@@ -0,0 +1,84 @@
+/*
+ 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"
+
+/* mapping of kcontrol text names to types */
+static const struct map_elem control_map[] = {
+ {"volsw", SND_SOC_TPLG_CTL_VOLSW},
+ {"volsw_sx", SND_SOC_TPLG_CTL_VOLSW_SX},
+ {"volsw_xr_sx", SND_SOC_TPLG_CTL_VOLSW_XR_SX},
+ {"enum", SND_SOC_TPLG_CTL_ENUM},
+ {"bytes", SND_SOC_TPLG_CTL_BYTES},
+ {"enum_value", SND_SOC_TPLG_CTL_ENUM_VALUE},
+ {"range", SND_SOC_TPLG_CTL_RANGE},
+ {"strobe", SND_SOC_TPLG_CTL_STROBE},
+};
+
+static int lookup_ops(const char *c)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(control_map); i++) {
+ if (strcmp(control_map[i].name, c) == 0)
+ return control_map[i].id;
+ }
+
+ /* cant find string name in our table so we use its ID number */
+ return atoi(c);
+}
+
+/* Parse Control operations. Ops can come from standard names above or
+ * bespoke driver controls with numbers >= 256
+ */
+int tplg_parse_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ snd_config_t *cfg, void *private)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ struct snd_soc_tplg_ctl_hdr *hdr = private;
+ const char *id, *value;
+
+ tplg_dbg("\tOps\n");
+ hdr->size = sizeof(*hdr);
+
+ snd_config_for_each(i, next, cfg) {
+
+ n = snd_config_iterator_entry(i);
+
+ /* get id */
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* get value - try strings then ints */
+ if (snd_config_get_string(n, &value) < 0)
+ continue;
+
+ if (strcmp(id, "info") == 0)
+ hdr->ops.info = lookup_ops(value);
+ else if (strcmp(id, "put") == 0)
+ hdr->ops.put = lookup_ops(value);
+ else if (strcmp(id, "get") == 0)
+ hdr->ops.get = lookup_ops(value);
+
+ tplg_dbg("\t\t%s = %s\n", id, value);
+ }
+
+ return 0;
+}
--
2.1.4
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 06/13] topology: Add private data parser
2015-07-29 16:45 [PATCH v3 00/13] alsa-lib topology support Liam Girdwood
` (4 preceding siblings ...)
2015-07-29 16:45 ` [PATCH v3 05/13] topology: Add operations parser Liam Girdwood
@ 2015-07-29 16:45 ` Liam Girdwood
2015-07-29 16:45 ` [PATCH v3 07/13] topology: Add DAPM object parser Liam Girdwood
` (7 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Liam Girdwood @ 2015-07-29 16:45 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, Liam Girdwood, Mark Brown
Parse private data and store for attachment to other objects. Data can come
file or be locally defined as bytes, shorts or words.
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
---
src/topology/data.c | 396 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 396 insertions(+)
create mode 100644 src/topology/data.c
diff --git a/src/topology/data.c b/src/topology/data.c
new file mode 100644
index 0000000..ae66472
--- /dev/null
+++ b/src/topology/data.c
@@ -0,0 +1,396 @@
+/*
+ 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"
+
+/* Get Private data from a file. */
+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 *env = getenv(ALSA_CONFIG_TPLG_VAR);
+ FILE *fp;
+ size_t size, bytes_read;
+ int ret = 0;
+
+ tplg_dbg("data DataFile: %s\n", elem->id);
+
+ if (snd_config_get_string(cfg, &value) < 0)
+ return -EINVAL;
+
+ /* prepend alsa config directory to path */
+ snprintf(filename, sizeof(filename), "%s/%s",
+ env ? env : ALSA_TPLG_DIR, value);
+
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+ SNDERR("error: invalid data file path '%s'\n",
+ filename);
+ ret = -errno;
+ goto err;
+ }
+
+ fseek(fp, 0L, SEEK_END);
+ size = ftell(fp);
+ fseek(fp, 0L, SEEK_SET);
+ if (size <= 0) {
+ SNDERR("error: invalid data file size %zu\n", size);
+ ret = -EINVAL;
+ goto err;
+ }
+ if (size > TPLG_MAX_PRIV_SIZE) {
+ SNDERR("error: data file too big %zu\n", size);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ priv = calloc(1, sizeof(*priv) + size);
+ if (!priv) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ bytes_read = fread(&priv->data, 1, size, fp);
+ if (bytes_read != size) {
+ ret = -errno;
+ goto err;
+ }
+
+ elem->data = priv;
+ priv->size = size;
+ elem->size = sizeof(*priv) + size;
+ return 0;
+
+err:
+ if (priv)
+ free(priv);
+ return ret;
+}
+
+static void dump_priv_data(struct tplg_elem *elem)
+{
+ struct snd_soc_tplg_private *priv = elem->data;
+ unsigned char *p = (unsigned char *)priv->data;
+ unsigned int i, j = 0;
+
+ 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)
+ tplg_dbg("\n");
+
+ tplg_dbg(" 0x%x", *p++);
+ }
+
+ tplg_dbg("\n\n");
+}
+
+/* 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;
+
+ /* we expect "0x0, 0x0, 0x0" */
+ while (str < end) {
+
+ /* skip white space */
+ if (isspace(*str)) {
+ str++;
+ continue;
+ }
+
+ /* find delimeters */
+ if (*str == ',') {
+ commas++;
+ str++;
+ continue;
+ }
+
+ /* find 0x[0-9] values */
+ if (*str == '0' && str + 2 <= end) {
+ if (str[1] == 'x' && str[2] >= '0' && str[2] <= 'f') {
+ values++;
+ str += 3;
+ } else {
+ str++;
+ }
+ }
+
+ str++;
+ }
+
+ /* there should always be one less comma than value */
+ if (values -1 != commas)
+ return -EINVAL;
+
+ return values;
+}
+
+static int write_hex(char *buf, char *str, int width)
+{
+ long val;
+ void *p = &val;
+
+ errno = 0;
+ val = strtol(str, NULL, 16);
+
+ if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
+ || (errno != 0 && val == 0)) {
+ return -EINVAL;
+ }
+
+ switch (width) {
+ case 1:
+ *(unsigned char *)buf = *(unsigned char *)p;
+ break;
+ case 2:
+ *(unsigned short *)buf = *(unsigned short *)p;
+ break;
+ case 4:
+ *(unsigned int *)buf = *(unsigned int *)p;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int copy_data_hex(char *data, int off, const char *str, int width)
+{
+ char *tmp, *s = NULL, *p = data;
+ int ret;
+
+ tmp = strdup(str);
+ if (tmp == NULL)
+ return -ENOMEM;
+
+ p += off;
+ s = strtok(tmp, ",");
+
+ while (s != NULL) {
+ ret = write_hex(p, s, width);
+ if (ret < 0) {
+ free(tmp);
+ return ret;
+ }
+
+ s = strtok(NULL, ",");
+ p += width;
+ }
+
+ free(tmp);
+ return 0;
+}
+
+static int tplg_parse_data_hex(snd_config_t *cfg, struct tplg_elem *elem,
+ int width)
+{
+ struct snd_soc_tplg_private *priv;
+ const char *value = NULL;
+ int size, esize, off, num;
+ int ret;
+
+ tplg_dbg(" data: %s\n", elem->id);
+
+ if (snd_config_get_string(cfg, &value) < 0)
+ return -EINVAL;
+
+ num = get_hex_num(value);
+ if (num <= 0) {
+ SNDERR("error: malformed hex variable list %s\n", value);
+ return -EINVAL;
+ }
+
+ size = num * width;
+ priv = elem->data;
+
+ if (esize > TPLG_MAX_PRIV_SIZE) {
+ SNDERR("error: data too big %d\n", esize);
+ return -EINVAL;
+ }
+
+ if (priv != NULL) {
+ off = priv->size;
+ esize = elem->size + size;
+ priv = realloc(priv, esize);
+ } else {
+ off = 0;
+ esize = sizeof(*priv) + size;
+ priv = calloc(1, esize);
+ }
+
+ if (!priv)
+ return -ENOMEM;
+
+ elem->data = priv;
+ priv->size += size;
+ elem->size = esize;
+
+ ret = copy_data_hex(priv->data, off, value, width);
+
+ dump_priv_data(elem);
+ return ret;
+}
+
+
+/* Parse Private data.
+ *
+ * Object private data can either be from file or defined as bytes, shorts,
+ * words.
+ */
+int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg,
+ void *private ATTRIBUTE_UNUSED)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id, *val = NULL;
+ int err = 0;
+ struct tplg_elem *elem;
+
+ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_DATA);
+ if (!elem)
+ return -ENOMEM;
+
+ 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, "file") == 0) {
+ err = tplg_parse_data_file(n, elem);
+ if (err < 0) {
+ SNDERR("error: failed to parse data file\n");
+ return err;
+ }
+ continue;
+ }
+
+ if (strcmp(id, "bytes") == 0) {
+ err = tplg_parse_data_hex(n, elem, 1);
+ if (err < 0) {
+ SNDERR("error: failed to parse data bytes\n");
+ return err;
+ }
+ continue;
+ }
+
+ if (strcmp(id, "shorts") == 0) {
+ err = tplg_parse_data_hex(n, elem, 2);
+ if (err < 0) {
+ SNDERR("error: failed to parse data shorts\n");
+ return err;
+ }
+ continue;
+ }
+
+ if (strcmp(id, "words") == 0) {
+ err = tplg_parse_data_hex(n, elem, 4);
+ if (err < 0) {
+ SNDERR("error: failed to parse data words\n");
+ return err;
+ }
+ continue;
+ }
+
+ if (strcmp(id, "index") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ elem->index = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, elem->index);
+ continue;
+ }
+
+ if (strcmp(id, "type") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ elem->vendor_type = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, elem->index);
+ continue;
+ }
+ }
+
+ return err;
+}
+
+/* copy private data into the bytes extended control */
+int tplg_copy_data(struct tplg_elem *elem, struct tplg_elem *ref)
+{
+ struct snd_soc_tplg_private *priv;
+ int priv_data_size;
+
+ if (!ref)
+ return -EINVAL;
+
+ tplg_dbg("Data '%s' used by '%s'\n", ref->id, elem->id);
+ priv_data_size = ref->data->size;
+
+ switch (elem->type) {
+ case OBJECT_TYPE_MIXER:
+ elem->mixer_ctrl = realloc(elem->mixer_ctrl,
+ elem->size + priv_data_size);
+ if (!elem->mixer_ctrl)
+ return -ENOMEM;
+ priv = &elem->mixer_ctrl->priv;
+ break;
+
+ case OBJECT_TYPE_ENUM:
+ elem->enum_ctrl = realloc(elem->enum_ctrl,
+ elem->size + priv_data_size);
+ if (!elem->enum_ctrl)
+ return -ENOMEM;
+ priv = &elem->enum_ctrl->priv;
+ break;
+
+ case OBJECT_TYPE_BYTES:
+ elem->bytes_ext = realloc(elem->bytes_ext,
+ elem->size + priv_data_size);
+ if (!elem->bytes_ext)
+ return -ENOMEM;
+ priv = &elem->bytes_ext->priv;
+ break;
+
+
+ case OBJECT_TYPE_DAPM_WIDGET:
+ elem->widget = realloc(elem->widget,
+ elem->size + priv_data_size);
+ if (!elem->widget)
+ return -ENOMEM;
+ priv = &elem->widget->priv;
+ break;
+
+ default:
+ SNDERR("elem '%s': type %d private data not supported \n",
+ elem->id, elem->type);
+ return -EINVAL;
+ }
+
+ elem->size += priv_data_size;
+ priv->size = priv_data_size;
+ ref->compound_elem = 1;
+ memcpy(priv->data, ref->data->data, priv_data_size);
+ return 0;
+}
--
2.1.4
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 07/13] topology: Add DAPM object parser
2015-07-29 16:45 [PATCH v3 00/13] alsa-lib topology support Liam Girdwood
` (5 preceding siblings ...)
2015-07-29 16:45 ` [PATCH v3 06/13] topology: Add private data parser Liam Girdwood
@ 2015-07-29 16:45 ` Liam Girdwood
2015-07-29 16:45 ` [PATCH v3 08/13] topology: Add CTL parser Liam Girdwood
` (6 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Liam Girdwood @ 2015-07-29 16:45 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, Liam Girdwood, Mark Brown
Parse DAPM objects including widgets and graph elements.
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
---
src/topology/dapm.c | 562 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 562 insertions(+)
create mode 100644 src/topology/dapm.c
diff --git a/src/topology/dapm.c b/src/topology/dapm.c
new file mode 100644
index 0000000..1da82ad
--- /dev/null
+++ b/src/topology/dapm.c
@@ -0,0 +1,562 @@
+/*
+ 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"
+
+/* mapping of widget text names to types */
+static const struct map_elem widget_map[] = {
+ {"input", SND_SOC_TPLG_DAPM_INPUT},
+ {"output", SND_SOC_TPLG_DAPM_OUTPUT},
+ {"mux", SND_SOC_TPLG_DAPM_MUX},
+ {"mixer", SND_SOC_TPLG_DAPM_MIXER},
+ {"pga", SND_SOC_TPLG_DAPM_PGA},
+ {"out_drv", SND_SOC_TPLG_DAPM_OUT_DRV},
+ {"adc", SND_SOC_TPLG_DAPM_ADC},
+ {"dac", SND_SOC_TPLG_DAPM_DAC},
+ {"switch", SND_SOC_TPLG_DAPM_SWITCH},
+ {"pre", SND_SOC_TPLG_DAPM_PRE},
+ {"post", SND_SOC_TPLG_DAPM_POST},
+ {"aif_in", SND_SOC_TPLG_DAPM_AIF_IN},
+ {"aif_out", SND_SOC_TPLG_DAPM_AIF_OUT},
+ {"dai_in", SND_SOC_TPLG_DAPM_DAI_IN},
+ {"dai_out", SND_SOC_TPLG_DAPM_DAI_OUT},
+ {"dai_link", SND_SOC_TPLG_DAPM_DAI_LINK},
+};
+
+/* mapping of widget kcontrol text names to types */
+static const struct map_elem widget_control_map[] = {
+ {"volsw", SND_SOC_TPLG_DAPM_CTL_VOLSW},
+ {"enum_double", SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE},
+ {"enum_virt", SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT},
+ {"enum_value", SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE},
+};
+
+static int lookup_widget(const char *w)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(widget_map); i++) {
+ if (strcmp(widget_map[i].name, w) == 0)
+ return widget_map[i].id;
+ }
+
+ return -EINVAL;
+}
+
+static int tplg_parse_dapm_mixers(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 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, OBJECT_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, OBJECT_TYPE_ENUM, value);
+ tplg_dbg("\t\t %s\n", value);
+ }
+
+ return 0;
+}
+
+/* move referenced controls to the widget */
+static int copy_dapm_control(struct tplg_elem *elem, struct tplg_elem *ref)
+{
+ struct snd_soc_tplg_dapm_widget *widget = elem->widget;
+ struct snd_soc_tplg_mixer_control *mixer_ctrl = ref->mixer_ctrl;
+ struct snd_soc_tplg_enum_control *enum_ctrl = ref->enum_ctrl;
+
+ tplg_dbg("Control '%s' used by '%s'\n", ref->id, elem->id);
+ tplg_dbg("\tparent size: %d + %d -> %d, priv size -> %d\n",
+ elem->size, ref->size, elem->size + ref->size,
+ widget->priv.size);
+
+ widget = realloc(widget, elem->size + ref->size);
+ if (!widget)
+ return -ENOMEM;
+
+ elem->widget = widget;
+
+ /* copy new widget at the end */
+ if (ref->type == OBJECT_TYPE_MIXER)
+ memcpy((void*)widget + elem->size, mixer_ctrl, ref->size);
+ else if (ref->type == OBJECT_TYPE_ENUM)
+ memcpy((void*)widget + elem->size, enum_ctrl, ref->size);
+
+ elem->size += ref->size;
+ widget->num_kcontrols++;
+ ref->compound_elem = 1;
+ return 0;
+}
+
+/* check referenced controls for a widget */
+static int tplg_build_widget(snd_tplg_t *tplg,
+ struct tplg_elem *elem)
+{
+ struct tplg_ref *ref;
+ struct list_head *base, *pos;
+ int err = 0;
+
+ base = &elem->ref_list;
+
+ /* for each ref in this control elem */
+ list_for_each(pos, base) {
+
+ ref = list_entry(pos, struct tplg_ref, list);
+ if (ref->id == NULL || ref->elem)
+ continue;
+
+ switch (ref->type) {
+ case OBJECT_TYPE_MIXER:
+ ref->elem = tplg_elem_lookup(&tplg->mixer_list,
+ ref->id, OBJECT_TYPE_MIXER);
+ if (ref->elem)
+ err = copy_dapm_control(elem, ref->elem);
+ break;
+
+ case OBJECT_TYPE_ENUM:
+ ref->elem = tplg_elem_lookup(&tplg->enum_list,
+ ref->id, OBJECT_TYPE_ENUM);
+ if (ref->elem)
+ err = copy_dapm_control(elem, ref->elem);
+ break;
+
+ case OBJECT_TYPE_DATA:
+ ref->elem = tplg_elem_lookup(&tplg->pdata_list,
+ ref->id, OBJECT_TYPE_DATA);
+ if (ref->elem)
+ err = tplg_copy_data(elem, ref->elem);
+ break;
+ default:
+ break;
+ }
+
+ if (!ref->elem) {
+ SNDERR("error: cannot find control '%s'"
+ " referenced by widget '%s'\n",
+ ref->id, elem->id);
+ return -EINVAL;
+ }
+
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+int tplg_build_widgets(snd_tplg_t *tplg)
+{
+
+ struct list_head *base, *pos;
+ struct tplg_elem *elem;
+ int err;
+
+ base = &tplg->widget_list;
+ list_for_each(pos, base) {
+
+ elem = list_entry(pos, struct tplg_elem, list);
+ if (!elem->widget || elem->type != OBJECT_TYPE_DAPM_WIDGET) {
+ SNDERR("error: invalid widget '%s'\n",
+ elem->id);
+ return -EINVAL;
+ }
+
+ err = tplg_build_widget(tplg, elem);
+ if (err < 0)
+ return err;
+
+ /* add widget to manifest */
+ tplg->manifest.widget_elems++;
+ }
+
+ return 0;
+}
+
+int tplg_build_routes(snd_tplg_t *tplg)
+{
+ struct list_head *base, *pos;
+ struct tplg_elem *elem;
+ struct snd_soc_tplg_dapm_graph_elem *route;
+
+ base = &tplg->route_list;
+
+ list_for_each(pos, base) {
+ elem = list_entry(pos, struct tplg_elem, list);
+
+ if (!elem->route || elem->type != OBJECT_TYPE_DAPM_GRAPH) {
+ SNDERR("error: invalid route '%s'\n",
+ elem->id);
+ return -EINVAL;
+ }
+
+ route = elem->route;
+ tplg_dbg("\nCheck route: sink '%s', control '%s', source '%s'\n",
+ route->sink, route->control, route->source);
+
+ /* validate sink */
+ if (strlen(route->sink) <= 0) {
+ SNDERR("error: no sink\n");
+ return -EINVAL;
+
+ }
+ if (!tplg_elem_lookup(&tplg->widget_list, route->sink,
+ OBJECT_TYPE_DAPM_WIDGET)) {
+ SNDERR("warning: undefined sink widget/stream '%s'\n",
+ route->sink);
+ }
+
+ /* validate control name */
+ if (strlen(route->control)) {
+ if (!tplg_elem_lookup(&tplg->mixer_list,
+ route->control, OBJECT_TYPE_MIXER) &&
+ !tplg_elem_lookup(&tplg->enum_list,
+ route->control, OBJECT_TYPE_ENUM)) {
+ SNDERR("warning: Undefined mixer/enum control '%s'\n",
+ route->control);
+ }
+ }
+
+ /* validate source */
+ if (strlen(route->source) <= 0) {
+ SNDERR("error: no source\n");
+ return -EINVAL;
+
+ }
+ if (!tplg_elem_lookup(&tplg->widget_list, route->source,
+ OBJECT_TYPE_DAPM_WIDGET)) {
+ SNDERR("warning: Undefined source widget/stream '%s'\n",
+ route->source);
+ }
+
+ /* add graph to manifest */
+ tplg->manifest.graph_elems++;
+ }
+
+ return 0;
+}
+
+#define LINE_SIZE 1024
+
+/* line is defined as '"source, control, sink"' */
+static int tplg_parse_line(const char *text,
+ struct snd_soc_tplg_dapm_graph_elem *line)
+{
+ char buf[LINE_SIZE];
+ unsigned int len, i;
+ const char *source = NULL, *sink = NULL, *control = NULL;
+
+ elem_copy_text(buf, text, LINE_SIZE);
+
+ len = strlen(buf);
+ if (len <= 2) {
+ SNDERR("error: invalid route \"%s\"\n", buf);
+ return -EINVAL;
+ }
+
+ /* find first , */
+ for (i = 1; i < len; i++) {
+ if (buf[i] == ',')
+ goto second;
+ }
+ SNDERR("error: invalid route \"%s\"\n", buf);
+ return -EINVAL;
+
+second:
+ /* find second , */
+ sink = buf;
+ control = &buf[i + 2];
+ buf[i] = 0;
+
+ for (; i < len; i++) {
+ if (buf[i] == ',')
+ goto done;
+ }
+
+ SNDERR("error: invalid route \"%s\"\n", buf);
+ return -EINVAL;
+
+done:
+ buf[i] = 0;
+ source = &buf[i + 2];
+
+ strcpy(line->source, source);
+ strcpy(line->control, control);
+ strcpy(line->sink, sink);
+ return 0;
+}
+
+
+static int tplg_parse_routes(snd_tplg_t *tplg, snd_config_t *cfg)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ struct tplg_elem *elem;
+ struct snd_soc_tplg_dapm_graph_elem *line = NULL;
+ int err;
+
+ 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;
+
+ elem = tplg_elem_new();
+ if (!elem)
+ return -ENOMEM;
+
+ list_add_tail(&elem->list, &tplg->route_list);
+ strcpy(elem->id, "line");
+ elem->type = OBJECT_TYPE_DAPM_GRAPH;
+ elem->size = sizeof(*line);
+
+ line = calloc(1, sizeof(*line));
+ if (!line)
+ return -ENOMEM;
+
+ elem->route = line;
+
+ err = tplg_parse_line(val, line);
+ if (err < 0)
+ return err;
+
+ tplg_dbg("route: sink '%s', control '%s', source '%s'\n",
+ line->sink, line->control, line->source);
+ }
+
+ return 0;
+}
+
+int tplg_parse_dapm_graph(snd_tplg_t *tplg, snd_config_t *cfg,
+ void *private ATTRIBUTE_UNUSED)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ int err;
+ const char *graph_id;
+
+ if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
+ SNDERR("error: compound is expected for dapm graph definition\n");
+ return -EINVAL;
+ }
+
+ snd_config_get_id(cfg, &graph_id);
+
+ snd_config_for_each(i, next, cfg) {
+ const char *id;
+
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0) {
+ continue;
+ }
+
+ if (strcmp(id, "lines") == 0) {
+ err = tplg_parse_routes(tplg, n);
+ if (err < 0) {
+ SNDERR("error: failed to parse dapm graph %s\n",
+ graph_id);
+ return err;
+ }
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/* DAPM Widget */
+int tplg_parse_dapm_widget(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
+{
+ struct snd_soc_tplg_dapm_widget *widget;
+ struct tplg_elem *elem;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id, *val = NULL;
+ int widget_type, err;
+
+ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_DAPM_WIDGET);
+ if (!elem)
+ return -ENOMEM;
+
+ tplg_dbg(" Widget: %s\n", elem->id);
+
+ widget = elem->widget;
+ elem_copy_text(widget->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+ widget->size = elem->size;
+
+ snd_config_for_each(i, next, cfg) {
+
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* skip comments */
+ if (strcmp(id, "comment") == 0)
+ continue;
+ if (id[0] == '#')
+ continue;
+
+ if (strcmp(id, "index") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ elem->index = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, elem->index);
+ continue;
+ }
+
+ if (strcmp(id, "type") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ widget_type = lookup_widget(val);
+ if (widget_type < 0){
+ SNDERR("Widget '%s': Unsupported widget type %s\n",
+ elem->id, val);
+ return -EINVAL;
+ }
+
+ widget->id = widget_type;
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+ }
+
+ if (strcmp(id, "no_pm") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ if (strcmp(val, "true") == 0)
+ widget->reg = -1;
+
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+ }
+
+ if (strcmp(id, "shift") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ widget->shift = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, widget->shift);
+ continue;
+ }
+
+ if (strcmp(id, "reg") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ widget->reg = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, widget->reg);
+ continue;
+ }
+
+ if (strcmp(id, "invert") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ widget->invert = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, widget->invert);
+ continue;
+ }
+
+ if (strcmp(id, "subseq") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ widget->subseq= atoi(val);
+ tplg_dbg("\t%s: %d\n", id, widget->subseq);
+ continue;
+ }
+
+ if (strcmp(id, "event_type") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ widget->event_type = atoi(val);
+ 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)
+ return -EINVAL;
+
+ widget->event_flags = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, widget->event_flags);
+ continue;
+ }
+
+ if (strcmp(id, "enum") == 0) {
+ err = tplg_parse_dapm_enums(n, elem);
+ if (err < 0)
+ return err;
+
+ continue;
+ }
+
+ if (strcmp(id, "mixer") == 0) {
+ err = tplg_parse_dapm_mixers(n, elem);
+ if (err < 0)
+ return err;
+
+ continue;
+ }
+
+ if (strcmp(id, "data") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ tplg_ref_add(elem, OBJECT_TYPE_DATA, val);
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+ }
+ }
+
+ return 0;
+}
--
2.1.4
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 08/13] topology: Add CTL parser
2015-07-29 16:45 [PATCH v3 00/13] alsa-lib topology support Liam Girdwood
` (6 preceding siblings ...)
2015-07-29 16:45 ` [PATCH v3 07/13] topology: Add DAPM object parser Liam Girdwood
@ 2015-07-29 16:45 ` Liam Girdwood
2015-07-29 16:45 ` [PATCH v3 09/13] topology: Add Channel map parser Liam Girdwood
` (5 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Liam Girdwood @ 2015-07-29 16:45 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, Liam Girdwood, Mark Brown
Add support to parse mixers, enums and byte controls.
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
---
src/topology/ctl.c | 613 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 613 insertions(+)
create mode 100644 src/topology/ctl.c
diff --git a/src/topology/ctl.c b/src/topology/ctl.c
new file mode 100644
index 0000000..9c1333c
--- /dev/null
+++ b/src/topology/ctl.c
@@ -0,0 +1,613 @@
+/*
+ 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"
+
+/* copy referenced TLV to the mixer control */
+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);
+
+ /* TLV has a fixed size */
+ mixer_ctrl->tlv = *tlv;
+
+ /* set size of TLV data */
+ mixer_ctrl->hdr.tlv_size = tlv->count * sizeof(uint32_t);
+ return 0;
+}
+
+/* check referenced TLV for a mixer control */
+static int tplg_build_mixer_control(snd_tplg_t *tplg,
+ struct tplg_elem *elem)
+{
+ struct tplg_ref *ref;
+ struct list_head *base, *pos;
+ int err = 0;
+
+ base = &elem->ref_list;
+
+ /* for each ref in this control elem */
+ list_for_each(pos, base) {
+
+ ref = list_entry(pos, struct tplg_ref, list);
+ if (ref->id == NULL || ref->elem)
+ continue;
+
+ if (ref->type == OBJECT_TYPE_TLV) {
+ ref->elem = tplg_elem_lookup(&tplg->tlv_list,
+ ref->id, OBJECT_TYPE_TLV);
+ if (ref->elem)
+ err = copy_tlv(elem, ref->elem);
+
+ } else if (ref->type == OBJECT_TYPE_DATA) {
+ ref->elem = tplg_elem_lookup(&tplg->pdata_list,
+ ref->id, OBJECT_TYPE_DATA);
+ err = tplg_copy_data(elem, ref->elem);
+ }
+
+ if (!ref->elem) {
+ SNDERR("error: cannot find '%s' referenced by"
+ " control '%s'\n", ref->id, elem->id);
+ return -EINVAL;
+ } else if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static void copy_enum_texts(struct tplg_elem *enum_elem,
+ struct tplg_elem *ref_elem)
+{
+ struct snd_soc_tplg_enum_control *ec = enum_elem->enum_ctrl;
+
+ memcpy(ec->texts, ref_elem->texts,
+ SND_SOC_TPLG_NUM_TEXTS * SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+}
+
+/* check referenced text for a enum control */
+static int tplg_build_enum_control(snd_tplg_t *tplg,
+ struct tplg_elem *elem)
+{
+ struct tplg_ref *ref;
+ struct list_head *base, *pos;
+ int err = 0;
+
+ base = &elem->ref_list;
+
+ list_for_each(pos, base) {
+
+ ref = list_entry(pos, struct tplg_ref, list);
+ if (ref->id == NULL || ref->elem)
+ continue;
+
+ if (ref->type == OBJECT_TYPE_TEXT) {
+ ref->elem = tplg_elem_lookup(&tplg->text_list,
+ ref->id, OBJECT_TYPE_TEXT);
+ if (ref->elem)
+ copy_enum_texts(elem, ref->elem);
+
+ } else if (ref->type == OBJECT_TYPE_DATA) {
+ ref->elem = tplg_elem_lookup(&tplg->pdata_list,
+ ref->id, OBJECT_TYPE_DATA);
+ err = tplg_copy_data(elem, ref->elem);
+ }
+ if (!ref->elem) {
+ SNDERR("error: cannot find '%s' referenced by"
+ " control '%s'\n", ref->id, elem->id);
+ return -EINVAL;
+ } else if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/* check referenced private data for a byte control */
+static int tplg_build_bytes_control(snd_tplg_t *tplg, struct tplg_elem *elem)
+{
+ struct tplg_ref *ref;
+ struct list_head *base, *pos;
+
+ base = &elem->ref_list;
+
+ list_for_each(pos, base) {
+
+ ref = list_entry(pos, struct tplg_ref, list);
+ if (ref->id == NULL || ref->elem)
+ continue;
+
+ /* bytes control only reference one private data section */
+ ref->elem = tplg_elem_lookup(&tplg->pdata_list,
+ ref->id, OBJECT_TYPE_DATA);
+ if (!ref->elem) {
+ SNDERR("error: cannot find data '%s'"
+ " referenced by control '%s'\n",
+ ref->id, elem->id);
+ return -EINVAL;
+ }
+
+ /* copy texts to enum elem */
+ return tplg_copy_data(elem, ref->elem);
+ }
+
+ return 0;
+}
+
+int tplg_build_controls(snd_tplg_t *tplg)
+{
+ struct list_head *base, *pos;
+ struct tplg_elem *elem;
+ int err = 0;
+
+ base = &tplg->mixer_list;
+ list_for_each(pos, base) {
+
+ elem = list_entry(pos, struct tplg_elem, list);
+ err = tplg_build_mixer_control(tplg, elem);
+ if (err < 0)
+ return err;
+
+ /* add control to manifest */
+ tplg->manifest.control_elems++;
+ }
+
+ base = &tplg->enum_list;
+ list_for_each(pos, base) {
+
+ elem = list_entry(pos, struct tplg_elem, list);
+ err = tplg_build_enum_control(tplg, elem);
+ if (err < 0)
+ return err;
+
+ /* add control to manifest */
+ tplg->manifest.control_elems++;
+ }
+
+ base = &tplg->bytes_ext_list;
+ list_for_each(pos, base) {
+
+ elem = list_entry(pos, struct tplg_elem, list);
+ err = tplg_build_bytes_control(tplg, elem);
+ if (err < 0)
+ return err;
+
+ /* add control to manifest */
+ tplg->manifest.control_elems++;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Parse TLV of DBScale type.
+ *
+ * Parse DBScale describing min, step, mute in DB.
+ */
+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;
+ const char *id = NULL, *value = NULL;
+ int *data;
+
+ tplg_dbg(" scale: %s\n", elem->id);
+
+ tplg_tlv = calloc(1, sizeof(*tplg_tlv));
+ if (!tplg_tlv)
+ return -ENOMEM;
+ data = (int*)(tplg_tlv->data);
+
+ elem->tlv = tplg_tlv;
+ tplg_tlv->numid = SNDRV_CTL_TLVT_DB_SCALE;
+ tplg_tlv->count = 8;
+ tplg_tlv->size = sizeof(*tplg_tlv);
+
+ snd_config_for_each(i, next, cfg) {
+
+ n = snd_config_iterator_entry(i);
+
+ /* get ID */
+ if (snd_config_get_id(n, &id) < 0) {
+ SNDERR("error: cant get ID\n");
+ return -EINVAL;
+ }
+
+ /* get value */
+ if (snd_config_get_string(n, &value) < 0)
+ continue;
+
+ tplg_dbg("\t%s = %s\n", id, value);
+
+ /* get TLV data */
+ if (strcmp(id, "min") == 0)
+ data[0] = atoi(value);
+ else if (strcmp(id, "step") == 0)
+ data[1] = atoi(value);
+ else if (strcmp(id, "mute") == 0)
+ data[2] = atoi(value);
+ else
+ SNDERR("error: unknown key %s\n", id);
+ }
+
+ return 0;
+}
+
+/* Parse TLV */
+int tplg_parse_tlv(snd_tplg_t *tplg, snd_config_t *cfg,
+ void *private ATTRIBUTE_UNUSED)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id;
+ int err = 0;
+ struct tplg_elem *elem;
+
+ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_TLV);
+ if (!elem)
+ return -ENOMEM;
+
+ 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, "scale") == 0) {
+ err = tplg_parse_tlv_dbscale(n, elem);
+ if (err < 0) {
+ SNDERR("error: failed to DBScale");
+ return err;
+ }
+ continue;
+ }
+ }
+
+ return err;
+}
+
+/* Parse Control Bytes */
+int tplg_parse_control_bytes(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
+{
+ struct snd_soc_tplg_bytes_control *be;
+ struct tplg_elem *elem;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id, *val = NULL;
+ int err;
+
+ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_BYTES);
+ if (!elem)
+ return -ENOMEM;
+
+ be = elem->bytes_ext;
+ be->size = elem->size;
+ elem_copy_text(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);
+
+ snd_config_for_each(i, next, cfg) {
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* skip comments */
+ if (strcmp(id, "comment") == 0)
+ continue;
+ if (id[0] == '#')
+ continue;
+
+ if (strcmp(id, "index") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ elem->index = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, elem->index);
+ continue;
+ }
+
+ if (strcmp(id, "base") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ be->base = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, be->base);
+ continue;
+ }
+
+ if (strcmp(id, "num_regs") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ be->num_regs = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, be->num_regs);
+ continue;
+ }
+
+ if (strcmp(id, "max") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ be->max = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, be->num_regs);
+ continue;
+ }
+
+ if (strcmp(id, "mask") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ be->mask = strtol(val, NULL, 16);
+ tplg_dbg("\t%s: %d\n", id, be->mask);
+ continue;
+ }
+
+ if (strcmp(id, "data") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ tplg_ref_add(elem, OBJECT_TYPE_DATA, val);
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+ }
+
+ if (strcmp(id, "tlv") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ err = tplg_ref_add(elem, OBJECT_TYPE_TLV, val);
+ if (err < 0)
+ return err;
+
+ be->hdr.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/* Parse Control Enums. */
+int tplg_parse_control_enum(snd_tplg_t *tplg, snd_config_t *cfg,
+ void *private ATTRIBUTE_UNUSED)
+{
+ struct snd_soc_tplg_enum_control *ec;
+ struct tplg_elem *elem;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id, *val = NULL;
+ int err, j;
+
+ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_ENUM);
+ if (!elem)
+ return -ENOMEM;
+
+ /* init new mixer */
+ ec = elem->enum_ctrl;
+ elem_copy_text(ec->hdr.name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+ ec->hdr.type = SND_SOC_TPLG_TYPE_ENUM;
+ ec->size = elem->size;
+ tplg->channel_idx = 0;
+
+ /* set channel reg to default state */
+ for (j = 0; j < SND_SOC_TPLG_MAX_CHAN; j++)
+ ec->channel[j].reg = -1;
+
+ tplg_dbg(" Control Enum: %s\n", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* skip comments */
+ if (strcmp(id, "comment") == 0)
+ continue;
+ if (id[0] == '#')
+ continue;
+
+ if (strcmp(id, "index") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ elem->index = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, elem->index);
+ continue;
+ }
+
+ if (strcmp(id, "texts") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ tplg_ref_add(elem, OBJECT_TYPE_TEXT, val);
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+ }
+
+ if (strcmp(id, "channel") == 0) {
+ if (ec->num_channels >= SND_SOC_TPLG_MAX_CHAN) {
+ SNDERR("error: too many channels %s\n",
+ elem->id);
+ return -EINVAL;
+ }
+
+ err = tplg_parse_compound(tplg, n, tplg_parse_channel,
+ ec->channel);
+ if (err < 0)
+ return err;
+
+ ec->num_channels = tplg->channel_idx;
+ continue;
+ }
+
+ if (strcmp(id, "ops") == 0) {
+ err = tplg_parse_compound(tplg, n, tplg_parse_ops,
+ &ec->hdr);
+ if (err < 0)
+ return err;
+ continue;
+ }
+
+ if (strcmp(id, "data") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ tplg_ref_add(elem, OBJECT_TYPE_DATA, val);
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/* Parse Controls.
+ *
+ * Mixer control. Supports multiple channels.
+ */
+int tplg_parse_control_mixer(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
+{
+ struct snd_soc_tplg_mixer_control *mc;
+ struct tplg_elem *elem;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id, *val = NULL;
+ int err, j;
+
+ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_MIXER);
+ if (!elem)
+ return -ENOMEM;
+
+ /* init new mixer */
+ mc = elem->mixer_ctrl;
+ elem_copy_text(mc->hdr.name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+ mc->hdr.type = SND_SOC_TPLG_TYPE_MIXER;
+ mc->size = elem->size;
+ tplg->channel_idx = 0;
+
+ /* set channel reg to default state */
+ for (j = 0; j < SND_SOC_TPLG_MAX_CHAN; j++)
+ mc->channel[j].reg = -1;
+
+ tplg_dbg(" Control Mixer: %s\n", elem->id);
+
+ /* giterate trough each mixer elment */
+ snd_config_for_each(i, next, cfg) {
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* skip comments */
+ if (strcmp(id, "comment") == 0)
+ continue;
+ if (id[0] == '#')
+ continue;
+
+ if (strcmp(id, "index") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ elem->index = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, elem->index);
+ continue;
+ }
+
+ if (strcmp(id, "channel") == 0) {
+ if (mc->num_channels >= SND_SOC_TPLG_MAX_CHAN) {
+ SNDERR("error: too many channels %s\n",
+ elem->id);
+ return -EINVAL;
+ }
+
+ err = tplg_parse_compound(tplg, n, tplg_parse_channel,
+ mc->channel);
+ if (err < 0)
+ return err;
+
+ mc->num_channels = tplg->channel_idx;
+ continue;
+ }
+
+ if (strcmp(id, "max") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ mc->max = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, mc->max);
+ continue;
+ }
+
+ if (strcmp(id, "invert") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ if (strcmp(val, "true") == 0)
+ mc->invert = 1;
+ else if (strcmp(val, "false") == 0)
+ mc->invert = 0;
+
+ tplg_dbg("\t%s: %d\n", id, mc->invert);
+ continue;
+ }
+
+ if (strcmp(id, "ops") == 0) {
+ err = tplg_parse_compound(tplg, n, tplg_parse_ops,
+ &mc->hdr);
+ if (err < 0)
+ return err;
+ continue;
+ }
+
+ if (strcmp(id, "tlv") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ err = tplg_ref_add(elem, OBJECT_TYPE_TLV, val);
+ if (err < 0)
+ return err;
+
+ mc->hdr.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+ }
+
+ if (strcmp(id, "data") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ tplg_ref_add(elem, OBJECT_TYPE_DATA, val);
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+ }
+ }
+
+ return 0;
+}
--
2.1.4
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 09/13] topology: Add Channel map parser.
2015-07-29 16:45 [PATCH v3 00/13] alsa-lib topology support Liam Girdwood
` (7 preceding siblings ...)
2015-07-29 16:45 ` [PATCH v3 08/13] topology: Add CTL parser Liam Girdwood
@ 2015-07-29 16:45 ` Liam Girdwood
2015-07-29 16:45 ` [PATCH v3 10/13] topology: Add binary file builder Liam Girdwood
` (4 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Liam Girdwood @ 2015-07-29 16:45 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, Liam Girdwood, Mark Brown
Add support for parsing channel map to control registers.
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
---
src/topology/channel.c | 122 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 122 insertions(+)
create mode 100644 src/topology/channel.c
diff --git a/src/topology/channel.c b/src/topology/channel.c
new file mode 100644
index 0000000..9bc5d5a
--- /dev/null
+++ b/src/topology/channel.c
@@ -0,0 +1,122 @@
+/*
+ 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"
+
+/* mapping of channel text names to types */
+static const struct map_elem channel_map[] = {
+ {"mono", SNDRV_CHMAP_MONO}, /* mono stream */
+ {"fl", SNDRV_CHMAP_FL}, /* front left */
+ {"fr", SNDRV_CHMAP_FR}, /* front right */
+ {"rl", SNDRV_CHMAP_RL}, /* rear left */
+ {"rr", SNDRV_CHMAP_RR}, /* rear right */
+ {"fc", SNDRV_CHMAP_FC}, /* front center */
+ {"lfe", SNDRV_CHMAP_LFE}, /* LFE */
+ {"sl", SNDRV_CHMAP_SL}, /* side left */
+ {"sr", SNDRV_CHMAP_SR}, /* side right */
+ {"rc", SNDRV_CHMAP_RC}, /* rear center */
+ {"flc", SNDRV_CHMAP_FLC}, /* front left center */
+ {"frc", SNDRV_CHMAP_FRC}, /* front right center */
+ {"rlc", SNDRV_CHMAP_RLC}, /* rear left center */
+ {"rrc", SNDRV_CHMAP_RRC}, /* rear right center */
+ {"flw", SNDRV_CHMAP_FLW}, /* front left wide */
+ {"frw", SNDRV_CHMAP_FRW}, /* front right wide */
+ {"flh", SNDRV_CHMAP_FLH}, /* front left high */
+ {"fch", SNDRV_CHMAP_FCH}, /* front center high */
+ {"frh", SNDRV_CHMAP_FRH}, /* front right high */
+ {"tc", SNDRV_CHMAP_TC}, /* top center */
+ {"tfl", SNDRV_CHMAP_TFL}, /* top front left */
+ {"tfr", SNDRV_CHMAP_TFR}, /* top front right */
+ {"tfc", SNDRV_CHMAP_TFC}, /* top front center */
+ {"trl", SNDRV_CHMAP_TRL}, /* top rear left */
+ {"trr", SNDRV_CHMAP_TRR}, /* top rear right */
+ {"trc", SNDRV_CHMAP_TRC}, /* top rear center */
+ {"tflc", SNDRV_CHMAP_TFLC}, /* top front left center */
+ {"tfrc", SNDRV_CHMAP_TFRC}, /* top front right center */
+ {"tsl", SNDRV_CHMAP_TSL}, /* top side left */
+ {"tsr", SNDRV_CHMAP_TSR}, /* top side right */
+ {"llfe", SNDRV_CHMAP_LLFE}, /* left LFE */
+ {"rlfe", SNDRV_CHMAP_RLFE}, /* right LFE */
+ {"bc", SNDRV_CHMAP_BC}, /* bottom center */
+ {"blc", SNDRV_CHMAP_BLC}, /* bottom left center */
+ {"brc", SNDRV_CHMAP_BRC}, /* bottom right center */
+};
+
+
+static int lookup_channel(const char *c)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(channel_map); i++) {
+ if (strcasecmp(channel_map[i].name, c) == 0) {
+ return channel_map[i].id;
+ }
+ }
+
+ return -EINVAL;
+}
+
+/* Parse a channel mapping. */
+int tplg_parse_channel(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ struct snd_soc_tplg_channel *channel = private;
+ const char *id, *value;
+
+ if (tplg->channel_idx >= SND_SOC_TPLG_MAX_CHAN)
+ return -EINVAL;
+
+ channel += tplg->channel_idx;
+ snd_config_get_id(cfg, &id);
+ tplg_dbg("\tChannel %s at index %d\n", id, tplg->channel_idx);
+
+ channel->id = lookup_channel(id);
+ if (channel->id < 0) {
+ SNDERR("error: invalid channel %s\n", id);
+ return -EINVAL;
+ }
+
+ channel->size = sizeof(*channel);
+ tplg_dbg("\tChan %s = %d\n", id, channel->id);
+
+ snd_config_for_each(i, next, cfg) {
+
+ n = snd_config_iterator_entry(i);
+
+ /* get id */
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* get value */
+ if (snd_config_get_string(n, &value) < 0)
+ continue;
+
+ if (strcmp(id, "reg") == 0)
+ channel->reg = atoi(value);
+ else if (strcmp(id, "shift") == 0)
+ channel->shift = atoi(value);
+
+ tplg_dbg("\t\t%s = %s\n", id, value);
+ }
+
+ tplg->channel_idx++;
+ return 0;
+}
--
2.1.4
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 10/13] topology: Add binary file builder.
2015-07-29 16:45 [PATCH v3 00/13] alsa-lib topology support Liam Girdwood
` (8 preceding siblings ...)
2015-07-29 16:45 ` [PATCH v3 09/13] topology: Add Channel map parser Liam Girdwood
@ 2015-07-29 16:45 ` Liam Girdwood
2015-07-29 16:45 ` [PATCH v3 11/13] topology: autotools: Add build support for topology core Liam Girdwood
` (3 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Liam Girdwood @ 2015-07-29 16:45 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, Liam Girdwood, Mark Brown
Build the binary output file from all the locally parsed objects and elements.
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
---
src/topology/builder.c | 327 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 327 insertions(+)
create mode 100644 src/topology/builder.c
diff --git a/src/topology/builder.c b/src/topology/builder.c
new file mode 100644
index 0000000..0066b22
--- /dev/null
+++ b/src/topology/builder.c
@@ -0,0 +1,327 @@
+/*
+ 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"
+
+/* verbose output detailing each object size and file position */
+static void verbose(snd_tplg_t *tplg, const char *fmt, ...)
+{
+ int offset;
+ va_list va;
+
+ if (!tplg->verbose)
+ return;
+
+ offset = lseek(tplg->out_fd, 0, SEEK_CUR);
+
+ va_start(va, fmt);
+ fprintf(stdout, "0x%6.6x/%6.6d -", offset, offset);
+ vfprintf(stdout, fmt, va);
+ va_end(va);
+}
+
+/* write out block header to output file */
+static int write_block_header(snd_tplg_t *tplg, unsigned int type,
+ unsigned int vendor_type, unsigned int version, unsigned int index,
+ size_t payload_size, int count)
+{
+ struct snd_soc_tplg_hdr hdr;
+ size_t bytes;
+ int offset = lseek(tplg->out_fd, 0, SEEK_CUR);
+
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.magic = SND_SOC_TPLG_MAGIC;
+ hdr.abi = SND_SOC_TPLG_ABI_VERSION;
+ hdr.type = type;
+ hdr.vendor_type = vendor_type;
+ hdr.version = version;
+ hdr.payload_size = payload_size;
+ hdr.index = index;
+ hdr.size = sizeof(hdr);
+ hdr.count = count;
+
+ /* make sure file offset is aligned with the calculated HDR offset */
+ if ((unsigned int)offset != tplg->next_hdr_pos) {
+ SNDERR("error: New header is at offset 0x%x but file"
+ " offset 0x%x is %s by %d bytes\n",
+ tplg->next_hdr_pos, offset,
+ (unsigned int)offset > tplg->next_hdr_pos ? "ahead" : "behind",
+ abs(offset - tplg->next_hdr_pos));
+ exit(-EINVAL);
+ }
+
+ verbose(tplg, " header type %d size 0x%lx/%ld vendor %d "
+ "version %d\n", type, (long unsigned int)payload_size,
+ (long int)payload_size, vendor_type, version);
+
+ tplg->next_hdr_pos += hdr.payload_size + sizeof(hdr);
+
+ bytes = write(tplg->out_fd, &hdr, sizeof(hdr));
+ if (bytes != sizeof(hdr)) {
+ SNDERR("error: can't write section header %lu\n",
+ (long unsigned int)bytes);
+ return bytes;
+ }
+
+ return bytes;
+}
+
+static int write_data_block(snd_tplg_t *tplg, int size, int tplg_type,
+ const char *obj_name, void *data)
+{
+ int ret;
+
+ /* write the header for this block */
+ ret = write_block_header(tplg, tplg_type, 0,
+ SND_SOC_TPLG_ABI_VERSION, 0, size, 1);
+ if (ret < 0) {
+ SNDERR("error: failed to write %s block %d\n", obj_name, ret);
+ return ret;
+ }
+
+ verbose(tplg, " %s : write %d bytes\n", obj_name, size);
+
+ ret = write(tplg->out_fd, data, size);
+ if (ret < 0) {
+ SNDERR("error: failed to write %s %d\n", obj_name, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int write_elem_block(snd_tplg_t *tplg,
+ struct list_head *base, int size, int tplg_type, const char *obj_name)
+{
+ struct list_head *pos;
+ struct tplg_elem *elem;
+ int ret, wsize = 0, count = 0, vendor_type;
+
+ /* count number of elements */
+ list_for_each(pos, base)
+ count++;
+
+ /* write the header for this block */
+ list_for_each(pos, base) {
+ elem = list_entry(pos, struct tplg_elem, list);
+ vendor_type = elem->vendor_type;
+ break;
+ }
+
+ ret = write_block_header(tplg, tplg_type, vendor_type,
+ SND_SOC_TPLG_ABI_VERSION, 0, size, count);
+ if (ret < 0) {
+ SNDERR("error: failed to write %s block %d\n",
+ obj_name, ret);
+ return ret;
+ }
+
+ /* write each elem to block */
+ 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->type != OBJECT_TYPE_DAPM_GRAPH)
+ verbose(tplg, " %s '%s': write %d bytes\n",
+ obj_name, elem->id, elem->size);
+ else
+ verbose(tplg, " %s '%s': write %d bytes\n",
+ obj_name, elem->route->source, elem->size);
+
+ count = write(tplg->out_fd, elem->obj, elem->size);
+ if (count < 0) {
+ SNDERR("error: failed to write %s %d\n",
+ obj_name, ret);
+ return ret;
+ }
+
+ wsize += count;
+ }
+
+ /* make sure we have written the correct size */
+ if (wsize != size) {
+ SNDERR("error: size mismatch. Expected %d wrote %d\n",
+ size, wsize);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int calc_block_size(struct list_head *base)
+{
+ struct list_head *pos;
+ struct tplg_elem *elem;
+ int 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;
+
+ size += elem->size;
+ }
+
+ return size;
+}
+
+static int write_block(snd_tplg_t *tplg, struct list_head *base,
+ int type)
+{
+ int size;
+
+ /* calculate the block size in bytes for all elems in this list */
+ size = calc_block_size(base);
+ if (size <= 0)
+ return size;
+
+ verbose(tplg, " block size for type %d is %d\n", type, size);
+
+ /* write each elem for this block */
+ switch (type) {
+ case OBJECT_TYPE_MIXER:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_MIXER, "mixer");
+ case OBJECT_TYPE_BYTES:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_BYTES, "bytes");
+ case OBJECT_TYPE_ENUM:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_ENUM, "enum");
+ case OBJECT_TYPE_DAPM_GRAPH:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_DAPM_GRAPH, "route");
+ case OBJECT_TYPE_DAPM_WIDGET:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_DAPM_WIDGET, "widget");
+ case OBJECT_TYPE_PCM:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_PCM, "pcm");
+ case OBJECT_TYPE_BE:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_DAI_LINK, "be");
+ case OBJECT_TYPE_CC:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_DAI_LINK, "cc");
+ case OBJECT_TYPE_MANIFEST:
+ return write_data_block(tplg, size, SND_SOC_TPLG_TYPE_MANIFEST,
+ "manifest", &tplg->manifest);
+ case OBJECT_TYPE_DATA:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_PDATA, "data");
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int tplg_write_data(snd_tplg_t *tplg)
+{
+ int ret;
+
+ /* write manifest */
+ ret = write_data_block(tplg, sizeof(tplg->manifest),
+ OBJECT_TYPE_MANIFEST, "manifest", &tplg->manifest);
+ if (ret < 0) {
+ SNDERR("failed to write manifest %d\n", ret);
+ return ret;
+ }
+
+ /* write mixer elems. */
+ ret = write_block(tplg, &tplg->mixer_list,
+ OBJECT_TYPE_MIXER);
+ if (ret < 0) {
+ SNDERR("failed to write control elems %d\n", ret);
+ return ret;
+ }
+
+ /* write enum control elems. */
+ ret = write_block(tplg, &tplg->enum_list,
+ OBJECT_TYPE_ENUM);
+ if (ret < 0) {
+ SNDERR("failed to write control elems %d\n", ret);
+ return ret;
+ }
+
+ /* write bytes extended control elems. */
+ ret = write_block(tplg, &tplg->bytes_ext_list,
+ OBJECT_TYPE_BYTES);
+ if (ret < 0) {
+ SNDERR("failed to write control elems %d\n", ret);
+ return ret;
+ }
+
+ /* write widget elems */
+ ret = write_block(tplg, &tplg->widget_list,
+ OBJECT_TYPE_DAPM_WIDGET);
+ if (ret < 0) {
+ SNDERR("failed to write widget elems %d\n", ret);
+ return ret;
+ }
+
+ /* write pcm elems */
+ ret = write_block(tplg, &tplg->pcm_list,
+ OBJECT_TYPE_PCM);
+ if (ret < 0) {
+ SNDERR("failed to write pcm elems %d\n", ret);
+ return ret;
+ }
+
+ /* write be elems */
+ ret = write_block(tplg, &tplg->be_list,
+ OBJECT_TYPE_BE);
+ if (ret < 0) {
+ SNDERR("failed to write be elems %d\n", ret);
+ return ret;
+ }
+
+ /* write cc elems */
+ ret = write_block(tplg, &tplg->cc_list,
+ OBJECT_TYPE_CC);
+ if (ret < 0) {
+ SNDERR("failed to write cc elems %d\n", ret);
+ return ret;
+ }
+
+ /* write route elems */
+ ret = write_block(tplg, &tplg->route_list,
+ OBJECT_TYPE_DAPM_GRAPH);
+ if (ret < 0) {
+ SNDERR("failed to write graph elems %d\n", ret);
+ return ret;
+ }
+
+ /* write private data */
+ ret = write_block(tplg, &tplg->pdata_list,
+ OBJECT_TYPE_DATA);
+ if (ret < 0) {
+ SNDERR("failed to write private data %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
--
2.1.4
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 11/13] topology: autotools: Add build support for topology core
2015-07-29 16:45 [PATCH v3 00/13] alsa-lib topology support Liam Girdwood
` (9 preceding siblings ...)
2015-07-29 16:45 ` [PATCH v3 10/13] topology: Add binary file builder Liam Girdwood
@ 2015-07-29 16:45 ` Liam Girdwood
2015-07-29 16:45 ` [PATCH v3 12/13] topology: doxygen: Add doxygen " Liam Girdwood
` (2 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Liam Girdwood @ 2015-07-29 16:45 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, Liam Girdwood, Mark Brown
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
---
configure.ac | 11 ++++++++++-
include/Makefile.am | 4 ++++
src/Makefile.am | 7 +++++++
src/topology/Makefile.am | 19 +++++++++++++++++++
4 files changed, 40 insertions(+), 1 deletion(-)
create mode 100644 src/topology/Makefile.am
diff --git a/configure.ac b/configure.ac
index 9621d4e..a482b3e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -380,6 +380,9 @@ AC_ARG_ENABLE(seq,
AC_ARG_ENABLE(ucm,
AS_HELP_STRING([--disable-ucm], [disable the use-case-manager component]),
[build_ucm="$enableval"], [build_ucm="yes"])
+AC_ARG_ENABLE(topology,
+ AS_HELP_STRING([--disable-topology], [disable the DSP topology component]),
+ [build_topology="$enableval"], [build_topology="yes"])
AC_ARG_ENABLE(alisp,
AS_HELP_STRING([--disable-alisp], [disable the alisp component]),
[build_alisp="$enableval"], [build_alisp="yes"])
@@ -422,6 +425,7 @@ AM_CONDITIONAL([BUILD_RAWMIDI], [test x$build_rawmidi = xyes])
AM_CONDITIONAL([BUILD_HWDEP], [test x$build_hwdep = xyes])
AM_CONDITIONAL([BUILD_SEQ], [test x$build_seq = xyes])
AM_CONDITIONAL([BUILD_UCM], [test x$build_ucm = xyes])
+AM_CONDITIONAL([BUILD_TOPOLOGY], [test x$build_topology = xyes])
AM_CONDITIONAL([BUILD_ALISP], [test x$build_alisp = xyes])
AM_CONDITIONAL([BUILD_PYTHON], [test x$build_python = xyes])
@@ -443,6 +447,9 @@ fi
if test "$build_ucm" = "yes"; then
AC_DEFINE([BUILD_UCM], "1", [Build UCM component])
fi
+if test "$build_topology" = "yes"; then
+ AC_DEFINE([BUILD_TOPOLOGY], "1", [Build DSP Topology component])
+fi
dnl PCM Plugins
@@ -643,7 +650,7 @@ AC_OUTPUT(Makefile doc/Makefile doc/pictures/Makefile doc/doxygen.cfg \
src/pcm/Makefile src/pcm/scopes/Makefile \
src/rawmidi/Makefile src/timer/Makefile \
src/hwdep/Makefile src/seq/Makefile src/ucm/Makefile \
- src/alisp/Makefile \
+ src/alisp/Makefile src/topology/Makefile \
src/conf/Makefile src/conf/alsa.conf.d/Makefile \
src/conf/cards/Makefile \
src/conf/pcm/Makefile \
@@ -656,6 +663,8 @@ AC_OUTPUT(Makefile doc/Makefile doc/pictures/Makefile doc/doxygen.cfg \
src/conf/ucm/PAZ00/Makefile \
src/conf/ucm/GoogleNyan/Makefile \
src/conf/ucm/broadwell-rt286/Makefile \
+ src/conf/topology/Makefile \
+ src/conf/topology/broadwell/Makefile \
modules/Makefile modules/mixer/Makefile modules/mixer/simple/Makefile \
alsalisp/Makefile aserver/Makefile \
test/Makefile test/lsb/Makefile \
diff --git a/include/Makefile.am b/include/Makefile.am
index 4baa03a..ff931fd 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -50,6 +50,10 @@ if BUILD_UCM
alsainclude_HEADERS += use-case.h
endif
+if BUILD_TOPOLOGY
+alsainclude_HEADERS += topology.h
+endif
+
if BUILD_ALISP
alsainclude_HEADERS += alisp.h
endif
diff --git a/src/Makefile.am b/src/Makefile.am
index fa255ff..57686a6 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -42,6 +42,10 @@ if BUILD_UCM
SUBDIRS += ucm
libasound_la_LIBADD += ucm/libucm.la
endif
+if BUILD_TOPOLOGY
+SUBDIRS += topology
+libasound_la_LIBADD += topology/libtopology.la
+endif
if BUILD_ALISP
SUBDIRS += alisp
libasound_la_LIBADD += alisp/libalisp.la
@@ -81,6 +85,9 @@ seq/libseq.la:
ucm/libucm.la:
$(MAKE) -C ucm libucm.la
+topology/libtopology.la:
+ $(MAKE) -C topology libtopology.la
+
instr/libinstr.la:
$(MAKE) -C instr libinstr.la
diff --git a/src/topology/Makefile.am b/src/topology/Makefile.am
new file mode 100644
index 0000000..3fb8bf7
--- /dev/null
+++ b/src/topology/Makefile.am
@@ -0,0 +1,19 @@
+EXTRA_LTLIBRARIES = libtopology.la
+
+libtopology_la_SOURCES =\
+ parser.c \
+ builder.c \
+ ctl.c \
+ dapm.c \
+ pcm.c \
+ data.c \
+ text.c \
+ channel.c \
+ ops.c \
+ elem.c
+
+noinst_HEADERS = tplg_local.h
+
+all: libtopology.la
+
+AM_CPPFLAGS=-I$(top_srcdir)/include
--
2.1.4
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 12/13] topology: doxygen: Add doxygen support for topology core.
2015-07-29 16:45 [PATCH v3 00/13] alsa-lib topology support Liam Girdwood
` (10 preceding siblings ...)
2015-07-29 16:45 ` [PATCH v3 11/13] topology: autotools: Add build support for topology core Liam Girdwood
@ 2015-07-29 16:45 ` Liam Girdwood
2015-07-29 16:45 ` [PATCH v3 13/13] conf: topology: Add topology file for broadwell audio DSP Liam Girdwood
2015-07-30 15:13 ` [PATCH v3 00/13] alsa-lib topology support Takashi Iwai
13 siblings, 0 replies; 17+ messages in thread
From: Liam Girdwood @ 2015-07-29 16:45 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, Liam Girdwood, Mark Brown
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
---
doc/doxygen.cfg.in | 7 +++++--
doc/index.doxygen | 1 +
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/doc/doxygen.cfg.in b/doc/doxygen.cfg.in
index 043e75b..92bd52b 100644
--- a/doc/doxygen.cfg.in
+++ b/doc/doxygen.cfg.in
@@ -29,6 +29,7 @@ INPUT = @top_srcdir@/doc/index.doxygen \
@top_srcdir@/include/control_external.h \
@top_srcdir@/include/mixer.h \
@top_srcdir@/include/use-case.h \
+ @top_srcdir@/include/topology.h \
@top_srcdir@/src/error.c \
@top_srcdir@/src/dlmisc.c \
@top_srcdir@/src/async.c \
@@ -78,7 +79,8 @@ INPUT = @top_srcdir@/doc/index.doxygen \
@top_srcdir@/src/timer \
@top_srcdir@/src/hwdep \
@top_srcdir@/src/seq \
- @top_srcdir@/src/ucm
+ @top_srcdir@/src/ucm \
+ @top_srcdir@/src/topology
EXCLUDE = @top_srcdir@/src/control/control_local.h \
@top_srcdir@/src/pcm/atomic.h \
@top_srcdir@/src/pcm/interval.h \
@@ -94,7 +96,8 @@ EXCLUDE = @top_srcdir@/src/control/control_local.h \
@top_srcdir@/src/mixer/mixer_local.h \
@top_srcdir@/src/rawmidi/rawmidi_local.h \
@top_srcdir@/src/seq/seq_local.h \
- @top_srcdir@/src/ucm/ucm_local.h
+ @top_srcdir@/src/ucm/ucm_local.h \
+ @top_srcdir@/src/topology/tplg_local.h
RECURSIVE = YES
FILE_PATTERNS = *.c *.h
EXAMPLE_PATH = @top_srcdir@/test
diff --git a/doc/index.doxygen b/doc/index.doxygen
index 7d049fe..b40c75a 100644
--- a/doc/index.doxygen
+++ b/doc/index.doxygen
@@ -41,6 +41,7 @@ may be placed in the library code instead of the kernel driver.</P>
<LI>Page \ref timer explains the design of the Timer API.
<LI>Page \ref seq explains the design of the Sequencer API.
<LI>Page \ref ucm explains the use case API.
+ <LI>Page \ref topology explains the DSP topology API.
</UL>
<H2>Configuration</H2>
--
2.1.4
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 13/13] conf: topology: Add topology file for broadwell audio DSP
2015-07-29 16:45 [PATCH v3 00/13] alsa-lib topology support Liam Girdwood
` (11 preceding siblings ...)
2015-07-29 16:45 ` [PATCH v3 12/13] topology: doxygen: Add doxygen " Liam Girdwood
@ 2015-07-29 16:45 ` Liam Girdwood
2015-07-30 15:13 ` [PATCH v3 00/13] alsa-lib topology support Takashi Iwai
13 siblings, 0 replies; 17+ messages in thread
From: Liam Girdwood @ 2015-07-29 16:45 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, Liam Girdwood, Mark Brown
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
---
src/conf/Makefile.am | 2 +-
src/conf/topology/Makefile.am | 1 +
src/conf/topology/broadwell/Makefile.am | 4 +
src/conf/topology/broadwell/broadwell.conf | 375 +++++++++++++++++++++++++++++
4 files changed, 381 insertions(+), 1 deletion(-)
create mode 100644 src/conf/topology/Makefile.am
create mode 100644 src/conf/topology/broadwell/Makefile.am
create mode 100644 src/conf/topology/broadwell/broadwell.conf
diff --git a/src/conf/Makefile.am b/src/conf/Makefile.am
index 948d5a1..a04f73f 100644
--- a/src/conf/Makefile.am
+++ b/src/conf/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS=cards pcm alsa.conf.d ucm
+SUBDIRS=cards pcm alsa.conf.d ucm topology
cfg_files = alsa.conf
if BUILD_ALISP
diff --git a/src/conf/topology/Makefile.am b/src/conf/topology/Makefile.am
new file mode 100644
index 0000000..f56a96c
--- /dev/null
+++ b/src/conf/topology/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS=broadwell
diff --git a/src/conf/topology/broadwell/Makefile.am b/src/conf/topology/broadwell/Makefile.am
new file mode 100644
index 0000000..35d1e83
--- /dev/null
+++ b/src/conf/topology/broadwell/Makefile.am
@@ -0,0 +1,4 @@
+alsaconfigdir = @ALSA_CONFIG_DIR@
+topologydir = $(alsaconfigdir)/topology/broadwell
+topology_DATA = broadwell.conf
+EXTRA_DIST = $(topology_DATA)
diff --git a/src/conf/topology/broadwell/broadwell.conf b/src/conf/topology/broadwell/broadwell.conf
new file mode 100644
index 0000000..05b3889
--- /dev/null
+++ b/src/conf/topology/broadwell/broadwell.conf
@@ -0,0 +1,375 @@
+# Dynamic Firmware Configuration for Broadwell
+
+# TLV
+SectionTLV."hsw_vol_tlv" {
+ Comment "TLV used by both global and stream volumes"
+
+ scale {
+ min "-9000"
+ step "300"
+ mute "1"
+ }
+}
+
+# Controls
+SectionControlMixer."Master Playback Volume" {
+ Comment "Global DSP volume"
+
+ # control belongs to this index group
+ index "1"
+
+ # Channel register and shift for Front Left/Right
+ channel."FL" {
+ reg "0"
+ shift "0"
+ }
+ channel."FR" {
+ reg "0"
+ shift "8"
+ }
+
+ # max control value and whether value is inverted
+ max "31"
+ invert "false"
+
+ # control uses bespoke driver get/put/info ID 0
+ ops."ctl" {
+ info "volsw"
+ get "256"
+ put "256"
+ }
+
+ # uses TLV data above
+ tlv "hsw_vol_tlv"
+}
+
+SectionControlMixer."Media0 Playback Volume" {
+ Comment "Offload 0 volume"
+
+ # control belongs to this index group
+ index "1"
+
+ # Channel register and shift for Front Left/Right
+ channel."FL" {
+ reg "1"
+ shift "0"
+ }
+ channel."FR" {
+ reg "1"
+ shift "8"
+ }
+
+ # max control value and whether value is inverted
+ max "31"
+ invert "false"
+
+ # control uses bespoke driver get/put/info ID 0
+ ops."ctl" {
+ info "volsw"
+ get "257"
+ put "257"
+ }
+
+ # uses TLV data above
+ tlv "hsw_vol_tlv"
+}
+
+SectionControlMixer."Media1 Playback Volume" {
+ Comment "Offload 1 volume"
+
+ # control belongs to this index group
+ index "1"
+
+ # Channel register and shift for Front Left/Right
+ channel."FL" {
+ reg "2"
+ shift "0"
+ }
+ channel."FR" {
+ reg "2"
+ shift "8"
+ }
+
+ # max control value and whether value is inverted
+ max "31"
+ invert "false"
+
+ # control uses bespoke driver get/put/info ID 0
+ ops."ctl" {
+ info "volsw"
+ get "257"
+ put "257"
+ }
+
+ # uses TLV data above
+ tlv "hsw_vol_tlv"
+}
+
+SectionControlMixer."Mic Capture Volume" {
+ Comment "Mic Capture volume"
+
+ # control belongs to this index group
+ index "1"
+
+ # Channel register and shift for Front Left/Right
+ channel."FL" {
+ reg "0"
+ shift "0"
+ }
+ channel."FR" {
+ reg "0"
+ shift "8"
+ }
+
+ # max control value and whether value is inverted
+ max "31"
+ invert "false"
+
+ # control uses bespoke driver get/put/info ID 0
+ ops."ctl" {
+ info "volsw"
+ get "257"
+ put "257"
+ }
+
+ # uses TLV data above
+ tlv "hsw_vol_tlv"
+}
+
+SectionWidget."SSP0 CODEC IN" {
+
+ index "1"
+ type "aif_in"
+ no_pm "true"
+ shift "0"
+ invert "0"
+}
+
+SectionWidget."SSP0 CODEC OUT" {
+
+ index "1"
+ type "aif_out"
+ no_pm "true"
+ shift "0"
+ invert "0"
+}
+
+SectionWidget."SSP1 BT IN" {
+
+ index "1"
+ type "aif_in"
+ no_pm "true"
+ shift "0"
+ invert "0"
+}
+
+SectionWidget."SSP1 BT OUT" {
+
+ index "1"
+ type "aif_out"
+ no_pm "true"
+ shift "0"
+ invert "0"
+}
+
+SectionWidget."Playback VMixer" {
+
+ index "1"
+ type "mixer"
+ no_pm "true"
+ shift "0"
+ invert "0"
+}
+
+# PCM Configurations supported by FW
+SectionPCMConfig."PCM 48k Stereo 24bit" {
+
+ config."playback" {
+ format "S24_LE"
+ rate "48000"
+ channels "2"
+ tdm_slot "0xf"
+ }
+
+ config."capture" {
+ format "S24_LE"
+ rate "48000"
+ channels "2"
+ tdm_slot "0xf"
+ }
+}
+
+SectionPCMConfig."PCM 48k Stereo 16bit" {
+
+ config."playback" {
+ format "S16_LE"
+ rate "48000"
+ channels "2"
+ tdm_slot "0xf"
+ }
+
+ config."capture" {
+ format "S16_LE"
+ rate "48000"
+ channels "2"
+ tdm_slot "0xf"
+ }
+}
+
+SectionPCMConfig."PCM 48k 2P/4C 16bit" {
+
+ config."playback" {
+ format "S16_LE"
+ rate "48000"
+ channels "2"
+ tdm_slot "0xf"
+ }
+
+ config."capture" {
+ format "S16_LE"
+ rate "48000"
+ channels "4"
+ tdm_slot "0xf"
+ }
+}
+
+# PCM capabilities supported by FW
+SectionPCMCapabilities."System Playback" {
+
+ formats "S24_LE,S16_LE"
+ rate_min "48000"
+ rate_max "48000"
+ channels_min "2"
+ channels_max "2"
+}
+
+SectionPCMCapabilities."Analog Capture" {
+
+ formats "S24_LE,S16_LE"
+ rate_min "48000"
+ rate_max "48000"
+ channels_min "2"
+ channels_max "4"
+}
+
+SectionPCMCapabilities."Loopback Capture" {
+
+ formats "S24_LE,S16_LE"
+ rate_min "48000"
+ rate_max "48000"
+ channels_min "2"
+ channels_max "2"
+}
+
+SectionPCMCapabilities."Offload0 Playback" {
+ formats "S24_LE,S16_LE"
+ rate_min "8000"
+ rate_max "192000"
+ channels_min "2"
+ channels_max "2"
+}
+
+SectionPCMCapabilities."Offload1 Playback" {
+ formats "S24_LE,S16_LE"
+ rate_min "8000"
+ rate_max "48000"
+ channels_min "2"
+ channels_max "2"
+}
+
+# PCM devices exported by Firmware
+SectionPCM."System Pin" {
+
+ index "1"
+
+ # used for binding to the PCM
+ ID "0"
+
+ pcm."playback" {
+
+ capabilities "System Playback"
+
+ configs [
+ "PCM 48k Stereo 24bit"
+ "PCM 48k Stereo 16bit"
+ ]
+ }
+
+ pcm."capture" {
+
+ capabilities "Analog Capture"
+
+ configs [
+ "PCM 48k Stereo 24bit"
+ "PCM 48k Stereo 16bit"
+ "PCM 48k 2P/4C 16bit"
+ ]
+ }
+}
+
+SectionPCM."Offload0 Pin" {
+
+ index "1"
+
+ # used for binding to the PCM
+ ID "1"
+
+ pcm."playback" {
+
+ capabilities "Offload0 Playback"
+
+ configs [
+ "PCM 48k Stereo 24bit"
+ "PCM 48k Stereo 16bit"
+ ]
+ }
+}
+
+SectionPCM."Offload1 Pin" {
+
+ index "1"
+
+ # used for binding to the PCM
+ ID "2"
+
+ pcm."playback" {
+
+ capabilities "Offload1 Playback"
+
+ configs [
+ "PCM 48k Stereo 24bit"
+ "PCM 48k Stereo 16bit"
+ ]
+ }
+}
+
+SectionPCM."Loopback Pin" {
+
+ index "1"
+
+ # used for binding to the PCM
+ ID "3"
+
+ pcm."capture" {
+
+ capabilities "Loopback Capture"
+
+ configs [
+ "PCM 48k Stereo 24bit"
+ "PCM 48k Stereo 16bit"
+ ]
+ }
+}
+
+SectionGraph."dsp" {
+ index "1"
+
+ lines [
+ "Playback VMixer, , System Playback"
+ "Playback VMixer, , Offload0 Playback"
+ "Playback VMixer, , Offload1 Playback"
+ "SSP0 CODEC OUT, , Playback VMixer"
+ "Loopback Capture, , Playback VMixer"
+ "Analog Capture, , SSP0 CODEC IN"
+ ]
+}
--
2.1.4
^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH v3 00/13] alsa-lib topology support
2015-07-29 16:45 [PATCH v3 00/13] alsa-lib topology support Liam Girdwood
` (12 preceding siblings ...)
2015-07-29 16:45 ` [PATCH v3 13/13] conf: topology: Add topology file for broadwell audio DSP Liam Girdwood
@ 2015-07-30 15:13 ` Takashi Iwai
2015-07-31 9:27 ` Liam Girdwood
13 siblings, 1 reply; 17+ messages in thread
From: Takashi Iwai @ 2015-07-30 15:13 UTC (permalink / raw)
To: Liam Girdwood; +Cc: alsa-devel, Mark Brown
On Wed, 29 Jul 2015 18:45:12 +0200,
Liam Girdwood wrote:
>
> This series adds support for parsing topology data text files and converting
> to the binary topology format accepted by the kernel.
>
> Changes since V2 :-
>
> o Added documentation in doxygen format describing API and text file format.
> o use SNDERR macro for error reporting.
> o Added text copy helper.
> o Make all parsed keywords lowercase
> o Assignment for some structure copies instead of memcpy().
> o Fixed the private data parser to handle byte, short and word data.
>
> Changes since V1 :-
>
> o Terminate all strncpy() strings with NULL.
> o Use snd_pcm_format_value() instead of local lookup.
> o Set a maximum size for private data files.
> o Set capture/playback stream direction for new PCMs
> o Only set TLV access flags for TLV CTLs.
>
>
> Liam Girdwood (13):
> topology: uapi: Add UAPI headers for topology ABI
> topology: Add topology core parser.
> topology: Add text section parser.
> topology: Add PCM parser.
> topology: Add operations parser
> topology: Add private data parser
> topology: Add DAPM object parser
> topology: Add CTL parser
> topology: Add Channel map parser.
> topology: Add binary file builder.
> topology: autotools: Add build support for topology core
> topology: doxygen: Add doxygen support for topology core.
> conf: topology: Add topology file for broadwell audio DSP
I merged all patches now together with a couple of trivial fixes.
(Also fixed the bisectionability in patches 11 and 13).
thanks,
Takashi
>
> configure.ac | 11 +-
> doc/doxygen.cfg.in | 7 +-
> doc/index.doxygen | 1 +
> include/Makefile.am | 4 +
> include/sound/Makefile.am | 3 +-
> include/sound/asoc.h | 388 ++++++++++++++++++
> include/sound/tlv.h | 23 ++
> include/topology.h | 497 ++++++++++++++++++++++
> src/Makefile.am | 7 +
> src/conf/Makefile.am | 2 +-
> src/conf/topology/Makefile.am | 1 +
> src/conf/topology/broadwell/Makefile.am | 4 +
> src/conf/topology/broadwell/broadwell.conf | 375 +++++++++++++++++
> src/topology/Makefile.am | 19 +
> src/topology/builder.c | 327 +++++++++++++++
> src/topology/channel.c | 122 ++++++
> src/topology/ctl.c | 613 +++++++++++++++++++++++++++
> src/topology/dapm.c | 562 +++++++++++++++++++++++++
> src/topology/data.c | 396 ++++++++++++++++++
> src/topology/elem.c | 187 +++++++++
> src/topology/ops.c | 84 ++++
> src/topology/parser.c | 359 ++++++++++++++++
> src/topology/pcm.c | 639 +++++++++++++++++++++++++++++
> src/topology/text.c | 88 ++++
> src/topology/tplg_local.h | 234 +++++++++++
> 25 files changed, 4948 insertions(+), 5 deletions(-)
> create mode 100644 include/sound/asoc.h
> create mode 100644 include/sound/tlv.h
> create mode 100644 include/topology.h
> create mode 100644 src/conf/topology/Makefile.am
> create mode 100644 src/conf/topology/broadwell/Makefile.am
> create mode 100644 src/conf/topology/broadwell/broadwell.conf
> create mode 100644 src/topology/Makefile.am
> create mode 100644 src/topology/builder.c
> create mode 100644 src/topology/channel.c
> create mode 100644 src/topology/ctl.c
> create mode 100644 src/topology/dapm.c
> create mode 100644 src/topology/data.c
> create mode 100644 src/topology/elem.c
> create mode 100644 src/topology/ops.c
> create mode 100644 src/topology/parser.c
> create mode 100644 src/topology/pcm.c
> create mode 100644 src/topology/text.c
> create mode 100644 src/topology/tplg_local.h
>
> --
> 2.1.4
>
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 00/13] alsa-lib topology support
2015-07-30 15:13 ` [PATCH v3 00/13] alsa-lib topology support Takashi Iwai
@ 2015-07-31 9:27 ` Liam Girdwood
2015-07-31 15:31 ` Takashi Iwai
0 siblings, 1 reply; 17+ messages in thread
From: Liam Girdwood @ 2015-07-31 9:27 UTC (permalink / raw)
To: Takashi Iwai; +Cc: Koul, Vinod, Lin, Mengdong, alsa-devel, Mark Brown
On Thu, 2015-07-30 at 17:13 +0200, Takashi Iwai wrote:
>
> I merged all patches now together with a couple of trivial fixes.
> (Also fixed the bisectionability in patches 11 and 13).
>
Thanks, when are you thinking of doing a new alsa-lib release ?
We have a few more new features/updates to add next week (it's being
tested internally on SKL atm). Any chance you could wait until after
they were upstreamed ?
Btw, I also have a topology command line utility for alsa-utils that
I'll send later today.
Thanks
Liam
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 00/13] alsa-lib topology support
2015-07-31 9:27 ` Liam Girdwood
@ 2015-07-31 15:31 ` Takashi Iwai
0 siblings, 0 replies; 17+ messages in thread
From: Takashi Iwai @ 2015-07-31 15:31 UTC (permalink / raw)
To: Liam Girdwood; +Cc: Koul, Vinod, Lin, Mengdong, alsa-devel, Mark Brown
On Fri, 31 Jul 2015 11:27:42 +0200,
Liam Girdwood wrote:
>
> On Thu, 2015-07-30 at 17:13 +0200, Takashi Iwai wrote:
> >
> > I merged all patches now together with a couple of trivial fixes.
> > (Also fixed the bisectionability in patches 11 and 13).
> >
>
> Thanks, when are you thinking of doing a new alsa-lib release ?
>
> We have a few more new features/updates to add next week (it's being
> tested internally on SKL atm). Any chance you could wait until after
> they were upstreamed ?
I don't think we hurry for the new release yet.
> Btw, I also have a topology command line utility for alsa-utils that
> I'll send later today.
That's great. Thanks!
Takashi
^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2015-07-31 15:31 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-07-29 16:45 [PATCH v3 00/13] alsa-lib topology support Liam Girdwood
2015-07-29 16:45 ` [PATCH v3 01/13] topology: uapi: Add UAPI headers for topology ABI Liam Girdwood
2015-07-29 16:45 ` [PATCH v3 02/13] topology: Add topology core parser Liam Girdwood
2015-07-29 16:45 ` [PATCH v3 03/13] topology: Add text section parser Liam Girdwood
2015-07-29 16:45 ` [PATCH v3 04/13] topology: Add PCM parser Liam Girdwood
2015-07-29 16:45 ` [PATCH v3 05/13] topology: Add operations parser Liam Girdwood
2015-07-29 16:45 ` [PATCH v3 06/13] topology: Add private data parser Liam Girdwood
2015-07-29 16:45 ` [PATCH v3 07/13] topology: Add DAPM object parser Liam Girdwood
2015-07-29 16:45 ` [PATCH v3 08/13] topology: Add CTL parser Liam Girdwood
2015-07-29 16:45 ` [PATCH v3 09/13] topology: Add Channel map parser Liam Girdwood
2015-07-29 16:45 ` [PATCH v3 10/13] topology: Add binary file builder Liam Girdwood
2015-07-29 16:45 ` [PATCH v3 11/13] topology: autotools: Add build support for topology core Liam Girdwood
2015-07-29 16:45 ` [PATCH v3 12/13] topology: doxygen: Add doxygen " Liam Girdwood
2015-07-29 16:45 ` [PATCH v3 13/13] conf: topology: Add topology file for broadwell audio DSP Liam Girdwood
2015-07-30 15:13 ` [PATCH v3 00/13] alsa-lib topology support Takashi Iwai
2015-07-31 9:27 ` Liam Girdwood
2015-07-31 15:31 ` Takashi Iwai
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.