* [PATCH 02/13] conf: topology: Add topology file for broadwell audio DSP
2015-06-29 16:32 [PATCH 01/13] topology: uapi: Add UAPI headers for topology ABI Liam Girdwood
@ 2015-06-29 16:32 ` Liam Girdwood
2015-06-29 16:32 ` [PATCH 03/13] topology: Add topology core parser Liam Girdwood
` (10 subsequent siblings)
11 siblings, 0 replies; 16+ messages in thread
From: Liam Girdwood @ 2015-06-29 16:32 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 | 406 +++++++++++++++++++++++++++++
4 files changed, 412 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..4d63357
--- /dev/null
+++ b/src/conf/topology/broadwell/broadwell.conf
@@ -0,0 +1,406 @@
+# 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"
+ ]
+ }
+}
+
+# Backend DAI links
+SectionBE."SSP0-Codec" {
+
+ index "1"
+
+ # used for binding to the BE DAI link
+ ID "0"
+
+ # BE DAI link capabilities and supported configs
+ be."playback" {
+
+ capabilities "System Playback"
+
+ configs [
+ "PCM 48k Stereo 24bit"
+ "PCM 48k Stereo 16bit"
+ ]
+ }
+
+ be."capture" {
+
+ capabilities "Analog Capture"
+
+ configs [
+ "PCM 48k Stereo 24bit"
+ "PCM 48k Stereo 16bit"
+ "PCM 48k 2P/4C 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] 16+ messages in thread* [PATCH 03/13] topology: Add topology core parser.
2015-06-29 16:32 [PATCH 01/13] topology: uapi: Add UAPI headers for topology ABI Liam Girdwood
2015-06-29 16:32 ` [PATCH 02/13] conf: topology: Add topology file for broadwell audio DSP Liam Girdwood
@ 2015-06-29 16:32 ` Liam Girdwood
2015-06-29 16:32 ` [PATCH 04/13] topology: Add text section parser Liam Girdwood
` (9 subsequent siblings)
11 siblings, 0 replies; 16+ messages in thread
From: Liam Girdwood @ 2015-06-29 16:32 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 | 75 ++++++++++
src/topology/elem.c | 185 ++++++++++++++++++++++++
src/topology/parser.c | 357 ++++++++++++++++++++++++++++++++++++++++++++++
src/topology/tplg_local.h | 222 ++++++++++++++++++++++++++++
4 files changed, 839 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..d9b223f
--- /dev/null
+++ b/include/topology.h
@@ -0,0 +1,75 @@
+/*
+ *
+ * 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
+ * The topology interface.
+ * See \ref Topology page for more details.
+ * \{
+ */
+
+/*! \page topology ALSA Topology Interface
+ *
+ * ALSA Topology Interface
+ *
+ */
+
+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(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 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..1da7c60
--- /dev/null
+++ b/src/topology/elem.c
@@ -0,0 +1,185 @@
+/*
+ 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->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, *npos;
+ struct tplg_elem *elem;
+
+ list_for_each_safe(pos, npos, 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 parser_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);
+
+ switch (type) {
+ case PARSER_TYPE_DATA:
+ list_add_tail(&elem->list, &tplg->pdata_list);
+ break;
+ case PARSER_TYPE_TEXT:
+ list_add_tail(&elem->list, &tplg->text_list);
+ break;
+ case PARSER_TYPE_TLV:
+ list_add_tail(&elem->list, &tplg->tlv_list);
+ elem->size = sizeof(struct snd_soc_tplg_ctl_tlv);
+ break;
+ case PARSER_TYPE_BYTES:
+ list_add_tail(&elem->list, &tplg->bytes_ext_list);
+ obj_size = sizeof(struct snd_soc_tplg_bytes_control);
+ break;
+ case PARSER_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 PARSER_TYPE_DAPM_WIDGET:
+ list_add_tail(&elem->list, &tplg->widget_list);
+ obj_size = sizeof(struct snd_soc_tplg_dapm_widget);
+ break;
+ case PARSER_TYPE_STREAM_CONFIG:
+ list_add_tail(&elem->list, &tplg->pcm_config_list);
+ obj_size = sizeof(struct snd_soc_tplg_stream_config);
+ break;
+ case PARSER_TYPE_STREAM_CAPS:
+ list_add_tail(&elem->list, &tplg->pcm_caps_list);
+ obj_size = sizeof(struct snd_soc_tplg_stream_caps);
+ break;
+ case PARSER_TYPE_PCM:
+ list_add_tail(&elem->list, &tplg->pcm_list);
+ obj_size = sizeof(struct snd_soc_tplg_pcm_dai);
+ break;
+ case PARSER_TYPE_BE:
+ list_add_tail(&elem->list, &tplg->be_list);
+ obj_size = sizeof(struct snd_soc_tplg_pcm_dai);
+ break;
+ case PARSER_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..f813deb
--- /dev/null
+++ b/src/topology/parser.c
@@ -0,0 +1,357 @@
+/*
+ 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) {
+ fprintf(stderr, "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) {
+ fprintf(stderr, "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) {
+ fprintf(stderr, "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;
+ }
+
+ fprintf(stderr, "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) {
+ fprintf(stdout, "error: could not open configuration file %s",
+ file);
+ return -errno;
+ }
+
+ ret = snd_input_stdio_attach(&in, fp, 1);
+ if (ret < 0) {
+ fprintf(stdout, "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) {
+ fprintf(stdout, "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, PARSER_TYPE_PCM);
+ if (err < 0)
+ return err;
+
+ err = tplg_build_pcm_dai(tplg, PARSER_TYPE_BE);
+ if (err < 0)
+ return err;
+
+ err = tplg_build_pcm_dai(tplg, PARSER_TYPE_CC);
+ if (err < 0)
+ return err;
+
+ err = tplg_build_routes(tplg);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+int snd_tplg_build(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) {
+ fprintf(stderr, "error: failed to open %s err %d\n",
+ outfile, -errno);
+ return -errno;
+ }
+
+ err = tplg_load_config(infile, &cfg);
+ if (err < 0) {
+ fprintf(stderr, "error: failed to load topology file %s\n",
+ infile);
+ return err;
+ }
+
+ err = tplg_parse_config(tplg, cfg);
+ if (err < 0) {
+ fprintf(stderr, "error: failed to parse topology\n");
+ goto out;
+ }
+
+ err = tplg_build_integ(tplg);
+ if (err < 0) {
+ fprintf(stderr, "error: failed to check topology integrity\n");
+ goto out;
+ }
+
+ err = tplg_write_data(tplg);
+ if (err < 0) {
+ fprintf(stderr, "error: failed to write data %d\n", err);
+ goto out;
+ }
+
+out:
+ snd_config_delete(cfg);
+ 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..9c9c490
--- /dev/null
+++ b/src/topology/tplg_local.h
@@ -0,0 +1,222 @@
+/*
+ * 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 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 parser_type {
+ PARSER_TYPE_TLV = 0,
+ PARSER_TYPE_MIXER,
+ PARSER_TYPE_ENUM,
+ PARSER_TYPE_TEXT,
+ PARSER_TYPE_DATA,
+ PARSER_TYPE_BYTES,
+ PARSER_TYPE_STREAM_CONFIG,
+ PARSER_TYPE_STREAM_CAPS,
+ PARSER_TYPE_PCM,
+ PARSER_TYPE_DAPM_WIDGET,
+ PARSER_TYPE_DAPM_GRAPH,
+ PARSER_TYPE_BE,
+ PARSER_TYPE_CC,
+};
+
+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;
+
+ /* 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 parser_type type;
+
+ int size; /* total size of this object inc pdata and ref objects */
+ int compound_elem; /* dont write this element as individual elem */
+
+ /* 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 parser_type type);
+
+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] 16+ messages in thread* [PATCH 04/13] topology: Add text section parser.
2015-06-29 16:32 [PATCH 01/13] topology: uapi: Add UAPI headers for topology ABI Liam Girdwood
2015-06-29 16:32 ` [PATCH 02/13] conf: topology: Add topology file for broadwell audio DSP Liam Girdwood
2015-06-29 16:32 ` [PATCH 03/13] topology: Add topology core parser Liam Girdwood
@ 2015-06-29 16:32 ` Liam Girdwood
2015-06-29 16:32 ` [PATCH 05/13] topology: Add PCM parser Liam Girdwood
` (8 subsequent siblings)
11 siblings, 0 replies; 16+ messages in thread
From: Liam Girdwood @ 2015-06-29 16:32 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 | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 98 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..4a16b02
--- /dev/null
+++ b/src/topology/text.c
@@ -0,0 +1,98 @@
+/*
+ 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;
+
+ strncpy(&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.
+ *
+ * Object text strings.
+ *
+ * SectionText."text name" {
+ *
+ * Values [
+ *
+ * ]
+ * }
+ */
+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, PARSER_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) {
+ fprintf(stderr, "error: failed to parse text values");
+ return err;
+ }
+ continue;
+ }
+ }
+
+ return err;
+}
--
2.1.4
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH 05/13] topology: Add PCM parser.
2015-06-29 16:32 [PATCH 01/13] topology: uapi: Add UAPI headers for topology ABI Liam Girdwood
` (2 preceding siblings ...)
2015-06-29 16:32 ` [PATCH 04/13] topology: Add text section parser Liam Girdwood
@ 2015-06-29 16:32 ` Liam Girdwood
2015-06-29 18:37 ` Takashi Iwai
2015-06-29 16:32 ` [PATCH 06/13] topology: Add operations parser Liam Girdwood
` (7 subsequent siblings)
11 siblings, 1 reply; 16+ messages in thread
From: Liam Girdwood @ 2015-06-29 16:32 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 | 782 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 782 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..476af55
--- /dev/null
+++ b/src/topology/pcm.c
@@ -0,0 +1,782 @@
+/*
+ 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 format text names to types */
+static const struct map_elem pcm_format_map[] = {
+ {"S16_LE", SNDRV_PCM_FORMAT_S16_LE},
+ {"S16_BE", SNDRV_PCM_FORMAT_S16_BE},
+ {"U16_LE", SNDRV_PCM_FORMAT_U16_LE},
+ {"U16_BE", SNDRV_PCM_FORMAT_U16_BE},
+ {"S24_LE", SNDRV_PCM_FORMAT_S24_LE},
+ {"S24_BE", SNDRV_PCM_FORMAT_S24_BE},
+ {"U24_LE", SNDRV_PCM_FORMAT_U24_LE},
+ {"U24_BE", SNDRV_PCM_FORMAT_U24_BE},
+ {"S32_LE", SNDRV_PCM_FORMAT_S32_LE},
+ {"S32_BE", SNDRV_PCM_FORMAT_S32_BE},
+ {"U32_LE", SNDRV_PCM_FORMAT_U32_LE},
+ {"U32_BE", SNDRV_PCM_FORMAT_U32_BE},
+};
+
+static int lookup_pcm_format(const char *c, snd_pcm_format_t *format)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(pcm_format_map); i++) {
+ if (strcmp(pcm_format_map[i].name, c) == 0) {
+ *format = pcm_format_map[i].id;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+struct tplg_elem *lookup_pcm_dai_stream(struct list_head *base, const char* id)
+{
+ struct list_head *pos, *npos;
+ struct tplg_elem *elem;
+ struct snd_soc_tplg_pcm_dai *pcm_dai;
+
+ list_for_each_safe(pos, npos, base) {
+
+ elem = list_entry(pos, struct tplg_elem, list);
+ if (elem->type != PARSER_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);
+
+ memcpy((void*)caps, ref_caps, sizeof(*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);
+
+ memcpy((void*)cfg, ref_cfg, sizeof(*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 PARSER_TYPE_PCM:
+ pcm_dai = elem->pcm;
+ break;
+ case PARSER_TYPE_BE:
+ pcm_dai = elem->be;
+ break;
+ case PARSER_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, PARSER_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,
+ PARSER_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, *npos;
+ struct tplg_elem *elem;
+ int err = 0;
+
+ switch (type) {
+ case PARSER_TYPE_PCM:
+ base = &tplg->pcm_list;
+ break;
+ case PARSER_TYPE_BE:
+ base = &tplg->be_list;
+ break;
+ case PARSER_TYPE_CC:
+ base = &tplg->cc_list;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ list_for_each_safe(pos, npos, base) {
+
+ elem = list_entry(pos, struct tplg_elem, list);
+ if (elem->type != type) {
+ fprintf(stderr, "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
+ *
+ * Describes the PCM configuration for playback and capture streams.
+ *
+ * config."name" {
+ * format "S24_LE"
+ * rate "48000"
+ * channels "2"
+ * tdm_slot "0xf"
+ * }
+ */
+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) {
+ ret = lookup_pcm_format(val, &format);
+ if (ret < 0) {
+ fprintf(stderr, "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
+ *
+ * SectionPCMConfig."PCM config name" {
+ *
+ * config."playback" {
+ *
+ * }
+ *
+ * config."capture" {
+ *
+ * }
+ * }
+ */
+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, PARSER_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)) {
+ ret = lookup_pcm_format(s, &format);
+ if (ret < 0) {
+ fprintf(stderr, "error: unsupported stream format %s\n", s);
+ return -EINVAL;
+ }
+
+ caps->formats[i] = format;
+ s = strtok(NULL, ", ");
+ i++;
+ }
+
+ return 0;
+}
+
+/* Parse pcm Capabilities
+ *
+ * SectionPCMCapabilities." PCM capabilities name" {
+ *
+ * formats "S24_LE,S16_LE"
+ * rate_min "48000"
+ * rate_max "48000"
+ * channels_min "2"
+ * channels_max "2"
+ * }
+ */
+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, PARSER_TYPE_STREAM_CAPS);
+ if (!elem)
+ return -ENOMEM;
+
+ sc = elem->stream_caps;
+ sc->size = elem->size;
+ strncpy(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;
+
+ strncpy(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.
+ *
+ * pcm."name" {
+ *
+ * capabilities "System playback"
+ *
+ * configs [
+ * "PCM 48k Stereo 24bit"
+ * "PCM 48k Stereo 16bit"
+ * ]
+ * }
+ */
+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 == PARSER_TYPE_PCM)
+ pcm_dai = elem->pcm;
+ else if (elem->type == PARSER_TYPE_BE)
+ pcm_dai = elem->be;
+ else if (elem->type == PARSER_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;
+ else if (strcmp(id, "capture") == 0)
+ stream = SND_SOC_TPLG_STREAM_CAPTURE;
+ 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;
+
+ strncpy(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
+ *
+ * SectionPCM."System Pin" {
+ *
+ * index "1"
+ *
+ * # used for binding to the PCM
+ * ID "0"
+ *
+ * pcm."playback" {
+ * capabilities "System Playback"
+ * config "PCM 48k Stereo 24bit"
+ * config "PCM 48k Stereo 16bit"
+ * }
+ *
+ * pcm."capture" {
+ * capabilities "Analog Capture"
+ * config "PCM 48k Stereo 24bit"
+ * config "PCM 48k Stereo 16bit"
+ * config "PCM 48k 2P/4C 16bit"
+ * }
+ * }
+ */
+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, PARSER_TYPE_PCM);
+ if (!elem)
+ return -ENOMEM;
+
+ pcm_dai = elem->pcm;
+ pcm_dai->size = elem->size;
+ strncpy(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
+ *
+ * SectionBE."SSP0-Codec" {
+ *
+ * index "1"
+ *
+ * # used for binding to the PCM
+ * ID "0"
+ *
+ * be."playback" {
+ * capabilities "System Playback"
+ * config "PCM 48k Stereo 24bit"
+ * config "PCM 48k Stereo 16bit"
+ * }
+ *
+ * be."capture" {
+ * capabilities "Analog Capture"
+ * config "PCM 48k Stereo 24bit"
+ * config "PCM 48k Stereo 16bit"
+ * config "PCM 48k 2P/4C 16bit"
+ * }
+ * }
+ */
+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, PARSER_TYPE_BE);
+ if (!elem)
+ return -ENOMEM;
+
+ pcm_dai = elem->be;
+ pcm_dai->size = elem->size;
+ strncpy(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
+ *
+ * SectionCC."FM-Codec" {
+ *
+ * index "1"
+ *
+ * # used for binding to the CC link
+ * ID "0"
+ *
+ * # CC DAI link capabilities and supported configs
+ * cc."playback" {
+ *
+ * capabilities "System playback"
+ *
+ * configs [
+ * "PCM 48k Stereo 16bit"
+ * ]
+ * }
+ *
+ * cc."capture" {
+ *
+ * capabilities "Analog capture"
+ *
+ * configs [
+ * "PCM 48k Stereo 16bit"
+ * ]
+ * }
+ * }
+ */
+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, PARSER_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] 16+ messages in thread* Re: [PATCH 05/13] topology: Add PCM parser.
2015-06-29 16:32 ` [PATCH 05/13] topology: Add PCM parser Liam Girdwood
@ 2015-06-29 18:37 ` Takashi Iwai
0 siblings, 0 replies; 16+ messages in thread
From: Takashi Iwai @ 2015-06-29 18:37 UTC (permalink / raw)
To: Liam Girdwood; +Cc: alsa-devel, Mark Brown
At Mon, 29 Jun 2015 17:32:50 +0100,
Liam Girdwood wrote:
>
> 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 | 782 +++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 782 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..476af55
> --- /dev/null
> +++ b/src/topology/pcm.c
> @@ -0,0 +1,782 @@
> +/*
> + 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 format text names to types */
> +static const struct map_elem pcm_format_map[] = {
> + {"S16_LE", SNDRV_PCM_FORMAT_S16_LE},
> + {"S16_BE", SNDRV_PCM_FORMAT_S16_BE},
> + {"U16_LE", SNDRV_PCM_FORMAT_U16_LE},
> + {"U16_BE", SNDRV_PCM_FORMAT_U16_BE},
> + {"S24_LE", SNDRV_PCM_FORMAT_S24_LE},
> + {"S24_BE", SNDRV_PCM_FORMAT_S24_BE},
> + {"U24_LE", SNDRV_PCM_FORMAT_U24_LE},
> + {"U24_BE", SNDRV_PCM_FORMAT_U24_BE},
> + {"S32_LE", SNDRV_PCM_FORMAT_S32_LE},
> + {"S32_BE", SNDRV_PCM_FORMAT_S32_BE},
> + {"U32_LE", SNDRV_PCM_FORMAT_U32_LE},
> + {"U32_BE", SNDRV_PCM_FORMAT_U32_BE},
> +};
> +
> +static int lookup_pcm_format(const char *c, snd_pcm_format_t *format)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < ARRAY_SIZE(pcm_format_map); i++) {
> + if (strcmp(pcm_format_map[i].name, c) == 0) {
> + *format = pcm_format_map[i].id;
> + return 0;
> + }
> + }
> +
> + return -EINVAL;
> +}
You can use snd_pcm_format_value(), I suppose.
Takashi
> +
> +struct tplg_elem *lookup_pcm_dai_stream(struct list_head *base, const char* id)
> +{
> + struct list_head *pos, *npos;
> + struct tplg_elem *elem;
> + struct snd_soc_tplg_pcm_dai *pcm_dai;
> +
> + list_for_each_safe(pos, npos, base) {
> +
> + elem = list_entry(pos, struct tplg_elem, list);
> + if (elem->type != PARSER_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);
> +
> + memcpy((void*)caps, ref_caps, sizeof(*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);
> +
> + memcpy((void*)cfg, ref_cfg, sizeof(*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 PARSER_TYPE_PCM:
> + pcm_dai = elem->pcm;
> + break;
> + case PARSER_TYPE_BE:
> + pcm_dai = elem->be;
> + break;
> + case PARSER_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, PARSER_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,
> + PARSER_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, *npos;
> + struct tplg_elem *elem;
> + int err = 0;
> +
> + switch (type) {
> + case PARSER_TYPE_PCM:
> + base = &tplg->pcm_list;
> + break;
> + case PARSER_TYPE_BE:
> + base = &tplg->be_list;
> + break;
> + case PARSER_TYPE_CC:
> + base = &tplg->cc_list;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + list_for_each_safe(pos, npos, base) {
> +
> + elem = list_entry(pos, struct tplg_elem, list);
> + if (elem->type != type) {
> + fprintf(stderr, "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
> + *
> + * Describes the PCM configuration for playback and capture streams.
> + *
> + * config."name" {
> + * format "S24_LE"
> + * rate "48000"
> + * channels "2"
> + * tdm_slot "0xf"
> + * }
> + */
> +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) {
> + ret = lookup_pcm_format(val, &format);
> + if (ret < 0) {
> + fprintf(stderr, "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
> + *
> + * SectionPCMConfig."PCM config name" {
> + *
> + * config."playback" {
> + *
> + * }
> + *
> + * config."capture" {
> + *
> + * }
> + * }
> + */
> +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, PARSER_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)) {
> + ret = lookup_pcm_format(s, &format);
> + if (ret < 0) {
> + fprintf(stderr, "error: unsupported stream format %s\n", s);
> + return -EINVAL;
> + }
> +
> + caps->formats[i] = format;
> + s = strtok(NULL, ", ");
> + i++;
> + }
> +
> + return 0;
> +}
> +
> +/* Parse pcm Capabilities
> + *
> + * SectionPCMCapabilities." PCM capabilities name" {
> + *
> + * formats "S24_LE,S16_LE"
> + * rate_min "48000"
> + * rate_max "48000"
> + * channels_min "2"
> + * channels_max "2"
> + * }
> + */
> +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, PARSER_TYPE_STREAM_CAPS);
> + if (!elem)
> + return -ENOMEM;
> +
> + sc = elem->stream_caps;
> + sc->size = elem->size;
> + strncpy(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;
> +
> + strncpy(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.
> + *
> + * pcm."name" {
> + *
> + * capabilities "System playback"
> + *
> + * configs [
> + * "PCM 48k Stereo 24bit"
> + * "PCM 48k Stereo 16bit"
> + * ]
> + * }
> + */
> +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 == PARSER_TYPE_PCM)
> + pcm_dai = elem->pcm;
> + else if (elem->type == PARSER_TYPE_BE)
> + pcm_dai = elem->be;
> + else if (elem->type == PARSER_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;
> + else if (strcmp(id, "capture") == 0)
> + stream = SND_SOC_TPLG_STREAM_CAPTURE;
> + 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;
> +
> + strncpy(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
> + *
> + * SectionPCM."System Pin" {
> + *
> + * index "1"
> + *
> + * # used for binding to the PCM
> + * ID "0"
> + *
> + * pcm."playback" {
> + * capabilities "System Playback"
> + * config "PCM 48k Stereo 24bit"
> + * config "PCM 48k Stereo 16bit"
> + * }
> + *
> + * pcm."capture" {
> + * capabilities "Analog Capture"
> + * config "PCM 48k Stereo 24bit"
> + * config "PCM 48k Stereo 16bit"
> + * config "PCM 48k 2P/4C 16bit"
> + * }
> + * }
> + */
> +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, PARSER_TYPE_PCM);
> + if (!elem)
> + return -ENOMEM;
> +
> + pcm_dai = elem->pcm;
> + pcm_dai->size = elem->size;
> + strncpy(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
> + *
> + * SectionBE."SSP0-Codec" {
> + *
> + * index "1"
> + *
> + * # used for binding to the PCM
> + * ID "0"
> + *
> + * be."playback" {
> + * capabilities "System Playback"
> + * config "PCM 48k Stereo 24bit"
> + * config "PCM 48k Stereo 16bit"
> + * }
> + *
> + * be."capture" {
> + * capabilities "Analog Capture"
> + * config "PCM 48k Stereo 24bit"
> + * config "PCM 48k Stereo 16bit"
> + * config "PCM 48k 2P/4C 16bit"
> + * }
> + * }
> + */
> +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, PARSER_TYPE_BE);
> + if (!elem)
> + return -ENOMEM;
> +
> + pcm_dai = elem->be;
> + pcm_dai->size = elem->size;
> + strncpy(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
> + *
> + * SectionCC."FM-Codec" {
> + *
> + * index "1"
> + *
> + * # used for binding to the CC link
> + * ID "0"
> + *
> + * # CC DAI link capabilities and supported configs
> + * cc."playback" {
> + *
> + * capabilities "System playback"
> + *
> + * configs [
> + * "PCM 48k Stereo 16bit"
> + * ]
> + * }
> + *
> + * cc."capture" {
> + *
> + * capabilities "Analog capture"
> + *
> + * configs [
> + * "PCM 48k Stereo 16bit"
> + * ]
> + * }
> + * }
> + */
> +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, PARSER_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 [flat|nested] 16+ messages in thread
* [PATCH 06/13] topology: Add operations parser
2015-06-29 16:32 [PATCH 01/13] topology: uapi: Add UAPI headers for topology ABI Liam Girdwood
` (3 preceding siblings ...)
2015-06-29 16:32 ` [PATCH 05/13] topology: Add PCM parser Liam Girdwood
@ 2015-06-29 16:32 ` Liam Girdwood
2015-06-29 16:32 ` [PATCH 07/13] topology: Add private data parser Liam Girdwood
` (6 subsequent siblings)
11 siblings, 0 replies; 16+ messages in thread
From: Liam Girdwood @ 2015-06-29 16:32 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 | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 90 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..d3f4a28
--- /dev/null
+++ b/src/topology/ops.c
@@ -0,0 +1,90 @@
+/*
+ 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
+ *
+ * ops."name" {
+ * info "volsw"
+ * get "256"
+ * put "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] 16+ messages in thread* [PATCH 07/13] topology: Add private data parser
2015-06-29 16:32 [PATCH 01/13] topology: uapi: Add UAPI headers for topology ABI Liam Girdwood
` (4 preceding siblings ...)
2015-06-29 16:32 ` [PATCH 06/13] topology: Add operations parser Liam Girdwood
@ 2015-06-29 16:32 ` Liam Girdwood
2015-06-29 18:41 ` Takashi Iwai
2015-06-29 16:32 ` [PATCH 08/13] topology: Add DAPM object parser Liam Girdwood
` (5 subsequent siblings)
11 siblings, 1 reply; 16+ messages in thread
From: Liam Girdwood @ 2015-06-29 16:32 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 | 348 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 348 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..b579df1
--- /dev/null
+++ b/src/topology/data.c
@@ -0,0 +1,348 @@
+/*
+ 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);
+ filename[sizeof(filename)-1] = '\0';
+
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "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) {
+ fprintf(stderr, "error: invalid data file size %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");
+}
+
+static int get_hex_num(const char *str)
+{
+ char *tmp, *s = NULL;
+ int i = 0;
+
+ tmp = strdup(str);
+ if (tmp == NULL)
+ return -ENOMEM;
+
+ s = strtok(tmp, ",");
+ while (s != NULL) {
+ s = strtok(NULL, ",");
+ i++;
+ }
+
+ free(tmp);
+ return i;
+}
+
+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);
+ size = num * width;
+ priv = elem->data;
+
+ 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.
+ *
+ * SectionData."data name" {
+ *
+ * DataFile "filename"
+ * bytes "0x12,0x34,0x56,0x78"
+ * shorts "0x1122,0x3344,0x5566,0x7788"
+ * words "0xaabbccdd,0x11223344,0x66aa77bb,0xefef1234"
+ * }
+ */
+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;
+ int err = 0;
+ struct tplg_elem *elem;
+
+ elem = tplg_elem_new_common(tplg, cfg, PARSER_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) {
+ fprintf(stderr, "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) {
+ fprintf(stderr, "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) {
+ fprintf(stderr, "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) {
+ fprintf(stderr, "error: failed to parse data words\n");
+ return err;
+ }
+ 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 PARSER_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 PARSER_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 PARSER_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 PARSER_TYPE_DAPM_WIDGET:
+ elem->widget = realloc(elem->widget,
+ elem->size + priv_data_size);
+ if (!elem->widget)
+ return -ENOMEM;
+ priv = &elem->widget->priv;
+ break;
+
+ default:
+ fprintf(stderr, "elem '%s': type %d shall not have private data\n",
+ elem->id, elem->type);
+ return -EINVAL;
+ }
+
+ elem->size += priv_data_size;
+ priv->size = priv_data_size;
+ memcpy(priv->data, ref->data->data, priv_data_size);
+ return 0;
+}
--
2.1.4
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PATCH 07/13] topology: Add private data parser
2015-06-29 16:32 ` [PATCH 07/13] topology: Add private data parser Liam Girdwood
@ 2015-06-29 18:41 ` Takashi Iwai
0 siblings, 0 replies; 16+ messages in thread
From: Takashi Iwai @ 2015-06-29 18:41 UTC (permalink / raw)
To: Liam Girdwood; +Cc: alsa-devel, Mark Brown
At Mon, 29 Jun 2015 17:32:52 +0100,
Liam Girdwood wrote:
>
> 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 | 348 ++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 348 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..b579df1
> --- /dev/null
> +++ b/src/topology/data.c
> @@ -0,0 +1,348 @@
> +/*
> + 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);
> + filename[sizeof(filename)-1] = '\0';
> +
> + fp = fopen(filename, "r");
> + if (fp == NULL) {
> + fprintf(stderr, "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) {
> + fprintf(stderr, "error: invalid data file size %zu\n", size);
> + ret = -EINVAL;
> + goto err;
> + }
> +
> + priv = calloc(1, sizeof(*priv) + size);
Isn't it better to have some sane value check before allocation?
You may pass a GB size data wrongly.
Takashi
> + 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");
> +}
> +
> +static int get_hex_num(const char *str)
> +{
> + char *tmp, *s = NULL;
> + int i = 0;
> +
> + tmp = strdup(str);
> + if (tmp == NULL)
> + return -ENOMEM;
> +
> + s = strtok(tmp, ",");
> + while (s != NULL) {
> + s = strtok(NULL, ",");
> + i++;
> + }
> +
> + free(tmp);
> + return i;
> +}
> +
> +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;
This doesn't look portable in regard of endianess.
Takashi
> + 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);
> + size = num * width;
> + priv = elem->data;
> +
> + 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.
> + *
> + * SectionData."data name" {
> + *
> + * DataFile "filename"
> + * bytes "0x12,0x34,0x56,0x78"
> + * shorts "0x1122,0x3344,0x5566,0x7788"
> + * words "0xaabbccdd,0x11223344,0x66aa77bb,0xefef1234"
> + * }
> + */
> +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;
> + int err = 0;
> + struct tplg_elem *elem;
> +
> + elem = tplg_elem_new_common(tplg, cfg, PARSER_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) {
> + fprintf(stderr, "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) {
> + fprintf(stderr, "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) {
> + fprintf(stderr, "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) {
> + fprintf(stderr, "error: failed to parse data words\n");
> + return err;
> + }
> + 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 PARSER_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 PARSER_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 PARSER_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 PARSER_TYPE_DAPM_WIDGET:
> + elem->widget = realloc(elem->widget,
> + elem->size + priv_data_size);
> + if (!elem->widget)
> + return -ENOMEM;
> + priv = &elem->widget->priv;
> + break;
> +
> + default:
> + fprintf(stderr, "elem '%s': type %d shall not have private data\n",
> + elem->id, elem->type);
> + return -EINVAL;
> + }
> +
> + elem->size += priv_data_size;
> + priv->size = priv_data_size;
> + memcpy(priv->data, ref->data->data, priv_data_size);
> + return 0;
> +}
> --
> 2.1.4
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 08/13] topology: Add DAPM object parser
2015-06-29 16:32 [PATCH 01/13] topology: uapi: Add UAPI headers for topology ABI Liam Girdwood
` (5 preceding siblings ...)
2015-06-29 16:32 ` [PATCH 07/13] topology: Add private data parser Liam Girdwood
@ 2015-06-29 16:32 ` Liam Girdwood
2015-06-29 18:43 ` Takashi Iwai
2015-06-29 16:32 ` [PATCH 09/13] topology: Add CTL parser Liam Girdwood
` (4 subsequent siblings)
11 siblings, 1 reply; 16+ messages in thread
From: Liam Girdwood @ 2015-06-29 16:32 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 | 560 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 560 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..fafe3cb
--- /dev/null
+++ b/src/topology/dapm.c
@@ -0,0 +1,560 @@
+/*
+ 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, PARSER_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, PARSER_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 == PARSER_TYPE_MIXER)
+ memcpy((void*)widget + elem->size, mixer_ctrl, ref->size);
+ else if (ref->type == PARSER_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, *npos;
+ int err = 0;
+
+ base = &elem->ref_list;
+
+ /* for each ref in this control elem */
+ list_for_each_safe(pos, npos, base) {
+
+ ref = list_entry(pos, struct tplg_ref, list);
+ if (ref->id == NULL || ref->elem)
+ continue;
+
+ switch (ref->type) {
+ case PARSER_TYPE_MIXER:
+ ref->elem = tplg_elem_lookup(&tplg->mixer_list,
+ ref->id, PARSER_TYPE_MIXER);
+ if (ref->elem)
+ err = copy_dapm_control(elem, ref->elem);
+ break;
+
+ case PARSER_TYPE_ENUM:
+ ref->elem = tplg_elem_lookup(&tplg->enum_list,
+ ref->id, PARSER_TYPE_ENUM);
+ if (ref->elem)
+ err = copy_dapm_control(elem, ref->elem);
+ break;
+
+ case PARSER_TYPE_DATA:
+ ref->elem = tplg_elem_lookup(&tplg->pdata_list,
+ ref->id, PARSER_TYPE_DATA);
+ if (ref->elem)
+ err = tplg_copy_data(elem, ref->elem);
+ break;
+ default:
+ break;
+ }
+
+ if (!ref->elem) {
+ fprintf(stderr, "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, *npos;
+ struct tplg_elem *elem;
+ int err;
+
+ base = &tplg->widget_list;
+ list_for_each_safe(pos, npos, base) {
+
+ elem = list_entry(pos, struct tplg_elem, list);
+ if (!elem->widget || elem->type != PARSER_TYPE_DAPM_WIDGET) {
+ fprintf(stderr, "error: invalid widget '%s'\n",
+ elem->id);
+ return -EINVAL;
+ }
+
+ err = tplg_build_widget(tplg, elem);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+int tplg_build_routes(snd_tplg_t *tplg)
+{
+ struct list_head *base, *pos, *npos;
+ struct tplg_elem *elem;
+ struct snd_soc_tplg_dapm_graph_elem *route;
+
+ base = &tplg->route_list;
+
+ list_for_each_safe(pos, npos, base) {
+ elem = list_entry(pos, struct tplg_elem, list);
+
+ if (!elem->route || elem->type != PARSER_TYPE_DAPM_GRAPH) {
+ fprintf(stderr, "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) {
+ fprintf(stderr, "error: no sink\n");
+ return -EINVAL;
+
+ }
+ if (!tplg_elem_lookup(&tplg->widget_list, route->sink,
+ PARSER_TYPE_DAPM_WIDGET)) {
+ fprintf(stderr, "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, PARSER_TYPE_MIXER) &&
+ !tplg_elem_lookup(&tplg->enum_list,
+ route->control, PARSER_TYPE_ENUM)) {
+ fprintf(stderr, "warning: Undefined mixer/enum control '%s'\n",
+ route->control);
+ }
+ }
+
+ /* validate source */
+ if (strlen(route->source) <= 0) {
+ fprintf(stderr, "error: no source\n");
+ return -EINVAL;
+
+ }
+ if (!tplg_elem_lookup(&tplg->widget_list, route->source,
+ PARSER_TYPE_DAPM_WIDGET)) {
+ fprintf(stderr, "warning: Undefined source widget/stream '%s'\n",
+ route->source);
+ }
+ }
+
+ 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;
+
+ strncpy(buf, text, LINE_SIZE);
+
+ len = strlen(buf);
+ if (len <= 2) {
+ fprintf(stderr, "error: invalid route \"%s\"\n", buf);
+ return -EINVAL;
+ }
+
+ /* find first , */
+ for (i = 1; i < len; i++) {
+ if (buf[i] == ',')
+ goto second;
+ }
+ fprintf(stderr, "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;
+ }
+
+ fprintf(stderr, "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 = PARSER_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) {
+ fprintf(stderr, "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) {
+ fprintf(stderr, "error: failed to parse dapm graph %s\n",
+ graph_id);
+ return err;
+ }
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/* DAPM Widget.
+ *
+ * SectionWidget."widget name" {
+ *
+ * index "1"
+ * type "aif_in"
+ * no_pm "true"
+ * shift "0"
+ * invert "1
+ * mixer "name"
+ * enum "name"
+ * data "name"
+ * }
+ */
+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, PARSER_TYPE_DAPM_WIDGET);
+ if (!elem)
+ return -ENOMEM;
+
+ tplg_dbg(" Widget: %s\n", elem->id);
+
+ widget = elem->widget;
+ strncpy(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){
+ fprintf(stderr, "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, "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, PARSER_TYPE_DATA, val);
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+ }
+ }
+
+ return 0;
+}
--
2.1.4
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PATCH 08/13] topology: Add DAPM object parser
2015-06-29 16:32 ` [PATCH 08/13] topology: Add DAPM object parser Liam Girdwood
@ 2015-06-29 18:43 ` Takashi Iwai
0 siblings, 0 replies; 16+ messages in thread
From: Takashi Iwai @ 2015-06-29 18:43 UTC (permalink / raw)
To: Liam Girdwood; +Cc: alsa-devel, Mark Brown
At Mon, 29 Jun 2015 17:32:53 +0100,
Liam Girdwood wrote:
>
> Parse DAPM objects including widgets and graph elements.
>
> Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
> ---
> src/topology/dapm.c | 560 ++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 560 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..fafe3cb
> --- /dev/null
> +++ b/src/topology/dapm.c
> @@ -0,0 +1,560 @@
> +/*
> + 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, PARSER_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, PARSER_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 == PARSER_TYPE_MIXER)
> + memcpy((void*)widget + elem->size, mixer_ctrl, ref->size);
> + else if (ref->type == PARSER_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, *npos;
> + int err = 0;
> +
> + base = &elem->ref_list;
> +
> + /* for each ref in this control elem */
> + list_for_each_safe(pos, npos, base) {
> +
> + ref = list_entry(pos, struct tplg_ref, list);
> + if (ref->id == NULL || ref->elem)
> + continue;
> +
> + switch (ref->type) {
> + case PARSER_TYPE_MIXER:
> + ref->elem = tplg_elem_lookup(&tplg->mixer_list,
> + ref->id, PARSER_TYPE_MIXER);
> + if (ref->elem)
> + err = copy_dapm_control(elem, ref->elem);
> + break;
> +
> + case PARSER_TYPE_ENUM:
> + ref->elem = tplg_elem_lookup(&tplg->enum_list,
> + ref->id, PARSER_TYPE_ENUM);
> + if (ref->elem)
> + err = copy_dapm_control(elem, ref->elem);
> + break;
> +
> + case PARSER_TYPE_DATA:
> + ref->elem = tplg_elem_lookup(&tplg->pdata_list,
> + ref->id, PARSER_TYPE_DATA);
> + if (ref->elem)
> + err = tplg_copy_data(elem, ref->elem);
> + break;
> + default:
> + break;
> + }
> +
> + if (!ref->elem) {
> + fprintf(stderr, "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, *npos;
> + struct tplg_elem *elem;
> + int err;
> +
> + base = &tplg->widget_list;
> + list_for_each_safe(pos, npos, base) {
> +
> + elem = list_entry(pos, struct tplg_elem, list);
> + if (!elem->widget || elem->type != PARSER_TYPE_DAPM_WIDGET) {
> + fprintf(stderr, "error: invalid widget '%s'\n",
> + elem->id);
> + return -EINVAL;
> + }
> +
> + err = tplg_build_widget(tplg, elem);
> + if (err < 0)
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +int tplg_build_routes(snd_tplg_t *tplg)
> +{
> + struct list_head *base, *pos, *npos;
> + struct tplg_elem *elem;
> + struct snd_soc_tplg_dapm_graph_elem *route;
> +
> + base = &tplg->route_list;
> +
> + list_for_each_safe(pos, npos, base) {
> + elem = list_entry(pos, struct tplg_elem, list);
> +
> + if (!elem->route || elem->type != PARSER_TYPE_DAPM_GRAPH) {
> + fprintf(stderr, "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) {
> + fprintf(stderr, "error: no sink\n");
> + return -EINVAL;
> +
> + }
> + if (!tplg_elem_lookup(&tplg->widget_list, route->sink,
> + PARSER_TYPE_DAPM_WIDGET)) {
> + fprintf(stderr, "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, PARSER_TYPE_MIXER) &&
> + !tplg_elem_lookup(&tplg->enum_list,
> + route->control, PARSER_TYPE_ENUM)) {
> + fprintf(stderr, "warning: Undefined mixer/enum control '%s'\n",
> + route->control);
> + }
> + }
> +
> + /* validate source */
> + if (strlen(route->source) <= 0) {
> + fprintf(stderr, "error: no source\n");
> + return -EINVAL;
> +
> + }
> + if (!tplg_elem_lookup(&tplg->widget_list, route->source,
> + PARSER_TYPE_DAPM_WIDGET)) {
> + fprintf(stderr, "warning: Undefined source widget/stream '%s'\n",
> + route->source);
> + }
> + }
> +
> + 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;
> +
> + strncpy(buf, text, LINE_SIZE);
strncpy() doesn't terminate with NUL-character.
You have to put manually
buf[LINE_SIZE - 1] = 0;
after strncpy() call.
Unfortunately there is no strlcpy() in user-space...
Takashi
> +
> + len = strlen(buf);
> + if (len <= 2) {
> + fprintf(stderr, "error: invalid route \"%s\"\n", buf);
> + return -EINVAL;
> + }
> +
> + /* find first , */
> + for (i = 1; i < len; i++) {
> + if (buf[i] == ',')
> + goto second;
> + }
> + fprintf(stderr, "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;
> + }
> +
> + fprintf(stderr, "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 = PARSER_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) {
> + fprintf(stderr, "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) {
> + fprintf(stderr, "error: failed to parse dapm graph %s\n",
> + graph_id);
> + return err;
> + }
> + continue;
> + }
> + }
> +
> + return 0;
> +}
> +
> +/* DAPM Widget.
> + *
> + * SectionWidget."widget name" {
> + *
> + * index "1"
> + * type "aif_in"
> + * no_pm "true"
> + * shift "0"
> + * invert "1
> + * mixer "name"
> + * enum "name"
> + * data "name"
> + * }
> + */
> +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, PARSER_TYPE_DAPM_WIDGET);
> + if (!elem)
> + return -ENOMEM;
> +
> + tplg_dbg(" Widget: %s\n", elem->id);
> +
> + widget = elem->widget;
> + strncpy(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){
> + fprintf(stderr, "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, "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, PARSER_TYPE_DATA, val);
> + tplg_dbg("\t%s: %s\n", id, val);
> + continue;
> + }
> + }
> +
> + return 0;
> +}
> --
> 2.1.4
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 09/13] topology: Add CTL parser
2015-06-29 16:32 [PATCH 01/13] topology: uapi: Add UAPI headers for topology ABI Liam Girdwood
` (6 preceding siblings ...)
2015-06-29 16:32 ` [PATCH 08/13] topology: Add DAPM object parser Liam Girdwood
@ 2015-06-29 16:32 ` Liam Girdwood
2015-06-29 16:32 ` [PATCH 10/13] topology: Add Channel map parser Liam Girdwood
` (3 subsequent siblings)
11 siblings, 0 replies; 16+ messages in thread
From: Liam Girdwood @ 2015-06-29 16:32 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 | 659 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 659 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..b0ed008
--- /dev/null
+++ b/src/topology/ctl.c
@@ -0,0 +1,659 @@
+/*
+ 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 */
+ memcpy(&mixer_ctrl->tlv, tlv, sizeof(*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, *npos;
+ int err = 0;
+
+ base = &elem->ref_list;
+
+ /* for each ref in this control elem */
+ list_for_each_safe(pos, npos, base) {
+
+ ref = list_entry(pos, struct tplg_ref, list);
+ if (ref->id == NULL || ref->elem)
+ continue;
+
+ if (ref->type == PARSER_TYPE_TLV) {
+ ref->elem = tplg_elem_lookup(&tplg->tlv_list,
+ ref->id, PARSER_TYPE_TLV);
+ if (ref->elem)
+ err = copy_tlv(elem, ref->elem);
+
+ } else if (ref->type == PARSER_TYPE_DATA) {
+ ref->elem = tplg_elem_lookup(&tplg->pdata_list,
+ ref->id, PARSER_TYPE_DATA);
+ err = tplg_copy_data(elem, ref->elem);
+ }
+
+ if (!ref->elem) {
+ fprintf(stderr, "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, *npos;
+ int err = 0;
+
+ base = &elem->ref_list;
+
+ list_for_each_safe(pos, npos, base) {
+
+ ref = list_entry(pos, struct tplg_ref, list);
+ if (ref->id == NULL || ref->elem)
+ continue;
+
+ if (ref->type == PARSER_TYPE_TEXT) {
+ ref->elem = tplg_elem_lookup(&tplg->text_list,
+ ref->id, PARSER_TYPE_TEXT);
+ if (ref->elem)
+ copy_enum_texts(elem, ref->elem);
+
+ } else if (ref->type == PARSER_TYPE_DATA) {
+ ref->elem = tplg_elem_lookup(&tplg->pdata_list,
+ ref->id, PARSER_TYPE_DATA);
+ err = tplg_copy_data(elem, ref->elem);
+ }
+ if (!ref->elem) {
+ fprintf(stderr, "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, *npos;
+
+ base = &elem->ref_list;
+
+ list_for_each_safe(pos, npos, 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, PARSER_TYPE_DATA);
+ if (!ref->elem) {
+ fprintf(stderr, "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, *npos;
+ struct tplg_elem *elem;
+ int err = 0;
+
+ base = &tplg->mixer_list;
+ list_for_each_safe(pos, npos, base) {
+
+ elem = list_entry(pos, struct tplg_elem, list);
+ err = tplg_build_mixer_control(tplg, elem);
+ if (err < 0)
+ return err;
+ }
+
+ base = &tplg->enum_list;
+ list_for_each_safe(pos, npos, base) {
+
+ elem = list_entry(pos, struct tplg_elem, list);
+ err = tplg_build_enum_control(tplg, elem);
+ if (err < 0)
+ return err;
+ }
+
+ base = &tplg->bytes_ext_list;
+ list_for_each_safe(pos, npos, base) {
+
+ elem = list_entry(pos, struct tplg_elem, list);
+ err = tplg_build_bytes_control(tplg, elem);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Parse TLV of DBScale type.
+ *
+ * Parse DBScale describing min, step, mute in DB.
+ *
+ * scale {
+ * min "-9000"
+ * step "300"
+ * mute "1"
+ * }
+ */
+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) {
+ fprintf(stderr, "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
+ fprintf(stderr, "error: unknown key %s\n", id);
+ }
+
+ return 0;
+}
+
+/* Parse TLV.
+ *
+ * Each TLV is described in new section
+ * Supported TLV types: scale.
+ *
+ * SectionTLV."tlv name" {
+ * type {
+ *
+ * }
+ * }
+ */
+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, PARSER_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) {
+ fprintf(stderr, "error: failed to DBScale");
+ return err;
+ }
+ continue;
+ }
+ }
+
+ return err;
+}
+
+/* Parse Control Bytes
+ *
+ * Each Control is described in new section
+ * Supported control types: Byte
+ *
+ * SectionControlBytes."control name" {
+ * comment "optional comments"
+ *
+ * index "1"
+ * base "0"
+ * num_regs "16"
+ * mask "0xff"
+ * max "255"
+ * }
+ */
+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;
+
+ elem = tplg_elem_new_common(tplg, cfg, PARSER_TYPE_BYTES);
+ if (!elem)
+ return -ENOMEM;
+
+ be = elem->bytes_ext;
+ be->size = elem->size;
+ strncpy(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, PARSER_TYPE_DATA, val);
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/* Parse Control Enums.
+ *
+ * Enumerated control. Supports mutiple channels.
+ *
+ * SectionControlMixer."control name" {
+ * comment "optional comments"
+ *
+ * index "1"
+ * texts "EQU1"
+ *
+ * channel."name" {
+ * }
+ *
+ * ops."ctl" {
+ * }
+ *
+ * tlv "hsw_vol_tlv"
+ * }
+ */
+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, PARSER_TYPE_ENUM);
+ if (!elem)
+ return -ENOMEM;
+
+ /* init new mixer */
+ ec = elem->enum_ctrl;
+ strncpy(ec->hdr.name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+ ec->hdr.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ 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, PARSER_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) {
+ fprintf(stderr, "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, PARSER_TYPE_DATA, val);
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/* Parse Controls.
+ *
+ * Mixer control. Supports multiple channels.
+ *
+ * SectionControlMixer."control name" {
+ * comment "optional comments"
+ *
+ * index "1"
+ *
+ * channel."name" {
+ * }
+ *
+ * ops."ctl" {
+ * }
+ *
+ * max "32"
+ * invert "0"
+ *
+ * ops."ctl" {
+ * }
+ *
+ * tlv "hsw_vol_tlv"
+ * }
+ */
+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, PARSER_TYPE_MIXER);
+ if (!elem)
+ return -ENOMEM;
+
+ /* init new mixer */
+ mc = elem->mixer_ctrl;
+ strncpy(mc->hdr.name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+ mc->hdr.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ 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) {
+ fprintf(stderr, "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, PARSER_TYPE_TLV, val);
+ if (err < 0)
+ return err;
+
+ 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, PARSER_TYPE_DATA, val);
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+ }
+ }
+
+ return 0;
+}
--
2.1.4
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH 10/13] topology: Add Channel map parser.
2015-06-29 16:32 [PATCH 01/13] topology: uapi: Add UAPI headers for topology ABI Liam Girdwood
` (7 preceding siblings ...)
2015-06-29 16:32 ` [PATCH 09/13] topology: Add CTL parser Liam Girdwood
@ 2015-06-29 16:32 ` Liam Girdwood
2015-06-29 16:32 ` [PATCH 11/13] topology: Add binary file builder Liam Girdwood
` (2 subsequent siblings)
11 siblings, 0 replies; 16+ messages in thread
From: Liam Girdwood @ 2015-06-29 16:32 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 | 128 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 128 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..e33c70d
--- /dev/null
+++ b/src/topology/channel.c
@@ -0,0 +1,128 @@
+/*
+ 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.
+ *
+ * channel."channel_map.name" {
+ * reg "0" (register)
+ * shift "0" (shift)
+ * }
+ */
+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) {
+ fprintf(stderr, "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] 16+ messages in thread* [PATCH 11/13] topology: Add binary file builder.
2015-06-29 16:32 [PATCH 01/13] topology: uapi: Add UAPI headers for topology ABI Liam Girdwood
` (8 preceding siblings ...)
2015-06-29 16:32 ` [PATCH 10/13] topology: Add Channel map parser Liam Girdwood
@ 2015-06-29 16:32 ` Liam Girdwood
2015-06-29 16:32 ` [PATCH 12/13] topology: autotools: Add build support for topology core Liam Girdwood
2015-06-29 16:32 ` [PATCH 13/13] topology: doxygen: Add doxygen " Liam Girdwood
11 siblings, 0 replies; 16+ messages in thread
From: Liam Girdwood @ 2015-06-29 16:32 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 | 275 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 275 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..12adc81
--- /dev/null
+++ b/src/topology/builder.c
@@ -0,0 +1,275 @@
+/*
+ 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) {
+ fprintf(stderr, "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)) {
+ fprintf(stderr, "error: can't write section header %lu\n",
+ (long unsigned int)bytes);
+ return bytes;
+ }
+
+ return bytes;
+}
+
+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, *npos;
+ struct tplg_elem *elem;
+ int ret, wsize = 0, count = 0;
+
+ /* count number of elements */
+ list_for_each_safe(pos, npos, base)
+ count++;
+
+ /* write the header for this block */
+ ret = write_block_header(tplg, tplg_type, 0,
+ SND_SOC_TPLG_ABI_VERSION, 0, size, count);
+ if (ret < 0) {
+ fprintf(stderr, "error: failed to write %s block %d\n",
+ obj_name, ret);
+ return ret;
+ }
+
+ /* write each elem to block */
+ list_for_each_safe(pos, npos, 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 != PARSER_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) {
+ fprintf(stderr, "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) {
+ fprintf(stderr, "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, *npos;
+ struct tplg_elem *elem;
+ int size = 0;
+
+ list_for_each_safe(pos, npos, 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 PARSER_TYPE_MIXER:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_MIXER, "mixer");
+ case PARSER_TYPE_BYTES:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_BYTES, "bytes");
+ case PARSER_TYPE_ENUM:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_ENUM, "enum");
+ case PARSER_TYPE_DAPM_GRAPH:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_DAPM_GRAPH, "route");
+ case PARSER_TYPE_DAPM_WIDGET:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_DAPM_WIDGET, "widget");
+ case PARSER_TYPE_PCM:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_PCM, "pcm");
+ case PARSER_TYPE_BE:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_DAI_LINK, "be");
+ case PARSER_TYPE_CC:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_DAI_LINK, "cc");
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int tplg_write_data(snd_tplg_t *tplg)
+{
+ int ret;
+
+ /* write mixer elems. */
+ ret = write_block(tplg, &tplg->mixer_list,
+ PARSER_TYPE_MIXER);
+ if (ret < 0) {
+ fprintf(stderr, "failed to write control elems %d\n", ret);
+ return ret;
+ }
+
+ /* write enum control elems. */
+ ret = write_block(tplg, &tplg->enum_list,
+ PARSER_TYPE_ENUM);
+ if (ret < 0) {
+ fprintf(stderr, "failed to write control elems %d\n", ret);
+ return ret;
+ }
+
+ /* write bytes extended control elems. */
+ ret = write_block(tplg, &tplg->bytes_ext_list,
+ PARSER_TYPE_BYTES);
+ if (ret < 0) {
+ fprintf(stderr, "failed to write control elems %d\n", ret);
+ return ret;
+ }
+
+ /* write widget elems */
+ ret = write_block(tplg, &tplg->widget_list,
+ PARSER_TYPE_DAPM_WIDGET);
+ if (ret < 0) {
+ fprintf(stderr, "failed to write widget elems %d\n", ret);
+ return ret;
+ }
+
+ /* write pcm elems */
+ ret = write_block(tplg, &tplg->pcm_list,
+ PARSER_TYPE_PCM);
+ if (ret < 0) {
+ fprintf(stderr, "failed to write pcm elems %d\n", ret);
+ return ret;
+ }
+
+ /* write be elems */
+ ret = write_block(tplg, &tplg->be_list,
+ PARSER_TYPE_BE);
+ if (ret < 0) {
+ fprintf(stderr, "failed to write be elems %d\n", ret);
+ return ret;
+ }
+
+ /* write cc elems */
+ ret = write_block(tplg, &tplg->cc_list,
+ PARSER_TYPE_CC);
+ if (ret < 0) {
+ fprintf(stderr, "failed to write cc elems %d\n", ret);
+ return ret;
+ }
+
+ /* write route elems */
+ ret = write_block(tplg, &tplg->route_list,
+ PARSER_TYPE_DAPM_GRAPH);
+ if (ret < 0) {
+ fprintf(stderr, "failed to write graph elems %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
--
2.1.4
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH 12/13] topology: autotools: Add build support for topology core
2015-06-29 16:32 [PATCH 01/13] topology: uapi: Add UAPI headers for topology ABI Liam Girdwood
` (9 preceding siblings ...)
2015-06-29 16:32 ` [PATCH 11/13] topology: Add binary file builder Liam Girdwood
@ 2015-06-29 16:32 ` Liam Girdwood
2015-06-29 16:32 ` [PATCH 13/13] topology: doxygen: Add doxygen " Liam Girdwood
11 siblings, 0 replies; 16+ messages in thread
From: Liam Girdwood @ 2015-06-29 16:32 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] 16+ messages in thread* [PATCH 13/13] topology: doxygen: Add doxygen support for topology core.
2015-06-29 16:32 [PATCH 01/13] topology: uapi: Add UAPI headers for topology ABI Liam Girdwood
` (10 preceding siblings ...)
2015-06-29 16:32 ` [PATCH 12/13] topology: autotools: Add build support for topology core Liam Girdwood
@ 2015-06-29 16:32 ` Liam Girdwood
11 siblings, 0 replies; 16+ messages in thread
From: Liam Girdwood @ 2015-06-29 16:32 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] 16+ messages in thread