Accepting request 320497 from multimedia:libs

1

OBS-URL: https://build.opensuse.org/request/show/320497
OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/alsa?expand=0&rev=163
This commit is contained in:
Dominique Leuenberger 2015-08-21 05:37:17 +00:00 committed by Git OBS Bridge
commit ec58dcef52
29 changed files with 7219 additions and 2 deletions

View File

@ -0,0 +1,63 @@
From bbe9248e6788e7a852af15f2110dbaeace4d1907 Mon Sep 17 00:00:00 2001
From: David Henningsson <david.henningsson@canonical.com>
Date: Thu, 18 Jun 2015 10:47:59 +0200
Subject: [PATCH] surround41/50.conf: Use chmap syntax for better flexibility
In case the hardware only supports a specific channel map,
this change would allow surround41/50 to select the correct
channel map and channel count in this situation.
Signed-off-by: David Henningsson <david.henningsson@canonical.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
src/conf/pcm/surround41.conf | 11 +++++------
src/conf/pcm/surround50.conf | 11 +++++------
2 files changed, 10 insertions(+), 12 deletions(-)
diff --git a/src/conf/pcm/surround41.conf b/src/conf/pcm/surround41.conf
index 10e486e21528..2f823815821a 100644
--- a/src/conf/pcm/surround41.conf
+++ b/src/conf/pcm/surround41.conf
@@ -53,12 +53,11 @@ pcm.!surround41 {
]
}
}
- slave.channels 6
- ttable.0.0 1
- ttable.1.1 1
- ttable.2.2 1
- ttable.3.3 1
- ttable.4.5 1
+ ttable.0.FL 1
+ ttable.1.FR 1
+ ttable.2.RL 1
+ ttable.3.RR 1
+ ttable.4.LFE 1
hint {
description "4.1 Surround output to Front, Rear and Subwoofer speakers"
device $DEV
diff --git a/src/conf/pcm/surround50.conf b/src/conf/pcm/surround50.conf
index 7b7b17e10246..dc95c179da68 100644
--- a/src/conf/pcm/surround50.conf
+++ b/src/conf/pcm/surround50.conf
@@ -53,12 +53,11 @@ pcm.!surround50 {
]
}
}
- slave.channels 6
- ttable.0.0 1
- ttable.1.1 1
- ttable.2.2 1
- ttable.3.3 1
- ttable.4.4 1
+ ttable.0.FL 1
+ ttable.1.FR 1
+ ttable.2.RL 1
+ ttable.3.RR 1
+ ttable.4.FC 1
hint {
description "5.0 Surround output to Front, Center and Rear speakers"
device $DEV
--
2.5.0

View File

@ -0,0 +1,28 @@
From f66c7cc293b13765ffd67f530008e836f8354b13 Mon Sep 17 00:00:00 2001
From: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Date: Mon, 29 Jun 2015 16:25:57 +0100
Subject: [PATCH 24/49] ucm: docs: fix doxygen exclude patch for UCM local
header
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
doc/doxygen.cfg.in | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/doxygen.cfg.in b/doc/doxygen.cfg.in
index f4499d610290..043e75b2d7eb 100644
--- a/doc/doxygen.cfg.in
+++ b/doc/doxygen.cfg.in
@@ -94,7 +94,7 @@ 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/seq/ucm_local.h
+ @top_srcdir@/src/ucm/ucm_local.h
RECURSIVE = YES
FILE_PATTERNS = *.c *.h
EXAMPLE_PATH = @top_srcdir@/test
--
2.5.0

View File

@ -0,0 +1,69 @@
From bb92545e064c3e2549878a328277eebe343aae61 Mon Sep 17 00:00:00 2001
From: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Date: Mon, 29 Jun 2015 16:25:58 +0100
Subject: [PATCH 25/49] ucm: docs: Fix doxygen formatting for UCM main page.
Make sure group is defined and lists dipplayed correctly.
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
include/use-case.h | 20 +++++++++-----------
1 file changed, 9 insertions(+), 11 deletions(-)
diff --git a/include/use-case.h b/include/use-case.h
index c7789c03c4e8..9aac6e2fb51d 100644
--- a/include/use-case.h
+++ b/include/use-case.h
@@ -43,15 +43,13 @@ extern "C" {
#endif
/**
- * \defgroup Use Case Interface
+ * \defgroup ucm Use Case Interface
* The ALSA Use Case manager interface.
* See \ref Usecase page for more details.
* \{
*/
/*! \page Usecase ALSA Use Case Interface
- *
- * ALSA Use Case Interface
*
* The use case manager works by configuring the sound card ALSA kcontrols to
* change the hardware digital and analog audio routing to match the requested
@@ -69,9 +67,9 @@ extern "C" {
*
* However there are times when a use case has to be modified at runtime. e.g.
*
- * o Incoming phone call when the device is playing music
- * o Recording sections of a phone call
- * o Playing tones during a call.
+ * + Incoming phone call when the device is playing music
+ * + Recording sections of a phone call
+ * + Playing tones during a call.
*
* In order to allow asynchronous runtime use case adaptations, we have a third
* optional modifier parameter that can be used to further configure
@@ -79,13 +77,13 @@ extern "C" {
*
* This interface allows clients to :-
*
- * o Query the supported use case verbs, devices and modifiers for the machine.
- * o Set and Get use case verbs, devices and modifiers for the machine.
- * o Get the ALSA PCM playback and capture device PCMs for use case verb,
+ * + Query the supported use case verbs, devices and modifiers for the machine.
+ * + Set and Get use case verbs, devices and modifiers for the machine.
+ * + Get the ALSA PCM playback and capture device PCMs for use case verb,
* use case device and modifier.
- * o Get the TQ parameter for each use case verb, use case device and
+ * + Get the TQ parameter for each use case verb, use case device and
* modifier.
- * o Get the ALSA master playback and capture volume/switch kcontrols
+ * + Get the ALSA master playback and capture volume/switch kcontrols
* for each use case.
*/
--
2.5.0

View File

@ -0,0 +1,26 @@
From c6df8273746645b2c5109537b07af6ed33d268d1 Mon Sep 17 00:00:00 2001
From: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Date: Mon, 29 Jun 2015 16:25:59 +0100
Subject: [PATCH 26/49] docs: Add UCM link to main doxygen page.
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
doc/index.doxygen | 1 +
1 file changed, 1 insertion(+)
diff --git a/doc/index.doxygen b/doc/index.doxygen
index 45aa68a11caa..7d049fe5c32a 100644
--- a/doc/index.doxygen
+++ b/doc/index.doxygen
@@ -40,6 +40,7 @@ may be placed in the library code instead of the kernel driver.</P>
<LI>Page \ref rawmidi explains the design of the RawMidi API.
<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.
</UL>
<H2>Configuration</H2>
--
2.5.0

View File

@ -0,0 +1,105 @@
From 4dc44bb34aab2b23ab45c8e61e4b17bf2cf58959 Mon Sep 17 00:00:00 2001
From: "Alexander E. Patrakov" <patrakov@gmail.com>
Date: Mon, 29 Jun 2015 22:53:53 +0500
Subject: [PATCH 27/49] Replace unsafe characters with _ in card name
Otherwise, they get misinterpreted as argument separators
in USB-Audio PCM definitions, and thus prevent SPDIF blacklist entries
from working.
While at it, add my Logitec C910 webcam to the SPDIF blacklist.
Signed-off-by: Alexander E. Patrakov <patrakov@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
include/conf.h | 1 +
src/conf.c | 32 ++++++++++++++++++++++++++++++++
src/conf/cards/USB-Audio.conf | 3 ++-
src/confmisc.c | 2 +-
4 files changed, 36 insertions(+), 2 deletions(-)
diff --git a/include/conf.h b/include/conf.h
index ff270f617383..087c05dc6bcf 100644
--- a/include/conf.h
+++ b/include/conf.h
@@ -126,6 +126,7 @@ int snd_config_imake_integer(snd_config_t **config, const char *key, const long
int snd_config_imake_integer64(snd_config_t **config, const char *key, const long long value);
int snd_config_imake_real(snd_config_t **config, const char *key, const double value);
int snd_config_imake_string(snd_config_t **config, const char *key, const char *ascii);
+int snd_config_imake_safe_string(snd_config_t **config, const char *key, const char *ascii);
int snd_config_imake_pointer(snd_config_t **config, const char *key, const void *ptr);
snd_config_type_t snd_config_get_type(const snd_config_t *config);
diff --git a/src/conf.c b/src/conf.c
index bb256e7ae443..c6a83eef7d2f 100644
--- a/src/conf.c
+++ b/src/conf.c
@@ -2228,6 +2228,38 @@ int snd_config_imake_string(snd_config_t **config, const char *id, const char *v
return 0;
}
+int snd_config_imake_safe_string(snd_config_t **config, const char *id, const char *value)
+{
+ int err;
+ snd_config_t *tmp;
+ char *c;
+
+ err = snd_config_make(&tmp, id, SND_CONFIG_TYPE_STRING);
+ if (err < 0)
+ return err;
+ if (value) {
+ tmp->u.string = strdup(value);
+ if (!tmp->u.string) {
+ snd_config_delete(tmp);
+ return -ENOMEM;
+ }
+
+ for (c = tmp->u.string; *c; c++) {
+ if (*c == ' ' || *c == '-' || *c == '_' ||
+ (*c >= '0' && *c <= '9') ||
+ (*c >= 'a' && *c <= 'z') ||
+ (*c >= 'A' && *c <= 'Z'))
+ continue;
+ *c = '_';
+ }
+ } else {
+ tmp->u.string = NULL;
+ }
+ *config = tmp;
+ return 0;
+}
+
+
/**
* \brief Creates a pointer configuration node with the given initial value.
* \param[out] config The function puts the handle to the new node at
diff --git a/src/conf/cards/USB-Audio.conf b/src/conf/cards/USB-Audio.conf
index 031bee0d86fd..e365f2979e6a 100644
--- a/src/conf/cards/USB-Audio.conf
+++ b/src/conf/cards/USB-Audio.conf
@@ -57,7 +57,8 @@ USB-Audio.pcm.iec958_device {
"Scarlett 2i4 USB" 999
"Sennheiser USB headset" 999
"SWTOR Gaming Headset by Razer" 999
- "USB Device 0x46d:0x992" 999
+ "USB Device 0x46d_0x821" 999
+ "USB Device 0x46d_0x992" 999
}
# Second iec958 device number, if any.
diff --git a/src/confmisc.c b/src/confmisc.c
index 1fb4f282217e..ae0275ff4de3 100644
--- a/src/confmisc.c
+++ b/src/confmisc.c
@@ -935,7 +935,7 @@ int snd_func_card_name(snd_config_t **dst, snd_config_t *root,
}
err = snd_config_get_id(src, &id);
if (err >= 0)
- err = snd_config_imake_string(dst, id,
+ err = snd_config_imake_safe_string(dst, id,
snd_ctl_card_info_get_name(info));
__error:
if (ctl)
--
2.5.0

View File

@ -0,0 +1,84 @@
From 6cb31b444442f8ebca939cd78b80993f2ac85350 Mon Sep 17 00:00:00 2001
From: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Date: Wed, 1 Jul 2015 15:40:54 -0500
Subject: [PATCH 28/49] pcm: add helper functions to query timestamping
capabilities
extend support to link, link_estimated and link_synchronized
timestamp. wall-clock is deprecated
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
include/pcm.h | 3 ++-
src/pcm/pcm.c | 35 ++++++++++++++++++++++++++++++++++-
2 files changed, 36 insertions(+), 2 deletions(-)
diff --git a/include/pcm.h b/include/pcm.h
index 0655e7f43ef6..2aa1eff36be3 100644
--- a/include/pcm.h
+++ b/include/pcm.h
@@ -668,7 +668,8 @@ int snd_pcm_hw_params_is_half_duplex(const snd_pcm_hw_params_t *params);
int snd_pcm_hw_params_is_joint_duplex(const snd_pcm_hw_params_t *params);
int snd_pcm_hw_params_can_sync_start(const snd_pcm_hw_params_t *params);
int snd_pcm_hw_params_can_disable_period_wakeup(const snd_pcm_hw_params_t *params);
-int snd_pcm_hw_params_supports_audio_wallclock_ts(const snd_pcm_hw_params_t *params);
+int snd_pcm_hw_params_supports_audio_wallclock_ts(const snd_pcm_hw_params_t *params); /* deprecated, use audio_ts_type */
+int snd_pcm_hw_params_supports_audio_ts_type(const snd_pcm_hw_params_t *params, int type);
int snd_pcm_hw_params_get_rate_numden(const snd_pcm_hw_params_t *params,
unsigned int *rate_num,
unsigned int *rate_den);
diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c
index bc18954b92da..846d502a6cb1 100644
--- a/src/pcm/pcm.c
+++ b/src/pcm/pcm.c
@@ -3190,12 +3190,45 @@ int snd_pcm_hw_params_can_disable_period_wakeup(const snd_pcm_hw_params_t *param
*/
int snd_pcm_hw_params_supports_audio_wallclock_ts(const snd_pcm_hw_params_t *params)
{
+ /* deprecated */
+ return snd_pcm_hw_params_supports_audio_ts_type(params,
+ SNDRV_PCM_AUDIO_TSTAMP_TYPE_COMPAT);
+}
+
+/**
+ * \brief Check if hardware supports type of audio timestamps
+ * \param params Configuration space
+ * \param type Audio timestamp type
+ * \retval 0 Hardware doesn't support type of audio timestamps
+ * \retval 1 Hardware supports type of audio timestamps
+ *
+ * This function should only be called when the configuration space
+ * contains a single configuration. Call #snd_pcm_hw_params to choose
+ * a single configuration from the configuration space.
+ */
+int snd_pcm_hw_params_supports_audio_ts_type(const snd_pcm_hw_params_t *params, int type)
+{
assert(params);
if (CHECK_SANITY(params->info == ~0U)) {
SNDMSG("invalid PCM info field");
return 0; /* FIXME: should be a negative error? */
}
- return !!(params->info & SNDRV_PCM_INFO_HAS_WALL_CLOCK);
+ switch (type) {
+ case SNDRV_PCM_AUDIO_TSTAMP_TYPE_COMPAT:
+ return !!(params->info & SNDRV_PCM_INFO_HAS_WALL_CLOCK); /* deprecated */
+ case SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT:
+ return 1; /* always supported, based on hw_ptr */
+ case SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK:
+ return !!(params->info & SNDRV_PCM_INFO_HAS_LINK_ATIME);
+ case SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_ABSOLUTE:
+ return !!(params->info & SNDRV_PCM_INFO_HAS_LINK_ABSOLUTE_ATIME);
+ case SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_ESTIMATED:
+ return !!(params->info & SNDRV_PCM_INFO_HAS_LINK_ESTIMATED_ATIME);
+ case SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED:
+ return !!(params->info & SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME);
+ default:
+ return 0;
+ }
}
/**
--
2.5.0

View File

@ -0,0 +1,129 @@
From 6ec2464f397ff401c251057499abea77fd80b60b Mon Sep 17 00:00:00 2001
From: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Date: Wed, 1 Jul 2015 15:40:55 -0500
Subject: [PATCH 29/49] pcm: add support for get/set_audio_htstamp_config
Enable kernel-side functionality by letting user select what sort of
timestamp it desires
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
include/pcm.h | 44 ++++++++++++++++++++++++++++++++++++++++++++
src/pcm/pcm.c | 38 ++++++++++++++++++++++++++++++++++++++
2 files changed, 82 insertions(+)
diff --git a/include/pcm.h b/include/pcm.h
index 2aa1eff36be3..a1d14a989a47 100644
--- a/include/pcm.h
+++ b/include/pcm.h
@@ -330,6 +330,26 @@ typedef enum _snd_pcm_tstamp_type {
SND_PCM_TSTAMP_TYPE_LAST = SND_PCM_TSTAMP_TYPE_MONOTONIC_RAW,
} snd_pcm_tstamp_type_t;
+typedef struct _snd_pcm_audio_tstamp_config {
+ /* 5 of max 16 bits used */
+ unsigned int type_requested:4;
+ unsigned int report_delay:1; /* add total delay to A/D or D/A */
+} snd_pcm_audio_tstamp_config_t;
+
+typedef struct _snd_pcm_audio_tstamp_report {
+ /* 6 of max 16 bits used for bit-fields */
+
+ /* for backwards compatibility */
+ unsigned int valid:1;
+
+ /* actual type if hardware could not support requested timestamp */
+ unsigned int actual_type:4;
+
+ /* accuracy represented in ns units */
+ unsigned int accuracy_report:1; /* 0 if accuracy unknown, 1 if accuracy field is valid */
+ unsigned int accuracy; /* up to 4.29s, will be packed in separate field */
+} snd_pcm_audio_tstamp_report_t;
+
/** Unsigned frames quantity */
typedef unsigned long snd_pcm_uframes_t;
/** Signed frames quantity */
@@ -981,6 +1001,30 @@ void snd_pcm_status_get_trigger_htstamp(const snd_pcm_status_t *obj, snd_htimest
void snd_pcm_status_get_tstamp(const snd_pcm_status_t *obj, snd_timestamp_t *ptr);
void snd_pcm_status_get_htstamp(const snd_pcm_status_t *obj, snd_htimestamp_t *ptr);
void snd_pcm_status_get_audio_htstamp(const snd_pcm_status_t *obj, snd_htimestamp_t *ptr);
+void snd_pcm_status_get_driver_htstamp(const snd_pcm_status_t *obj, snd_htimestamp_t *ptr);
+void snd_pcm_status_get_audio_htstamp_report(const snd_pcm_status_t *obj,
+ snd_pcm_audio_tstamp_report_t *audio_tstamp_report);
+void snd_pcm_status_set_audio_htstamp_config(snd_pcm_status_t *obj,
+ snd_pcm_audio_tstamp_config_t *audio_tstamp_config);
+
+static inline void snd_pcm_pack_audio_tstamp_config(unsigned int *data,
+ snd_pcm_audio_tstamp_config_t *config)
+{
+ *data = config->report_delay;
+ *data <<= 4;
+ *data |= config->type_requested;
+}
+
+static inline void snd_pcm_unpack_audio_tstamp_report(unsigned int data, unsigned int accuracy,
+ snd_pcm_audio_tstamp_report_t *report)
+{
+ data >>= 16;
+ report->valid = data & 1;
+ report->actual_type = (data >> 1) & 0xF;
+ report->accuracy_report = (data >> 5) & 1;
+ report->accuracy = accuracy;
+}
+
snd_pcm_sframes_t snd_pcm_status_get_delay(const snd_pcm_status_t *obj);
snd_pcm_uframes_t snd_pcm_status_get_avail(const snd_pcm_status_t *obj);
snd_pcm_uframes_t snd_pcm_status_get_avail_max(const snd_pcm_status_t *obj);
diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c
index 846d502a6cb1..bae1d1653904 100644
--- a/src/pcm/pcm.c
+++ b/src/pcm/pcm.c
@@ -6367,6 +6367,44 @@ void snd_pcm_status_get_audio_htstamp(const snd_pcm_status_t *obj, snd_htimestam
}
/**
+ * \brief Get "now" hi-res driver timestamp from a PCM status container. Defines when the status
+ * was generated by driver, may differ from normal timestamp.
+ * \param obj pointer to #snd_pcm_status_t
+ * \param ptr Pointer to returned timestamp
+ */
+void snd_pcm_status_get_driver_htstamp(const snd_pcm_status_t *obj, snd_htimestamp_t *ptr)
+{
+ assert(obj && ptr);
+ *ptr = obj->driver_tstamp;
+}
+
+/**
+ * \brief Get audio_tstamp_report from a PCM status container
+ * \param obj pointer to #snd_pcm_status_t
+ * \param ptr Pointer to returned report (valid fields are accuracy and type)
+ */
+void snd_pcm_status_get_audio_htstamp_report(const snd_pcm_status_t *obj,
+ snd_pcm_audio_tstamp_report_t *audio_tstamp_report)
+{
+ assert(obj && audio_tstamp_report);
+ snd_pcm_unpack_audio_tstamp_report(obj->audio_tstamp_data,
+ obj->audio_tstamp_accuracy,
+ audio_tstamp_report);
+}
+
+/**
+ * \brief set audio_tstamp_config from a PCM status container
+ * \param obj pointer to #snd_pcm_status_t
+ * \param ptr Pointer to config (valid fields are type and report_analog_delay)
+ */
+void snd_pcm_status_set_audio_htstamp_config(snd_pcm_status_t *obj,
+ snd_pcm_audio_tstamp_config_t *audio_tstamp_config)
+{
+ assert(obj && audio_tstamp_config);
+ snd_pcm_pack_audio_tstamp_config(&obj->audio_tstamp_data, audio_tstamp_config);
+}
+
+/**
* \brief Get delay from a PCM status container (see #snd_pcm_delay)
* \return Delay in frames
*
--
2.5.0

View File

@ -0,0 +1,45 @@
From cc8b73436a90c35beda64bfa13b2196df20cfd81 Mon Sep 17 00:00:00 2001
From: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Date: Wed, 1 Jul 2015 15:40:56 -0500
Subject: [PATCH 30/49] pcm: add support for new STATUS_EXT ioctl
use STATUS_EXT ioctl if PCM protocol is > 2.0.12
All audio timestamp configuration will be ignored with an
older protocol.
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
src/pcm/pcm_hw.c | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c
index c34b766ee035..232b19736db9 100644
--- a/src/pcm/pcm_hw.c
+++ b/src/pcm/pcm_hw.c
@@ -510,10 +510,18 @@ static int snd_pcm_hw_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
{
snd_pcm_hw_t *hw = pcm->private_data;
int fd = hw->fd, err;
- if (ioctl(fd, SNDRV_PCM_IOCTL_STATUS, status) < 0) {
- err = -errno;
- SYSMSG("SNDRV_PCM_IOCTL_STATUS failed (%i)", err);
- return err;
+ if (SNDRV_PROTOCOL_VERSION(2, 0, 13) > hw->version) {
+ if (ioctl(fd, SNDRV_PCM_IOCTL_STATUS, status) < 0) {
+ err = -errno;
+ SYSMSG("SNDRV_PCM_IOCTL_STATUS failed (%i)", err);
+ return err;
+ }
+ } else {
+ if (ioctl(fd, SNDRV_PCM_IOCTL_STATUS_EXT, status) < 0) {
+ err = -errno;
+ SYSMSG("SNDRV_PCM_IOCTL_STATUS_EXT failed (%i)", err);
+ return err;
+ }
}
if (SNDRV_PROTOCOL_VERSION(2, 0, 5) > hw->version) {
status->tstamp.tv_nsec *= 1000L;
--
2.5.0

View File

@ -0,0 +1,596 @@
From 7bb3a74c4dbcfb01b0b91d94452d994b845b4ff3 Mon Sep 17 00:00:00 2001
From: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Date: Wed, 1 Jul 2015 15:40:57 -0500
Subject: [PATCH 31/49] test: fix audio_time with new get/set
audio_tstamp_config
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
test/audio_time.c | 491 ++++++++++++++++++++++++++++++++++--------------------
1 file changed, 313 insertions(+), 178 deletions(-)
diff --git a/test/audio_time.c b/test/audio_time.c
index 7435db6a7fd8..e369e59b4ff2 100644
--- a/test/audio_time.c
+++ b/test/audio_time.c
@@ -4,13 +4,39 @@
* helpful to verify the information reported by drivers.
*/
-#include "../include/asoundlib.h"
+#include <stdio.h>
+#include <malloc.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <time.h>
+#include <locale.h>
#include <math.h>
+#include "../include/asoundlib.h"
-static char *device = "hw:0,0";
-
+static char *command;
+static char *pcm_name = "hw:0";
snd_output_t *output = NULL;
+static void usage(char *command)
+{
+ printf("Usage: %s [OPTION]... \n"
+ "\n"
+ "-h, --help help\n"
+ "-c, --capture capture tstamps \n"
+ "-d, --delay add delay \n"
+ "-D, --device=NAME select PCM by name \n"
+ "-p, --playback playback tstamps \n"
+ "-t, --ts_type=TYPE Default(0),link(1),link_estimated(2),synchronized(3) \n"
+ , command);
+}
+
+
long long timestamp2ns(snd_htimestamp_t t)
{
long long nsec;
@@ -31,15 +57,20 @@ long long timediff(snd_htimestamp_t t1, snd_htimestamp_t t2)
return nsec1 - nsec2;
}
-void gettimestamp(snd_pcm_t *handle, snd_htimestamp_t *timestamp,
- snd_htimestamp_t *trigger_timestamp,
- snd_htimestamp_t *audio_timestamp,
- snd_pcm_uframes_t *avail, snd_pcm_sframes_t *delay)
+void _gettimestamp(snd_pcm_t *handle, snd_htimestamp_t *timestamp,
+ snd_htimestamp_t *trigger_timestamp,
+ snd_htimestamp_t *audio_timestamp,
+ snd_pcm_audio_tstamp_config_t *audio_tstamp_config,
+ snd_pcm_audio_tstamp_report_t *audio_tstamp_report,
+ snd_pcm_uframes_t *avail, snd_pcm_sframes_t *delay)
{
int err;
snd_pcm_status_t *status;
snd_pcm_status_alloca(&status);
+
+ snd_pcm_status_set_audio_htstamp_config(status, audio_tstamp_config);
+
if ((err = snd_pcm_status(handle, status)) < 0) {
printf("Stream status error: %s\n", snd_strerror(err));
exit(0);
@@ -47,26 +78,30 @@ void gettimestamp(snd_pcm_t *handle, snd_htimestamp_t *timestamp,
snd_pcm_status_get_trigger_htstamp(status, trigger_timestamp);
snd_pcm_status_get_htstamp(status, timestamp);
snd_pcm_status_get_audio_htstamp(status, audio_timestamp);
+ snd_pcm_status_get_audio_htstamp_report(status, audio_tstamp_report);
*avail = snd_pcm_status_get_avail(status);
*delay = snd_pcm_status_get_delay(status);
}
-#define PERIOD 6000
+#define TIMESTAMP_FREQ 8 /* Hz */
+#define SAMPLE_FREQ 48000
+#define PERIOD (SAMPLE_FREQ/TIMESTAMP_FREQ)
#define PCM_LINK /* sync start for playback and capture */
#define TRACK_CAPTURE /* dump capture timing info */
#define TRACK_PLAYBACK /* dump playback timing info */
-#define TRACK_SAMPLE_COUNTS /* show difference between sample counters and audiotimestamps returned by driver */
+/*#define TRACK_SAMPLE_COUNTS */ /* show difference between sample counters and audiotimestamps returned by driver */
#define PLAYBACK_BUFFERS 4
-#define TSTAMP_TYPE SND_PCM_TSTAMP_TYPE_MONOTONIC
+#define TSTAMP_TYPE SND_PCM_TSTAMP_TYPE_MONOTONIC_RAW
-int main(void)
+int main(int argc, char *argv[])
{
- int err;
- unsigned int i;
- snd_pcm_t *handle_p = NULL;
- snd_pcm_t *handle_c = NULL;
- snd_pcm_sframes_t frames;
+ int c;
+ int err;
+ unsigned int i;
+ snd_pcm_t *handle_p = NULL;
+ snd_pcm_t *handle_c = NULL;
+ snd_pcm_sframes_t frames;
snd_htimestamp_t tstamp_c, tstamp_p;
snd_htimestamp_t trigger_tstamp_c, trigger_tstamp_p;
snd_htimestamp_t audio_tstamp_c, audio_tstamp_p;
@@ -87,206 +122,306 @@ int main(void)
snd_pcm_sframes_t delay_p, delay_c;
snd_pcm_uframes_t avail_p, avail_c;
- if ((err = snd_pcm_open(&handle_p, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
- printf("Playback open error: %s\n", snd_strerror(err));
- goto _exit;
- }
- if ((err = snd_pcm_set_params(handle_p,
- SND_PCM_FORMAT_S16,
- SND_PCM_ACCESS_RW_INTERLEAVED,
- 2,
- 48000,
- 0,
- 500000)) < 0) { /* 0.5sec */
- printf("Playback open error: %s\n", snd_strerror(err));
- goto _exit;
+ snd_pcm_audio_tstamp_config_t audio_tstamp_config_p;
+ snd_pcm_audio_tstamp_config_t audio_tstamp_config_c;
+ snd_pcm_audio_tstamp_report_t audio_tstamp_report_p;
+ snd_pcm_audio_tstamp_report_t audio_tstamp_report_c;
+
+ int option_index;
+ static const char short_options[] = "hcpdD:t:";
+
+ static const struct option long_options[] = {
+ {"capture", 0, 0, 'c'},
+ {"delay", 0, 0, 'd'},
+ {"device", required_argument, 0, 'D'},
+ {"help", no_argument, 0, 'h'},
+ {"playback", 0, 0, 'p'},
+ {"ts_type", required_argument, 0, 't'},
+ {0, 0, 0, 0}
+ };
+
+ int do_delay = 0;
+ int do_playback = 0;
+ int do_capture = 0;
+ int type = 0;
+
+ while ((c = getopt_long(argc, argv, short_options, long_options, &option_index)) != -1) {
+ switch (c) {
+ case 'h':
+ usage(command);
+ return 0;
+ case 'p':
+ do_playback = 1;
+ break;
+ case 'c':
+ do_capture = 1;
+ break;
+ case 'd':
+ do_delay = 1;
+ break;
+ case 'D':
+ pcm_name = optarg;
+ break;
+ case 't':
+ type = atoi(optarg);
+ break;
+ }
}
- snd_pcm_hw_params_alloca(&hwparams_p);
- /* get the current hwparams */
- err = snd_pcm_hw_params_current(handle_p, hwparams_p);
- if (err < 0) {
- printf("Unable to determine current hwparams_p: %s\n", snd_strerror(err));
- goto _exit;
- }
- if (snd_pcm_hw_params_supports_audio_wallclock_ts(hwparams_p))
- printf("Playback relies on audio wallclock timestamps\n");
- else
- printf("Playback relies on audio sample counter timestamps\n");
-
- snd_pcm_sw_params_alloca(&swparams_p);
- /* get the current swparams */
- err = snd_pcm_sw_params_current(handle_p, swparams_p);
- if (err < 0) {
- printf("Unable to determine current swparams_p: %s\n", snd_strerror(err));
- goto _exit;
- }
+ memset(&audio_tstamp_config_p, 0, sizeof(snd_pcm_audio_tstamp_config_t));
+ memset(&audio_tstamp_config_c, 0, sizeof(snd_pcm_audio_tstamp_config_t));
+ memset(&audio_tstamp_report_p, 0, sizeof(snd_pcm_audio_tstamp_report_t));
+ memset(&audio_tstamp_report_c, 0, sizeof(snd_pcm_audio_tstamp_report_t));
- /* enable tstamp */
- err = snd_pcm_sw_params_set_tstamp_mode(handle_p, swparams_p, SND_PCM_TSTAMP_ENABLE);
- if (err < 0) {
- printf("Unable to set tstamp mode : %s\n", snd_strerror(err));
- goto _exit;
- }
+ if (do_playback) {
+ if ((err = snd_pcm_open(&handle_p, pcm_name, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
+ printf("Playback open error: %s\n", snd_strerror(err));
+ goto _exit;
+ }
- err = snd_pcm_sw_params_set_tstamp_type(handle_p, swparams_p, TSTAMP_TYPE);
- if (err < 0) {
- printf("Unable to set tstamp type : %s\n", snd_strerror(err));
- goto _exit;
- }
+ if ((err = snd_pcm_set_params(handle_p,
+ SND_PCM_FORMAT_S16,
+ SND_PCM_ACCESS_RW_INTERLEAVED,
+ 2,
+ SAMPLE_FREQ,
+ 0,
+ 4*1000000/TIMESTAMP_FREQ)) < 0) {
+ printf("Playback open error: %s\n", snd_strerror(err));
+ goto _exit;
+ }
- /* write the sw parameters */
- err = snd_pcm_sw_params(handle_p, swparams_p);
- if (err < 0) {
- printf("Unable to set swparams_p : %s\n", snd_strerror(err));
- goto _exit;
- }
+ snd_pcm_hw_params_alloca(&hwparams_p);
+/* get the current hwparams */
+ err = snd_pcm_hw_params_current(handle_p, hwparams_p);
+ if (err < 0) {
+ printf("Unable to determine current hwparams_p: %s\n", snd_strerror(err));
+ goto _exit;
+ }
- if ((err = snd_pcm_open(&handle_c, device, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0) {
- printf("Capture open error: %s\n", snd_strerror(err));
- goto _exit;
- }
- if ((err = snd_pcm_set_params(handle_c,
- SND_PCM_FORMAT_S16,
- SND_PCM_ACCESS_RW_INTERLEAVED,
- 2,
- 48000,
- 0,
- 500000)) < 0) { /* 0.5sec */
- printf("Capture open error: %s\n", snd_strerror(err));
- goto _exit;
- }
+ if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_p, 0))
+ printf("Playback supports audio compat timestamps\n");
+ if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_p, 1))
+ printf("Playback supports audio default timestamps\n");
+ if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_p, 2))
+ printf("Playback supports audio link timestamps\n");
+ if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_p, 3))
+ printf("Playback supports audio link absolute timestamps\n");
+ if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_p, 4))
+ printf("Playback supports audio link estimated timestamps\n");
+ if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_p, 5))
+ printf("Playback supports audio link synchronized timestamps\n");
+
+ snd_pcm_sw_params_alloca(&swparams_p);
+ /* get the current swparams */
+ err = snd_pcm_sw_params_current(handle_p, swparams_p);
+ if (err < 0) {
+ printf("Unable to determine current swparams_p: %s\n", snd_strerror(err));
+ goto _exit;
+ }
- snd_pcm_hw_params_alloca(&hwparams_c);
- /* get the current hwparams */
- err = snd_pcm_hw_params_current(handle_c, hwparams_c);
- if (err < 0) {
- printf("Unable to determine current hwparams_c: %s\n", snd_strerror(err));
- goto _exit;
- }
- if (snd_pcm_hw_params_supports_audio_wallclock_ts(hwparams_c))
- printf("Capture relies on audio wallclock timestamps\n");
- else
- printf("Capture relies on audio sample counter timestamps\n");
-
- snd_pcm_sw_params_alloca(&swparams_c);
- /* get the current swparams */
- err = snd_pcm_sw_params_current(handle_c, swparams_c);
- if (err < 0) {
- printf("Unable to determine current swparams_c: %s\n", snd_strerror(err));
- goto _exit;
- }
+ /* enable tstamp */
+ err = snd_pcm_sw_params_set_tstamp_mode(handle_p, swparams_p, SND_PCM_TSTAMP_ENABLE);
+ if (err < 0) {
+ printf("Unable to set tstamp mode : %s\n", snd_strerror(err));
+ goto _exit;
+ }
- /* enable tstamp */
- err = snd_pcm_sw_params_set_tstamp_mode(handle_c, swparams_c, SND_PCM_TSTAMP_ENABLE);
- if (err < 0) {
- printf("Unable to set tstamp mode : %s\n", snd_strerror(err));
- goto _exit;
- }
+ err = snd_pcm_sw_params_set_tstamp_type(handle_p, swparams_p, TSTAMP_TYPE);
+ if (err < 0) {
+ printf("Unable to set tstamp type : %s\n", snd_strerror(err));
+ goto _exit;
+ }
+
+ /* write the sw parameters */
+ err = snd_pcm_sw_params(handle_p, swparams_p);
+ if (err < 0) {
+ printf("Unable to set swparams_p : %s\n", snd_strerror(err));
+ goto _exit;
+ }
- err = snd_pcm_sw_params_set_tstamp_type(handle_c, swparams_c, TSTAMP_TYPE);
- if (err < 0) {
- printf("Unable to set tstamp type : %s\n", snd_strerror(err));
- goto _exit;
}
- /* write the sw parameters */
- err = snd_pcm_sw_params(handle_c, swparams_c);
- if (err < 0) {
- printf("Unable to set swparams_c : %s\n", snd_strerror(err));
- goto _exit;
+ if (do_capture) {
+
+ if ((err = snd_pcm_open(&handle_c, pcm_name, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0) {
+ printf("Capture open error: %s\n", snd_strerror(err));
+ goto _exit;
+ }
+ if ((err = snd_pcm_set_params(handle_c,
+ SND_PCM_FORMAT_S16,
+ SND_PCM_ACCESS_RW_INTERLEAVED,
+ 2,
+ SAMPLE_FREQ,
+ 0,
+ 4*1000000/TIMESTAMP_FREQ)) < 0) {
+ printf("Capture open error: %s\n", snd_strerror(err));
+ goto _exit;
+ }
+
+ snd_pcm_hw_params_alloca(&hwparams_c);
+ /* get the current hwparams */
+ err = snd_pcm_hw_params_current(handle_c, hwparams_c);
+ if (err < 0) {
+ printf("Unable to determine current hwparams_c: %s\n", snd_strerror(err));
+ goto _exit;
+ }
+
+ if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_c, 0))
+ printf("Capture supports audio compat timestamps\n");
+ if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_c, 1))
+ printf("Capture supports audio default timestamps\n");
+ if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_c, 2))
+ printf("Capture supports audio link timestamps\n");
+ if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_c, 3))
+ printf("Capture supports audio link absolute timestamps\n");
+ if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_c, 4))
+ printf("Capture supports audio link estimated timestamps\n");
+ if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_c, 5))
+ printf("Capture supports audio link synchronized timestamps\n");
+
+ snd_pcm_sw_params_alloca(&swparams_c);
+ /* get the current swparams */
+ err = snd_pcm_sw_params_current(handle_c, swparams_c);
+ if (err < 0) {
+ printf("Unable to determine current swparams_c: %s\n", snd_strerror(err));
+ goto _exit;
+ }
+
+ /* enable tstamp */
+ err = snd_pcm_sw_params_set_tstamp_mode(handle_c, swparams_c, SND_PCM_TSTAMP_ENABLE);
+ if (err < 0) {
+ printf("Unable to set tstamp mode : %s\n", snd_strerror(err));
+ goto _exit;
+ }
+
+ err = snd_pcm_sw_params_set_tstamp_type(handle_c, swparams_c, TSTAMP_TYPE);
+ if (err < 0) {
+ printf("Unable to set tstamp type : %s\n", snd_strerror(err));
+ goto _exit;
+ }
+
+ /* write the sw parameters */
+ err = snd_pcm_sw_params(handle_c, swparams_c);
+ if (err < 0) {
+ printf("Unable to set swparams_c : %s\n", snd_strerror(err));
+ goto _exit;
+ }
}
+ if (do_playback && do_capture) {
#ifdef PCM_LINK
- if ((err = snd_pcm_link(handle_c, handle_p)) < 0) {
- printf("Streams link error: %s\n", snd_strerror(err));
- exit(0);
- }
+ if ((err = snd_pcm_link(handle_c, handle_p)) < 0) {
+ printf("Streams link error: %s\n", snd_strerror(err));
+ exit(0);
+ }
#endif
-
- i = PLAYBACK_BUFFERS;
- while (i--) {
- frames = snd_pcm_writei(handle_p, buffer_p, PERIOD);
- if (frames < 0) {
- printf("snd_pcm_writei failed: %s\n", snd_strerror(frames));
- goto _exit;
- }
- frame_count_p += frames;
}
- if (PLAYBACK_BUFFERS != 4)
- snd_pcm_start(handle_p);
+ if (do_playback) {
+ i = PLAYBACK_BUFFERS;
+ while (i--) {
+ frames = snd_pcm_writei(handle_p, buffer_p, PERIOD);
+ if (frames < 0) {
+ printf("snd_pcm_writei failed: %s\n", snd_strerror(frames));
+ goto _exit;
+ }
+ frame_count_p += frames;
+ }
+
+ if (PLAYBACK_BUFFERS != 4)
+ snd_pcm_start(handle_p);
+ }
+ if (do_capture) {
#ifndef PCM_LINK
- /* need to start capture explicitly */
- snd_pcm_start(handle_c);
+ /* need to start capture explicitly */
+ snd_pcm_start(handle_c);
+#else
+ if (!do_playback)
+ /* need to start capture explicitly */
+ snd_pcm_start(handle_c);
#endif
+ }
- while (1) {
-
- frames = snd_pcm_wait(handle_c, -1);
- if (frames < 0) {
- printf("snd_pcm_wait failed: %s\n", snd_strerror(frames));
- goto _exit;
- }
-
- frames = snd_pcm_readi(handle_c, buffer_c, PERIOD);
- if (frames < 0) {
- printf("snd_pcm_readi failed: %s\n", snd_strerror(frames));
- goto _exit;
- }
- frame_count_c += frames;
+ while (1) {
- frames = snd_pcm_writei(handle_p, buffer_p, PERIOD);
- if (frames < 0) {
- printf("snd_pcm_writei failed: %s\n", snd_strerror(frames));
- goto _exit;
- }
+ if (do_capture) {
- frame_count_p += frames;
+ frames = snd_pcm_wait(handle_c, -1);
+ if (frames < 0) {
+ printf("snd_pcm_wait failed: %s\n", snd_strerror(frames));
+ goto _exit;
+ }
-#if defined(TRACK_PLAYBACK)
- gettimestamp(handle_p, &tstamp_p, &trigger_tstamp_p, &audio_tstamp_p, &avail_p, &delay_p);
+ frames = snd_pcm_readi(handle_c, buffer_c, PERIOD);
+ if (frames < 0) {
+ printf("snd_pcm_readi failed: %s\n", snd_strerror(frames));
+ goto _exit;
+ }
+ frame_count_c += frames;
+#if defined(TRACK_CAPTURE)
+ audio_tstamp_config_c.type_requested = type;
+ audio_tstamp_config_c.report_delay = do_delay;
+ _gettimestamp(handle_c, &tstamp_c, &trigger_tstamp_c,
+ &audio_tstamp_c, &audio_tstamp_config_c, &audio_tstamp_report_c,
+ &avail_c, &delay_c);
#if defined(TRACK_SAMPLE_COUNTS)
- curr_count_p = frame_count_p - delay_p; /* written minus queued */
+ curr_count_c = frame_count_c + delay_c; /* read plus queued */
- printf("playback: curr_count %lli driver count %lli, delta %lli\n",
- (long long)curr_count_p * 1000000000LL / 48000 ,
- timestamp2ns(audio_tstamp_p),
- (long long)curr_count_p * 1000000000LL / 48000 - timestamp2ns(audio_tstamp_p)
- );
+
+ printf("capture: curr_count %lli driver count %lli, delta %lli\n",
+ (long long)curr_count_c * 1000000000LL / SAMPLE_FREQ ,
+ timestamp2ns(audio_tstamp_c),
+ (long long)curr_count_c * 1000000000LL / SAMPLE_FREQ - timestamp2ns(audio_tstamp_c)
+ );
#endif
- printf("playback: systime: %lli nsec, audio time %lli nsec, \tsystime delta %lli\n",
- timediff(tstamp_p, trigger_tstamp_p),
- timestamp2ns(audio_tstamp_p),
- timediff(tstamp_p, trigger_tstamp_p) - timestamp2ns(audio_tstamp_p)
- );
+ printf("\t capture: systime: %lli nsec, audio time %lli nsec, \tsystime delta %lli\n",
+ timediff(tstamp_c, trigger_tstamp_c),
+ timestamp2ns(audio_tstamp_c),
+ timediff(tstamp_c, trigger_tstamp_c) - timestamp2ns(audio_tstamp_c)
+ );
#endif
+ }
-#if defined(TRACK_CAPTURE)
- gettimestamp(handle_c, &tstamp_c, &trigger_tstamp_c, &audio_tstamp_c, &avail_c, &delay_c);
+ if (do_playback) {
+ frames = snd_pcm_writei(handle_p, buffer_p, PERIOD);
+ if (frames < 0) {
+ printf("snd_pcm_writei failed: %s\n", snd_strerror(frames));
+ goto _exit;
+ }
-#if defined(TRACK_SAMPLE_COUNTS)
- curr_count_c = frame_count_c + delay_c; /* read plus queued */
+ frame_count_p += frames;
+
+#if defined(TRACK_PLAYBACK)
+ audio_tstamp_config_p.type_requested = type;
+ audio_tstamp_config_p.report_delay = do_delay;
+ _gettimestamp(handle_p, &tstamp_p, &trigger_tstamp_p,
+ &audio_tstamp_p, &audio_tstamp_config_p, &audio_tstamp_report_p,
+ &avail_p, &delay_p);
- printf("capture: curr_count %lli driver count %lli, delta %lli\n",
- (long long)curr_count_c * 1000000000LL / 48000 ,
- timestamp2ns(audio_tstamp_c),
- (long long)curr_count_c * 1000000000LL / 48000 - timestamp2ns(audio_tstamp_c)
- );
+#if defined(TRACK_SAMPLE_COUNTS)
+ curr_count_p = frame_count_p - delay_p; /* written minus queued */
+
+ printf("playback: curr_count %lli driver count %lli, delta %lli\n",
+ (long long)curr_count_p * 1000000000LL / SAMPLE_FREQ ,
+ timestamp2ns(audio_tstamp_p),
+ (long long)curr_count_p * 1000000000LL / SAMPLE_FREQ - timestamp2ns(audio_tstamp_p)
+ );
#endif
- printf("\t capture: systime: %lli nsec, audio time %lli nsec, \tsystime delta %lli\n",
- timediff(tstamp_c, trigger_tstamp_c),
- timestamp2ns(audio_tstamp_c),
- timediff(tstamp_c, trigger_tstamp_c) - timestamp2ns(audio_tstamp_c)
- );
+ printf("playback: systime: %lli nsec, audio time %lli nsec, \tsystime delta %lli\n",
+ timediff(tstamp_p, trigger_tstamp_p),
+ timestamp2ns(audio_tstamp_p),
+ timediff(tstamp_p, trigger_tstamp_p) - timestamp2ns(audio_tstamp_p)
+ );
#endif
+ }
+
- }
+ } /* while(1) */
_exit:
if (handle_p)
--
2.5.0

View File

@ -0,0 +1,106 @@
From 6849d7dc88e0fe22cddb6b2d06ddc967ad4c3a6c Mon Sep 17 00:00:00 2001
From: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Date: Wed, 1 Jul 2015 15:40:58 -0500
Subject: [PATCH 32/49] test: audio_time: show report validity and accuracy
Add checks to show if driver reports valid report and resolution
information. disabled by default
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
test/audio_time.c | 30 +++++++++++++++++++++++++-----
1 file changed, 25 insertions(+), 5 deletions(-)
diff --git a/test/audio_time.c b/test/audio_time.c
index e369e59b4ff2..a54c10dc9ebd 100644
--- a/test/audio_time.c
+++ b/test/audio_time.c
@@ -33,6 +33,7 @@ static void usage(char *command)
"-D, --device=NAME select PCM by name \n"
"-p, --playback playback tstamps \n"
"-t, --ts_type=TYPE Default(0),link(1),link_estimated(2),synchronized(3) \n"
+ "-r, --report show audio timestamp and accuracy validity\n"
, command);
}
@@ -128,7 +129,7 @@ int main(int argc, char *argv[])
snd_pcm_audio_tstamp_report_t audio_tstamp_report_c;
int option_index;
- static const char short_options[] = "hcpdD:t:";
+ static const char short_options[] = "hcpdrD:t:";
static const struct option long_options[] = {
{"capture", 0, 0, 'c'},
@@ -137,6 +138,7 @@ int main(int argc, char *argv[])
{"help", no_argument, 0, 'h'},
{"playback", 0, 0, 'p'},
{"ts_type", required_argument, 0, 't'},
+ {"report", 0, 0, 'r'},
{0, 0, 0, 0}
};
@@ -144,6 +146,7 @@ int main(int argc, char *argv[])
int do_playback = 0;
int do_capture = 0;
int type = 0;
+ int do_report = 0;
while ((c = getopt_long(argc, argv, short_options, long_options, &option_index)) != -1) {
switch (c) {
@@ -165,6 +168,8 @@ int main(int argc, char *argv[])
case 't':
type = atoi(optarg);
break;
+ case 'r':
+ do_report = 1;
}
}
@@ -376,11 +381,19 @@ int main(int argc, char *argv[])
(long long)curr_count_c * 1000000000LL / SAMPLE_FREQ - timestamp2ns(audio_tstamp_c)
);
#endif
+ if (do_report) {
+ if (audio_tstamp_report_c.valid == 0)
+ printf("Audio capture timestamp report invalid - ");
+ if (audio_tstamp_report_c.accuracy_report == 0)
+ printf("Audio capture timestamp accuracy report invalid");
+ printf("\n");
+ }
+
- printf("\t capture: systime: %lli nsec, audio time %lli nsec, \tsystime delta %lli\n",
+ printf("\t capture: systime: %lli nsec, audio time %lli nsec, \tsystime delta %lli \t resolution %d ns \n",
timediff(tstamp_c, trigger_tstamp_c),
timestamp2ns(audio_tstamp_c),
- timediff(tstamp_c, trigger_tstamp_c) - timestamp2ns(audio_tstamp_c)
+ timediff(tstamp_c, trigger_tstamp_c) - timestamp2ns(audio_tstamp_c), audio_tstamp_report_c.accuracy
);
#endif
}
@@ -411,11 +424,18 @@ int main(int argc, char *argv[])
(long long)curr_count_p * 1000000000LL / SAMPLE_FREQ - timestamp2ns(audio_tstamp_p)
);
#endif
+ if (do_report) {
+ if (audio_tstamp_report_p.valid == 0)
+ printf("Audio playback timestamp report invalid - ");
+ if (audio_tstamp_report_p.accuracy_report == 0)
+ printf("Audio playback timestamp accuracy report invalid");
+ printf("\n");
+ }
- printf("playback: systime: %lli nsec, audio time %lli nsec, \tsystime delta %lli\n",
+ printf("playback: systime: %lli nsec, audio time %lli nsec, \tsystime delta %lli resolution %d ns\n",
timediff(tstamp_p, trigger_tstamp_p),
timestamp2ns(audio_tstamp_p),
- timediff(tstamp_p, trigger_tstamp_p) - timestamp2ns(audio_tstamp_p)
+ timediff(tstamp_p, trigger_tstamp_p) - timestamp2ns(audio_tstamp_p), audio_tstamp_report_p.accuracy
);
#endif
}
--
2.5.0

View File

@ -0,0 +1,44 @@
From 77b6be63876ee46a95320e77735d977edeedd44a Mon Sep 17 00:00:00 2001
From: Martin Geier <martin.geier@streamunlimited.com>
Date: Fri, 24 Jul 2015 09:30:57 +0200
Subject: [PATCH 33/49] pcm: restore hw params on set latency failed
When method snd_pcm_set_params sets sample rate to 22050 and latency to 50000
to davinci soc driver method snd_pcm_hw_params_set_buffer_time_near fails
and variable params is already changed in the method so the next method
snd_pcm_hw_params_set_period_time_near fails also.
Signed-off-by: Martin Geier <martin.geier@streamunlimited.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
src/pcm/pcm.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c
index bae1d1653904..f5fc728518d8 100644
--- a/src/pcm/pcm.c
+++ b/src/pcm/pcm.c
@@ -8004,7 +8004,7 @@ int snd_pcm_set_params(snd_pcm_t *pcm,
int soft_resample,
unsigned int latency)
{
- snd_pcm_hw_params_t *params;
+ snd_pcm_hw_params_t *params, params_saved;
snd_pcm_sw_params_t *swparams;
const char *s = snd_pcm_stream_name(snd_pcm_stream(pcm));
snd_pcm_uframes_t buffer_size, period_size;
@@ -8057,9 +8057,11 @@ int snd_pcm_set_params(snd_pcm_t *pcm,
return -EINVAL;
}
/* set the buffer time */
+ params_saved = *params;
err = INTERNAL(snd_pcm_hw_params_set_buffer_time_near)(pcm, params, &latency, NULL);
if (err < 0) {
/* error path -> set period size as first */
+ *params = params_saved;
/* set the period time */
period_time = latency / 4;
err = INTERNAL(snd_pcm_hw_params_set_period_time_near)(pcm, params, &period_time, NULL);
--
2.5.0

View File

@ -0,0 +1,274 @@
From d0e13f87742e923edfb638d4b16b69b8dc2136d8 Mon Sep 17 00:00:00 2001
From: Takashi Iwai <tiwai@suse.de>
Date: Mon, 27 Jul 2015 12:32:37 +0200
Subject: [PATCH 34/49] Replace list.h with its own version
We copied include/list.h from Linux kernel, and it's of course in
GPLv2. This has raised concerns to many people, as it's not clear
whether such a code is considered to be completely trivial, thus it
might be seen as a derivative work, which takes effect in GPL, as
suggested by Clemens.
For clearing the situation, this patch replaces the existing list.h
implementation from a new version. The API is kept to be compatible,
but the codes were written from full scratch under LGPL, to be aligned
with other alsa-lib codes.
Reported-by: Clemens Lang <clemens.lang@bmw-carit.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
include/list.h | 212 +++++++++++++++++++--------------------------------------
1 file changed, 70 insertions(+), 142 deletions(-)
diff --git a/include/list.h b/include/list.h
index 4d9895feba05..947c1f8411e2 100644
--- a/include/list.h
+++ b/include/list.h
@@ -1,174 +1,102 @@
+/* Doubly linked list macros compatible with Linux kernel's version
+ * Copyright (c) 2015 by Takashi Iwai <tiwai@suse.de>
+ *
+ * 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.
+ */
+
#ifndef _LIST_H
#define _LIST_H
-/*
- * This code was taken from the Linux 2.4.0 kernel. [jaroslav]
- */
+#include <stddef.h>
-/*
- * Simple doubly linked list implementation.
- *
- * Some of the internal functions ("__xxx") are useful when
- * manipulating whole lists rather than single entries, as
- * sometimes we already know the next/prev entries and we can
- * generate better code by using them directly rather than
- * using the generic single-entry routines.
- */
-
-#ifndef LIST_HEAD_IS_DEFINED
struct list_head {
- struct list_head *next, *prev;
+ struct list_head *next;
+ struct list_head *prev;
};
-#endif
-
-#define LIST_HEAD_INIT(name) { &(name), &(name) }
-#define LIST_HEAD(name) \
- struct list_head name = LIST_HEAD_INIT(name)
+/* one-shot definition of a list head */
+#define LIST_HEAD(x) \
+ struct list_head x = { &x, &x }
-#define INIT_LIST_HEAD(ptr) do { \
- (ptr)->next = (ptr); (ptr)->prev = (ptr); \
-} while (0)
-
-/*
- * Insert a new entry between two known consecutive entries.
- *
- * This is only for internal list manipulation where we know
- * the prev/next entries already!
- */
-static __inline__ void __list_add(struct list_head * _new,
- struct list_head * prev,
- struct list_head * next)
+/* initialize a list head explicitly */
+static inline void INIT_LIST_HEAD(struct list_head *p)
{
- next->prev = _new;
- _new->next = next;
- _new->prev = prev;
- prev->next = _new;
+ p->next = p->prev = p;
}
-/**
- * list_add - add a new entry
- * @new: new entry to be added
- * @head: list head to add it after
- *
- * Insert a new entry after the specified head.
- * This is good for implementing stacks.
+#define list_entry_offset(p, type, offset) \
+ ((type *)((char *)(p) - (offset)))
+
+/* list_entry - retrieve the original struct from list_head
+ * @p: list_head pointer
+ * @type: struct type
+ * @member: struct field member containing the list_head
*/
-static __inline__ void list_add(struct list_head *_new, struct list_head *head)
-{
- __list_add(_new, head, head->next);
-}
+#define list_entry(p, type, member) \
+ list_entry_offset(p, type, offsetof(type, member))
-/**
- * list_add_tail - add a new entry
- * @new: new entry to be added
- * @head: list head to add it before
- *
- * Insert a new entry before the specified head.
- * This is useful for implementing queues.
+/* list_for_each - iterate over the linked list
+ * @p: iterator, a list_head pointer variable
+ * @list: list_head pointer containing the list
*/
-static __inline__ void list_add_tail(struct list_head *_new, struct list_head *head)
-{
- __list_add(_new, head->prev, head);
-}
+#define list_for_each(p, list) \
+ for (p = (list)->next; p != (list); p = p->next)
-/*
- * Delete a list entry by making the prev/next entries
- * point to each other.
- *
- * This is only for internal list manipulation where we know
- * the prev/next entries already!
+/* list_for_each_safe - iterate over the linked list, safe to delete
+ * @p: iterator, a list_head pointer variable
+ * @s: a temporary variable to keep the next, a list_head pointer, too
+ * @list: list_head pointer containing the list
*/
-static __inline__ void __list_del(struct list_head * prev,
- struct list_head * next)
-{
- next->prev = prev;
- prev->next = next;
-}
+#define list_for_each_safe(p, s, list) \
+ for (p = (list)->next; s = p->next, p != (list); p = s)
-/**
- * list_del - deletes entry from list.
- * @entry: the element to delete from the list.
- * Note: list_empty on entry does not return true after this, the entry is in an undefined state.
+/* list_add - prepend a list entry at the head
+ * @p: the new list entry to add
+ * @list: the list head
*/
-static __inline__ void list_del(struct list_head *entry)
+static inline void list_add(struct list_head *p, struct list_head *list)
{
- __list_del(entry->prev, entry->next);
+ struct list_head *first = list->next;
+
+ p->next = first;
+ first->prev = p;
+ list->next = p;
+ p->prev = list;
}
-/**
- * list_del_init - deletes entry from list and reinitialize it.
- * @entry: the element to delete from the list.n
+/* list_add_tail - append a list entry at the tail
+ * @p: the new list entry to add
+ * @list: the list head
*/
-static __inline__ void list_del_init(struct list_head *entry)
+static inline void list_add_tail(struct list_head *p, struct list_head *list)
{
- __list_del(entry->prev, entry->next);
- INIT_LIST_HEAD(entry);
+ struct list_head *last = list->prev;
+
+ last->next = p;
+ p->prev = last;
+ p->next = list;
+ list->prev = p;
}
-/**
- * list_empty - tests whether a list is empty
- * @head: the list to test.
- */
-static __inline__ int list_empty(struct list_head *head)
+/* list_del - delete the given list entry */
+static inline void list_del(struct list_head *p)
{
- return head->next == head;
+ p->prev->next = p->next;
+ p->next->prev = p->prev;
}
-/**
- * list_splice - join two lists
- * @list: the new list to add.
- * @head: the place to add it in the first list.
- */
-static __inline__ void list_splice(struct list_head *list, struct list_head *head)
+/* list_empty - returns 1 if the given list is empty */
+static inline int list_empty(const struct list_head *p)
{
- struct list_head *first = list->next;
-
- if (first != list) {
- struct list_head *last = list->prev;
- struct list_head *at = head->next;
-
- first->prev = head;
- head->next = first;
-
- last->next = at;
- at->prev = last;
- }
+ return p->next == p;
}
-/**
- * list_for_each - iterate over a list
- * @pos: the &struct list_head to use as a loop counter.
- * @head: the head for your list.
- */
-#define list_for_each(pos, head) \
- for (pos = (head)->next ; pos != (head); pos = pos->next)
-
-/**
- * list_for_each_safe - iterate over a list safely (actual pointer can be invalidated)
- * @pos: the &struct list_head to use as a loop counter.
- * @next: the &struct list_head to use to save next.
- * @head: the head for your list.
- */
-#define list_for_each_safe(pos, npos, head) \
- for (pos = (head)->next, npos = pos->next ; pos != (head); pos = npos, npos = pos->next)
-
-/**
- * list_entry - get the struct for this entry
- * @ptr: the &struct list_head pointer.
- * @type: the type of the struct this is embedded in.
- * @member: the name of the list_struct within the struct.
- */
-#define list_entry(ptr, type, member) \
- ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
-
-/**
- * list_entry - get the struct for this entry
- * @ptr: the &struct list_head pointer.
- * @type: the type of the struct this is embedded in.
- * @offset: offset of entry inside a struct
- */
-#define list_entry_offset(ptr, type, offset) \
- ((type *)((char *)(ptr)-(offset)))
-
#endif /* _LIST_H */
--
2.5.0

View File

@ -0,0 +1,454 @@
From 227c790c16db17a986df06c9a3ad79edade78db7 Mon Sep 17 00:00:00 2001
From: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Date: Wed, 29 Jul 2015 17:45:13 +0100
Subject: [PATCH 35/49] topology: uapi: Add UAPI headers for topology ABI
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
include/sound/Makefile.am | 3 +-
include/sound/asoc.h | 388 ++++++++++++++++++++++++++++++++++++++++++++++
include/sound/tlv.h | 23 +++
3 files changed, 413 insertions(+), 1 deletion(-)
create mode 100644 include/sound/asoc.h
create mode 100644 include/sound/tlv.h
diff --git a/include/sound/Makefile.am b/include/sound/Makefile.am
index 31aa2db43d27..b659985e7e36 100644
--- a/include/sound/Makefile.am
+++ b/include/sound/Makefile.am
@@ -1,6 +1,7 @@
alsasoundincludedir = ${includedir}/alsa/sound
alsasoundinclude_HEADERS = asound_fm.h hdsp.h hdspm.h sb16_csp.h \
- sscape_ioctl.h emu10k1.h type_compat.h
+ sscape_ioctl.h emu10k1.h type_compat.h \
+ asoc.h tlv.h
noinst_HEADERS = asound.h asoundef.h asequencer.h
diff --git a/include/sound/asoc.h b/include/sound/asoc.h
new file mode 100644
index 000000000000..bb6dcf3ff7b4
--- /dev/null
+++ b/include/sound/asoc.h
@@ -0,0 +1,388 @@
+/*
+ * uapi/sound/asoc.h -- ALSA SoC Firmware Controls and DAPM
+ *
+ * Copyright (C) 2012 Texas Instruments Inc.
+ * Copyright (C) 2015 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Simple file API to load FW that includes mixers, coefficients, DAPM graphs,
+ * algorithms, equalisers, DAIs, widgets etc.
+*/
+
+#ifndef __LINUX_UAPI_SND_ASOC_H
+#define __LINUX_UAPI_SND_ASOC_H
+
+/*
+ * Maximum number of channels topology kcontrol can represent.
+ */
+#define SND_SOC_TPLG_MAX_CHAN 8
+
+/*
+ * Maximum number of PCM formats capability
+ */
+#define SND_SOC_TPLG_MAX_FORMATS 16
+
+/*
+ * Maximum number of PCM stream configs
+ */
+#define SND_SOC_TPLG_STREAM_CONFIG_MAX 8
+
+/* individual kcontrol info types - can be mixed with other types */
+#define SND_SOC_TPLG_CTL_VOLSW 1
+#define SND_SOC_TPLG_CTL_VOLSW_SX 2
+#define SND_SOC_TPLG_CTL_VOLSW_XR_SX 3
+#define SND_SOC_TPLG_CTL_ENUM 4
+#define SND_SOC_TPLG_CTL_BYTES 5
+#define SND_SOC_TPLG_CTL_ENUM_VALUE 6
+#define SND_SOC_TPLG_CTL_RANGE 7
+#define SND_SOC_TPLG_CTL_STROBE 8
+
+
+/* individual widget kcontrol info types - can be mixed with other types */
+#define SND_SOC_TPLG_DAPM_CTL_VOLSW 64
+#define SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE 65
+#define SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT 66
+#define SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE 67
+#define SND_SOC_TPLG_DAPM_CTL_PIN 68
+
+/* DAPM widget types - add new items to the end */
+#define SND_SOC_TPLG_DAPM_INPUT 0
+#define SND_SOC_TPLG_DAPM_OUTPUT 1
+#define SND_SOC_TPLG_DAPM_MUX 2
+#define SND_SOC_TPLG_DAPM_MIXER 3
+#define SND_SOC_TPLG_DAPM_PGA 4
+#define SND_SOC_TPLG_DAPM_OUT_DRV 5
+#define SND_SOC_TPLG_DAPM_ADC 6
+#define SND_SOC_TPLG_DAPM_DAC 7
+#define SND_SOC_TPLG_DAPM_SWITCH 8
+#define SND_SOC_TPLG_DAPM_PRE 9
+#define SND_SOC_TPLG_DAPM_POST 10
+#define SND_SOC_TPLG_DAPM_AIF_IN 11
+#define SND_SOC_TPLG_DAPM_AIF_OUT 12
+#define SND_SOC_TPLG_DAPM_DAI_IN 13
+#define SND_SOC_TPLG_DAPM_DAI_OUT 14
+#define SND_SOC_TPLG_DAPM_DAI_LINK 15
+#define SND_SOC_TPLG_DAPM_LAST SND_SOC_TPLG_DAPM_DAI_LINK
+
+/* Header magic number and string sizes */
+#define SND_SOC_TPLG_MAGIC 0x41536F43 /* ASoC */
+
+/* string sizes */
+#define SND_SOC_TPLG_NUM_TEXTS 16
+
+/* ABI version */
+#define SND_SOC_TPLG_ABI_VERSION 0x3
+
+/* Max size of TLV data */
+#define SND_SOC_TPLG_TLV_SIZE 32
+
+/*
+ * File and Block header data types.
+ * Add new generic and vendor types to end of list.
+ * Generic types are handled by the core whilst vendors types are passed
+ * to the component drivers for handling.
+ */
+#define SND_SOC_TPLG_TYPE_MIXER 1
+#define SND_SOC_TPLG_TYPE_BYTES 2
+#define SND_SOC_TPLG_TYPE_ENUM 3
+#define SND_SOC_TPLG_TYPE_DAPM_GRAPH 4
+#define SND_SOC_TPLG_TYPE_DAPM_WIDGET 5
+#define SND_SOC_TPLG_TYPE_DAI_LINK 6
+#define SND_SOC_TPLG_TYPE_PCM 7
+#define SND_SOC_TPLG_TYPE_MANIFEST 8
+#define SND_SOC_TPLG_TYPE_CODEC_LINK 9
+#define SND_SOC_TPLG_TYPE_PDATA 10
+#define SND_SOC_TPLG_TYPE_MAX SND_SOC_TPLG_TYPE_PDATA
+
+/* vendor block IDs - please add new vendor types to end */
+#define SND_SOC_TPLG_TYPE_VENDOR_FW 1000
+#define SND_SOC_TPLG_TYPE_VENDOR_CONFIG 1001
+#define SND_SOC_TPLG_TYPE_VENDOR_COEFF 1002
+#define SND_SOC_TPLG_TYPEVENDOR_CODEC 1003
+
+#define SND_SOC_TPLG_STREAM_PLAYBACK 0
+#define SND_SOC_TPLG_STREAM_CAPTURE 1
+
+/*
+ * Block Header.
+ * This header precedes all object and object arrays below.
+ */
+struct snd_soc_tplg_hdr {
+ __le32 magic; /* magic number */
+ __le32 abi; /* ABI version */
+ __le32 version; /* optional vendor specific version details */
+ __le32 type; /* SND_SOC_TPLG_TYPE_ */
+ __le32 size; /* size of this structure */
+ __le32 vendor_type; /* optional vendor specific type info */
+ __le32 payload_size; /* data bytes, excluding this header */
+ __le32 index; /* identifier for block */
+ __le32 count; /* number of elements in block */
+} __attribute__((packed));
+
+/*
+ * Private data.
+ * All topology objects may have private data that can be used by the driver or
+ * firmware. Core will ignore this data.
+ */
+struct snd_soc_tplg_private {
+ __le32 size; /* in bytes of private data */
+ char data[0];
+} __attribute__((packed));
+
+/*
+ * Kcontrol TLV data.
+ */
+struct snd_soc_tplg_ctl_tlv {
+ __le32 size; /* in bytes aligned to 4 */
+ __le32 numid; /* control element numeric identification */
+ __le32 count; /* number of elem in data array */
+ __le32 data[SND_SOC_TPLG_TLV_SIZE];
+} __attribute__((packed));
+
+/*
+ * Kcontrol channel data
+ */
+struct snd_soc_tplg_channel {
+ __le32 size; /* in bytes of this structure */
+ __le32 reg;
+ __le32 shift;
+ __le32 id; /* ID maps to Left, Right, LFE etc */
+} __attribute__((packed));
+
+/*
+ * Kcontrol Operations IDs
+ */
+struct snd_soc_tplg_kcontrol_ops_id {
+ __le32 get;
+ __le32 put;
+ __le32 info;
+} __attribute__((packed));
+
+/*
+ * kcontrol header
+ */
+struct snd_soc_tplg_ctl_hdr {
+ __le32 size; /* in bytes of this structure */
+ __le32 type;
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ __le32 access;
+ struct snd_soc_tplg_kcontrol_ops_id ops;
+ __le32 tlv_size; /* non zero means control has TLV data */
+} __attribute__((packed));
+
+/*
+ * Stream Capabilities
+ */
+struct snd_soc_tplg_stream_caps {
+ __le32 size; /* in bytes of this structure */
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ __le64 formats[SND_SOC_TPLG_MAX_FORMATS]; /* supported formats SNDRV_PCM_FMTBIT_* */
+ __le32 rates; /* supported rates SNDRV_PCM_RATE_* */
+ __le32 rate_min; /* min rate */
+ __le32 rate_max; /* max rate */
+ __le32 channels_min; /* min channels */
+ __le32 channels_max; /* max channels */
+ __le32 periods_min; /* min number of periods */
+ __le32 periods_max; /* max number of periods */
+ __le32 period_size_min; /* min period size bytes */
+ __le32 period_size_max; /* max period size bytes */
+ __le32 buffer_size_min; /* min buffer size bytes */
+ __le32 buffer_size_max; /* max buffer size bytes */
+} __attribute__((packed));
+
+/*
+ * FE or BE Stream configuration supported by SW/FW
+ */
+struct snd_soc_tplg_stream {
+ __le32 size; /* in bytes of this structure */
+ __le64 format; /* SNDRV_PCM_FMTBIT_* */
+ __le32 rate; /* SNDRV_PCM_RATE_* */
+ __le32 period_bytes; /* size of period in bytes */
+ __le32 buffer_bytes; /* size of buffer in bytes */
+ __le32 channels; /* channels */
+ __le32 tdm_slot; /* optional BE bitmask of supported TDM slots */
+ __le32 dai_fmt; /* SND_SOC_DAIFMT_ */
+} __attribute__((packed));
+
+/*
+ * Duplex stream configuration supported by SW/FW.
+ */
+struct snd_soc_tplg_stream_config {
+ __le32 size; /* in bytes of this structure */
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ struct snd_soc_tplg_stream playback;
+ struct snd_soc_tplg_stream capture;
+} __attribute__((packed));
+
+/*
+ * Manifest. List totals for each payload type. Not used in parsing, but will
+ * be passed to the component driver before any other objects in order for any
+ * global component resource allocations.
+ *
+ * File block representation for manifest :-
+ * +-----------------------------------+----+
+ * | struct snd_soc_tplg_hdr | 1 |
+ * +-----------------------------------+----+
+ * | struct snd_soc_tplg_manifest | 1 |
+ * +-----------------------------------+----+
+ */
+struct snd_soc_tplg_manifest {
+ __le32 size; /* in bytes of this structure */
+ __le32 control_elems; /* number of control elements */
+ __le32 widget_elems; /* number of widget elements */
+ __le32 graph_elems; /* number of graph elements */
+ __le32 dai_elems; /* number of DAI elements */
+ __le32 dai_link_elems; /* number of DAI link elements */
+ struct snd_soc_tplg_private priv;
+} __attribute__((packed));
+
+/*
+ * Mixer kcontrol.
+ *
+ * File block representation for mixer kcontrol :-
+ * +-----------------------------------+----+
+ * | struct snd_soc_tplg_hdr | 1 |
+ * +-----------------------------------+----+
+ * | struct snd_soc_tplg_mixer_control | N |
+ * +-----------------------------------+----+
+ */
+struct snd_soc_tplg_mixer_control {
+ struct snd_soc_tplg_ctl_hdr hdr;
+ __le32 size; /* in bytes of this structure */
+ __le32 min;
+ __le32 max;
+ __le32 platform_max;
+ __le32 invert;
+ __le32 num_channels;
+ struct snd_soc_tplg_channel channel[SND_SOC_TPLG_MAX_CHAN];
+ struct snd_soc_tplg_ctl_tlv tlv;
+ struct snd_soc_tplg_private priv;
+} __attribute__((packed));
+
+/*
+ * Enumerated kcontrol
+ *
+ * File block representation for enum kcontrol :-
+ * +-----------------------------------+----+
+ * | struct snd_soc_tplg_hdr | 1 |
+ * +-----------------------------------+----+
+ * | struct snd_soc_tplg_enum_control | N |
+ * +-----------------------------------+----+
+ */
+struct snd_soc_tplg_enum_control {
+ struct snd_soc_tplg_ctl_hdr hdr;
+ __le32 size; /* in bytes of this structure */
+ __le32 num_channels;
+ struct snd_soc_tplg_channel channel[SND_SOC_TPLG_MAX_CHAN];
+ __le32 items;
+ __le32 mask;
+ __le32 count;
+ char texts[SND_SOC_TPLG_NUM_TEXTS][SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ __le32 values[SND_SOC_TPLG_NUM_TEXTS * SNDRV_CTL_ELEM_ID_NAME_MAXLEN / 4];
+ struct snd_soc_tplg_private priv;
+} __attribute__((packed));
+
+/*
+ * Bytes kcontrol
+ *
+ * File block representation for bytes kcontrol :-
+ * +-----------------------------------+----+
+ * | struct snd_soc_tplg_hdr | 1 |
+ * +-----------------------------------+----+
+ * | struct snd_soc_tplg_bytes_control | N |
+ * +-----------------------------------+----+
+ */
+struct snd_soc_tplg_bytes_control {
+ struct snd_soc_tplg_ctl_hdr hdr;
+ __le32 size; /* in bytes of this structure */
+ __le32 max;
+ __le32 mask;
+ __le32 base;
+ __le32 num_regs;
+ struct snd_soc_tplg_private priv;
+} __attribute__((packed));
+
+/*
+ * DAPM Graph Element
+ *
+ * File block representation for DAPM graph elements :-
+ * +-------------------------------------+----+
+ * | struct snd_soc_tplg_hdr | 1 |
+ * +-------------------------------------+----+
+ * | struct snd_soc_tplg_dapm_graph_elem | N |
+ * +-------------------------------------+----+
+ */
+struct snd_soc_tplg_dapm_graph_elem {
+ char sink[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ char control[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ char source[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+} __attribute__((packed));
+
+/*
+ * DAPM Widget.
+ *
+ * File block representation for DAPM widget :-
+ * +-------------------------------------+-----+
+ * | struct snd_soc_tplg_hdr | 1 |
+ * +-------------------------------------+-----+
+ * | struct snd_soc_tplg_dapm_widget | N |
+ * +-------------------------------------+-----+
+ * | struct snd_soc_tplg_enum_control | 0|1 |
+ * | struct snd_soc_tplg_mixer_control | 0|N |
+ * +-------------------------------------+-----+
+ *
+ * Optional enum or mixer control can be appended to the end of each widget
+ * in the block.
+ */
+struct snd_soc_tplg_dapm_widget {
+ __le32 size; /* in bytes of this structure */
+ __le32 id; /* SND_SOC_DAPM_CTL */
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ char sname[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+
+ __le32 reg; /* negative reg = no direct dapm */
+ __le32 shift; /* bits to shift */
+ __le32 mask; /* non-shifted mask */
+ __le32 subseq; /* sort within widget type */
+ __u32 invert; /* invert the power bit */
+ __u32 ignore_suspend; /* kept enabled over suspend */
+ __u16 event_flags;
+ __u16 event_type;
+ __u16 num_kcontrols;
+ struct snd_soc_tplg_private priv;
+ /*
+ * kcontrols that relate to this widget
+ * follow here after widget private data
+ */
+} __attribute__((packed));
+
+struct snd_soc_tplg_pcm_cfg_caps {
+ struct snd_soc_tplg_stream_caps caps;
+ struct snd_soc_tplg_stream_config configs[SND_SOC_TPLG_STREAM_CONFIG_MAX];
+ __le32 num_configs; /* number of configs */
+} __attribute__((packed));
+
+/*
+ * Describes SW/FW specific features of PCM or DAI link.
+ *
+ * File block representation for PCM/DAI-Link :-
+ * +-----------------------------------+-----+
+ * | struct snd_soc_tplg_hdr | 1 |
+ * +-----------------------------------+-----+
+ * | struct snd_soc_tplg_dapm_pcm_dai | N |
+ * +-----------------------------------+-----+
+ */
+struct snd_soc_tplg_pcm_dai {
+ __le32 size; /* in bytes of this structure */
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ __le32 id; /* unique ID - used to match */
+ __le32 playback; /* supports playback mode */
+ __le32 capture; /* supports capture mode */
+ __le32 compress; /* 1 = compressed; 0 = PCM */
+ struct snd_soc_tplg_pcm_cfg_caps capconf[2]; /* capabilities and configs */
+} __attribute__((packed));
+
+#endif
diff --git a/include/sound/tlv.h b/include/sound/tlv.h
new file mode 100644
index 000000000000..33d747df1410
--- /dev/null
+++ b/include/sound/tlv.h
@@ -0,0 +1,23 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __UAPI_SOUND_TLV_H
+#define __UAPI_SOUND_TLV_H
+
+#define SNDRV_CTL_TLVT_CONTAINER 0 /* one level down - group of TLVs */
+#define SNDRV_CTL_TLVT_DB_SCALE 1 /* dB scale */
+#define SNDRV_CTL_TLVT_DB_LINEAR 2 /* linear volume */
+#define SNDRV_CTL_TLVT_DB_RANGE 3 /* dB range container */
+#define SNDRV_CTL_TLVT_DB_MINMAX 4 /* dB scale with min/max */
+#define SNDRV_CTL_TLVT_DB_MINMAX_MUTE 5 /* dB scale with min/max with mute */
+
+#endif
--
2.5.0

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,112 @@
From 408396a8ca092846c840baa79c04b5f7dbe5da69 Mon Sep 17 00:00:00 2001
From: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Date: Wed, 29 Jul 2015 17:45:15 +0100
Subject: [PATCH 37/49] topology: Add text section parser.
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>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
src/topology/text.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 88 insertions(+)
create mode 100644 src/topology/text.c
diff --git a/src/topology/text.c b/src/topology/text.c
new file mode 100644
index 000000000000..ebb6e3840d62
--- /dev/null
+++ b/src/topology/text.c
@@ -0,0 +1,88 @@
+/*
+ Copyright(c) 2014-2015 Intel Corporation
+ All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ Authors: Mengdong Lin <mengdong.lin@intel.com>
+ Yao Jin <yao.jin@intel.com>
+ Liam Girdwood <liam.r.girdwood@linux.intel.com>
+
+*/
+
+#include "list.h"
+#include "tplg_local.h"
+
+#define TEXT_SIZE_MAX \
+ (SND_SOC_TPLG_NUM_TEXTS * SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+
+static int parse_text_values(snd_config_t *cfg, struct tplg_elem *elem)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *value = NULL;
+ int j = 0;
+
+ tplg_dbg(" Text Values: %s\n", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+ n = snd_config_iterator_entry(i);
+
+ if (j == SND_SOC_TPLG_NUM_TEXTS) {
+ tplg_dbg("error: text string number exceeds %d\n", j);
+ return -ENOMEM;
+ }
+
+ /* get value */
+ if (snd_config_get_string(n, &value) < 0)
+ continue;
+
+ elem_copy_text(&elem->texts[j][0], value,
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+ tplg_dbg("\t%s\n", &elem->texts[j][0]);
+
+ j++;
+ }
+
+ return 0;
+}
+
+/* Parse Text data */
+int tplg_parse_text(snd_tplg_t *tplg, snd_config_t *cfg,
+ void *private ATTRIBUTE_UNUSED)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id;
+ int err = 0;
+ struct tplg_elem *elem;
+
+ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_TEXT);
+ if (!elem)
+ return -ENOMEM;
+
+ snd_config_for_each(i, next, cfg) {
+
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ if (strcmp(id, "values") == 0) {
+ err = parse_text_values(n, elem);
+ if (err < 0) {
+ SNDERR("error: failed to parse text values");
+ return err;
+ }
+ continue;
+ }
+ }
+
+ return err;
+}
--
2.5.0

View File

@ -0,0 +1,664 @@
From 4db19506c3e7a68a0d0be40422172f22605c58d8 Mon Sep 17 00:00:00 2001
From: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Date: Wed, 29 Jul 2015 17:45:16 +0100
Subject: [PATCH 38/49] topology: Add PCM parser.
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>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
src/topology/pcm.c | 639 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 639 insertions(+)
create mode 100644 src/topology/pcm.c
diff --git a/src/topology/pcm.c b/src/topology/pcm.c
new file mode 100644
index 000000000000..8f23a6f12ec4
--- /dev/null
+++ b/src/topology/pcm.c
@@ -0,0 +1,639 @@
+/*
+ Copyright(c) 2014-2015 Intel Corporation
+ All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ Authors: Mengdong Lin <mengdong.lin@intel.com>
+ Yao Jin <yao.jin@intel.com>
+ Liam Girdwood <liam.r.girdwood@linux.intel.com>
+*/
+
+#include "list.h"
+#include "tplg_local.h"
+
+struct tplg_elem *lookup_pcm_dai_stream(struct list_head *base, const char* id)
+{
+ struct list_head *pos;
+ struct tplg_elem *elem;
+ struct snd_soc_tplg_pcm_dai *pcm_dai;
+
+ list_for_each(pos, base) {
+
+ elem = list_entry(pos, struct tplg_elem, list);
+ if (elem->type != OBJECT_TYPE_PCM)
+ return NULL;
+
+ pcm_dai = elem->pcm;
+
+ if (pcm_dai && (!strcmp(pcm_dai->capconf[0].caps.name, id)
+ || !strcmp(pcm_dai->capconf[1].caps.name, id)))
+ return elem;
+ }
+
+ return NULL;
+}
+
+/* copy referenced caps to the pcm */
+static void copy_pcm_caps(const char *id, struct snd_soc_tplg_stream_caps *caps,
+ struct tplg_elem *ref_elem)
+{
+ struct snd_soc_tplg_stream_caps *ref_caps = ref_elem->stream_caps;
+
+ tplg_dbg("Copy pcm caps (%ld bytes) from '%s' to '%s' \n",
+ sizeof(*caps), ref_elem->id, id);
+
+ *caps = *ref_caps;
+}
+
+/* copy referenced config to the pcm */
+static void copy_pcm_config(const char *id,
+ struct snd_soc_tplg_stream_config *cfg, struct tplg_elem *ref_elem)
+{
+ struct snd_soc_tplg_stream_config *ref_cfg = ref_elem->stream_cfg;
+
+ tplg_dbg("Copy pcm config (%ld bytes) from '%s' to '%s' \n",
+ sizeof(*cfg), ref_elem->id, id);
+
+ *cfg = *ref_cfg;
+}
+
+/* check referenced config and caps for a pcm */
+static int tplg_build_pcm_cfg_caps(snd_tplg_t *tplg, struct tplg_elem *elem)
+{
+ struct tplg_elem *ref_elem = NULL;
+ struct snd_soc_tplg_pcm_cfg_caps *capconf;
+ struct snd_soc_tplg_pcm_dai *pcm_dai;
+ unsigned int i, j;
+
+ switch (elem->type) {
+ case OBJECT_TYPE_PCM:
+ pcm_dai = elem->pcm;
+ break;
+ case OBJECT_TYPE_BE:
+ pcm_dai = elem->be;
+ break;
+ case OBJECT_TYPE_CC:
+ pcm_dai = elem->cc;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (i = 0; i < 2; i++) {
+ capconf = &pcm_dai->capconf[i];
+
+ ref_elem = tplg_elem_lookup(&tplg->pcm_caps_list,
+ capconf->caps.name, OBJECT_TYPE_STREAM_CAPS);
+
+ if (ref_elem != NULL)
+ copy_pcm_caps(elem->id, &capconf->caps, ref_elem);
+
+ for (j = 0; j < capconf->num_configs; j++) {
+ ref_elem = tplg_elem_lookup(&tplg->pcm_config_list,
+ capconf->configs[j].name,
+ OBJECT_TYPE_STREAM_CONFIG);
+
+ if (ref_elem != NULL)
+ copy_pcm_config(elem->id,
+ &capconf->configs[j],
+ ref_elem);
+ }
+ }
+
+ return 0;
+}
+
+int tplg_build_pcm_dai(snd_tplg_t *tplg, unsigned int type)
+{
+ struct list_head *base, *pos;
+ struct tplg_elem *elem;
+ int err = 0;
+
+ switch (type) {
+ case OBJECT_TYPE_PCM:
+ base = &tplg->pcm_list;
+ break;
+ case OBJECT_TYPE_BE:
+ base = &tplg->be_list;
+ break;
+ case OBJECT_TYPE_CC:
+ base = &tplg->cc_list;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ list_for_each(pos, base) {
+
+ elem = list_entry(pos, struct tplg_elem, list);
+ if (elem->type != type) {
+ SNDERR("error: invalid elem '%s'\n", elem->id);
+ return -EINVAL;
+ }
+
+ err = tplg_build_pcm_cfg_caps(tplg, elem);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/* PCM stream configuration */
+static int tplg_parse_stream_cfg(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ snd_config_t *cfg, void *private)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ struct snd_soc_tplg_stream_config *sc = private;
+ struct snd_soc_tplg_stream *stream;
+ const char *id, *val;
+ snd_pcm_format_t format;
+ int ret;
+
+ snd_config_get_id(cfg, &id);
+
+ if (strcmp(id, "playback") == 0)
+ stream = &sc->playback;
+ else if (strcmp(id, "capture") == 0)
+ stream = &sc->capture;
+ else
+ return -EINVAL;
+
+ tplg_dbg("\t%s:\n", id);
+
+ stream->size = sizeof(*stream);
+
+ snd_config_for_each(i, next, cfg) {
+
+ n = snd_config_iterator_entry(i);
+
+ if (snd_config_get_id(n, &id) < 0)
+ return -EINVAL;
+
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ if (strcmp(id, "format") == 0) {
+ format = snd_pcm_format_value(val);
+ if (format == SND_PCM_FORMAT_UNKNOWN) {
+ SNDERR("error: unsupported stream format %s\n",
+ val);
+ return -EINVAL;
+ }
+
+ stream->format = format;
+ tplg_dbg("\t\t%s: %s\n", id, val);
+ continue;
+ }
+
+ if (strcmp(id, "rate") == 0) {
+ stream->rate = atoi(val);
+ tplg_dbg("\t\t%s: %d\n", id, stream->rate);
+ continue;
+ }
+
+ if (strcmp(id, "channels") == 0) {
+ stream->channels = atoi(val);
+ tplg_dbg("\t\t%s: %d\n", id, stream->channels);
+ continue;
+ }
+
+ if (strcmp(id, "tdm_slot") == 0) {
+ stream->tdm_slot = strtol(val, NULL, 16);
+ tplg_dbg("\t\t%s: 0x%x\n", id, stream->tdm_slot);
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/* Parse pcm configuration */
+int tplg_parse_pcm_config(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
+{
+ struct snd_soc_tplg_stream_config *sc;
+ struct tplg_elem *elem;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id;
+ int err;
+
+ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_STREAM_CONFIG);
+ if (!elem)
+ return -ENOMEM;
+
+ sc = elem->stream_cfg;
+ sc->size = elem->size;
+
+ tplg_dbg(" PCM Config: %s\n", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* skip comments */
+ if (strcmp(id, "comment") == 0)
+ continue;
+ if (id[0] == '#')
+ continue;
+
+ if (strcmp(id, "config") == 0) {
+ err = tplg_parse_compound(tplg, n,
+ tplg_parse_stream_cfg, sc);
+ if (err < 0)
+ return err;
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+static int split_format(struct snd_soc_tplg_stream_caps *caps, char *str)
+{
+ char *s = NULL;
+ snd_pcm_format_t format;
+ int i = 0, ret;
+
+ s = strtok(str, ",");
+ while ((s != NULL) && (i < SND_SOC_TPLG_MAX_FORMATS)) {
+ format = snd_pcm_format_value(s);
+ if (format == SND_PCM_FORMAT_UNKNOWN) {
+ SNDERR("error: unsupported stream format %s\n", s);
+ return -EINVAL;
+ }
+
+ caps->formats[i] = format;
+ s = strtok(NULL, ", ");
+ i++;
+ }
+
+ return 0;
+}
+
+/* Parse pcm Capabilities */
+int tplg_parse_pcm_caps(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
+{
+ struct snd_soc_tplg_stream_caps *sc;
+ struct tplg_elem *elem;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id, *val;
+ char *s;
+ int err;
+
+ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_STREAM_CAPS);
+ if (!elem)
+ return -ENOMEM;
+
+ sc = elem->stream_caps;
+ sc->size = elem->size;
+ elem_copy_text(sc->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+
+ tplg_dbg(" PCM Capabilities: %s\n", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* skip comments */
+ if (strcmp(id, "comment") == 0)
+ continue;
+ if (id[0] == '#')
+ continue;
+
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ if (strcmp(id, "formats") == 0) {
+ s = strdup(val);
+ if (s == NULL)
+ return -ENOMEM;
+
+ err = split_format(sc, s);
+ free(s);
+
+ if (err < 0)
+ return err;
+
+ tplg_dbg("\t\t%s: %s\n", id, val);
+ continue;
+ }
+
+ if (strcmp(id, "rate_min") == 0) {
+ sc->rate_min = atoi(val);
+ tplg_dbg("\t\t%s: %d\n", id, sc->rate_min);
+ continue;
+ }
+
+ if (strcmp(id, "rate_max") == 0) {
+ sc->rate_max = atoi(val);
+ tplg_dbg("\t\t%s: %d\n", id, sc->rate_max);
+ continue;
+ }
+
+ if (strcmp(id, "channels_min") == 0) {
+ sc->channels_min = atoi(val);
+ tplg_dbg("\t\t%s: %d\n", id, sc->channels_min);
+ continue;
+ }
+
+ if (strcmp(id, "channels_max") == 0) {
+ sc->channels_max = atoi(val);
+ tplg_dbg("\t\t%s: %d\n", id, sc->channels_max);
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+static int tplg_parse_pcm_cfg(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ snd_config_t *cfg, void *private)
+{
+ struct snd_soc_tplg_pcm_cfg_caps *capconf = private;
+ struct snd_soc_tplg_stream_config *configs = capconf->configs;
+ unsigned int *num_configs = &capconf->num_configs;
+ const char *value;
+
+ if (*num_configs == SND_SOC_TPLG_STREAM_CONFIG_MAX)
+ return -EINVAL;
+
+ if (snd_config_get_string(cfg, &value) < 0)
+ return EINVAL;
+
+ elem_copy_text(configs[*num_configs].name, value,
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+
+ *num_configs += 1;
+
+ tplg_dbg("\t\t\t%s\n", value);
+
+ return 0;
+}
+
+/* Parse the cap and config of a pcm */
+int tplg_parse_pcm_cap_cfg(snd_tplg_t *tplg, snd_config_t *cfg,
+ void *private)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ struct tplg_elem *elem = private;
+ struct snd_soc_tplg_pcm_dai *pcm_dai;
+ const char *id, *value;
+ int err, stream;
+
+ if (elem->type == OBJECT_TYPE_PCM)
+ pcm_dai = elem->pcm;
+ else if (elem->type == OBJECT_TYPE_BE)
+ pcm_dai = elem->be;
+ else if (elem->type == OBJECT_TYPE_CC)
+ pcm_dai = elem->cc;
+ else
+ return -EINVAL;
+
+ snd_config_get_id(cfg, &id);
+
+ tplg_dbg("\t%s:\n", id);
+
+ if (strcmp(id, "playback") == 0) {
+ stream = SND_SOC_TPLG_STREAM_PLAYBACK;
+ pcm_dai->playback = 1;
+ } else if (strcmp(id, "capture") == 0) {
+ stream = SND_SOC_TPLG_STREAM_CAPTURE;
+ pcm_dai->capture = 1;
+ } else
+ return -EINVAL;
+
+ snd_config_for_each(i, next, cfg) {
+
+ n = snd_config_iterator_entry(i);
+
+ /* get id */
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ if (strcmp(id, "capabilities") == 0) {
+ if (snd_config_get_string(n, &value) < 0)
+ continue;
+
+ elem_copy_text(pcm_dai->capconf[stream].caps.name, value,
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+
+ tplg_dbg("\t\t%s\n\t\t\t%s\n", id, value);
+ continue;
+ }
+
+ if (strcmp(id, "configs") == 0) {
+ tplg_dbg("\t\tconfigs:\n");
+ err = tplg_parse_compound(tplg, n, tplg_parse_pcm_cfg,
+ &pcm_dai->capconf[stream]);
+ if (err < 0)
+ return err;
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/* Parse pcm */
+int tplg_parse_pcm(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
+{
+ struct snd_soc_tplg_pcm_dai *pcm_dai;
+ struct tplg_elem *elem;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id, *val = NULL;
+ int err;
+
+ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_PCM);
+ if (!elem)
+ return -ENOMEM;
+
+ pcm_dai = elem->pcm;
+ pcm_dai->size = elem->size;
+ elem_copy_text(pcm_dai->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+
+ tplg_dbg(" PCM: %s\n", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* skip comments */
+ if (strcmp(id, "comment") == 0)
+ continue;
+ if (id[0] == '#')
+ continue;
+
+ if (strcmp(id, "index") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ elem->index = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, elem->index);
+ continue;
+ }
+
+ if (strcmp(id, "id") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ pcm_dai->id = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, pcm_dai->id);
+ continue;
+ }
+
+ if (strcmp(id, "pcm") == 0) {
+ err = tplg_parse_compound(tplg, n,
+ tplg_parse_pcm_cap_cfg, elem);
+ if (err < 0)
+ return err;
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/* Parse be */
+int tplg_parse_be(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
+{
+ struct snd_soc_tplg_pcm_dai *pcm_dai;
+ struct tplg_elem *elem;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id, *val = NULL;
+ int err;
+
+ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_BE);
+ if (!elem)
+ return -ENOMEM;
+
+ pcm_dai = elem->be;
+ pcm_dai->size = elem->size;
+ elem_copy_text(pcm_dai->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+
+ tplg_dbg(" BE: %s\n", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* skip comments */
+ if (strcmp(id, "comment") == 0)
+ continue;
+ if (id[0] == '#')
+ continue;
+
+ if (strcmp(id, "index") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ elem->index = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, elem->index);
+ continue;
+ }
+
+ if (strcmp(id, "id") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ pcm_dai->id = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, pcm_dai->id);
+ continue;
+ }
+
+ if (strcmp(id, "be") == 0) {
+ err = tplg_parse_compound(tplg, n,
+ tplg_parse_pcm_cap_cfg, elem);
+ if (err < 0)
+ return err;
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/* Parse cc */
+int tplg_parse_cc(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
+{
+ struct snd_soc_tplg_pcm_dai *pcm_dai;
+ struct tplg_elem *elem;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id, *val = NULL;
+ int err;
+
+ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_CC);
+ if (!elem)
+ return -ENOMEM;
+
+ pcm_dai = elem->cc;
+ pcm_dai->size = elem->size;
+
+ tplg_dbg(" CC: %s\n", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* skip comments */
+ if (strcmp(id, "comment") == 0)
+ continue;
+ if (id[0] == '#')
+ continue;
+
+ if (strcmp(id, "index") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ elem->index = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, elem->index);
+ continue;
+ }
+
+ if (strcmp(id, "id") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ pcm_dai->id = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, pcm_dai->id);
+ continue;
+ }
+
+ if (strcmp(id, "cc") == 0) {
+ err = tplg_parse_compound(tplg, n,
+ tplg_parse_pcm_cap_cfg, elem);
+ if (err < 0)
+ return err;
+ continue;
+ }
+ }
+
+ return 0;
+}
--
2.5.0

View File

@ -0,0 +1,107 @@
From 353f1eddb608a837157342155fc061f85bf0a5f8 Mon Sep 17 00:00:00 2001
From: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Date: Wed, 29 Jul 2015 17:45:17 +0100
Subject: [PATCH 39/49] topology: Add operations parser
Parse operations so we can bind them to kcontrols in the kernel.
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
src/topology/ops.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 84 insertions(+)
create mode 100644 src/topology/ops.c
diff --git a/src/topology/ops.c b/src/topology/ops.c
new file mode 100644
index 000000000000..243d8c5e2bbc
--- /dev/null
+++ b/src/topology/ops.c
@@ -0,0 +1,84 @@
+/*
+ Copyright(c) 2014-2015 Intel Corporation
+ All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ Authors: Mengdong Lin <mengdong.lin@intel.com>
+ Yao Jin <yao.jin@intel.com>
+ Liam Girdwood <liam.r.girdwood@linux.intel.com>
+*/
+
+#include "list.h"
+#include "tplg_local.h"
+
+/* mapping of kcontrol text names to types */
+static const struct map_elem control_map[] = {
+ {"volsw", SND_SOC_TPLG_CTL_VOLSW},
+ {"volsw_sx", SND_SOC_TPLG_CTL_VOLSW_SX},
+ {"volsw_xr_sx", SND_SOC_TPLG_CTL_VOLSW_XR_SX},
+ {"enum", SND_SOC_TPLG_CTL_ENUM},
+ {"bytes", SND_SOC_TPLG_CTL_BYTES},
+ {"enum_value", SND_SOC_TPLG_CTL_ENUM_VALUE},
+ {"range", SND_SOC_TPLG_CTL_RANGE},
+ {"strobe", SND_SOC_TPLG_CTL_STROBE},
+};
+
+static int lookup_ops(const char *c)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(control_map); i++) {
+ if (strcmp(control_map[i].name, c) == 0)
+ return control_map[i].id;
+ }
+
+ /* cant find string name in our table so we use its ID number */
+ return atoi(c);
+}
+
+/* Parse Control operations. Ops can come from standard names above or
+ * bespoke driver controls with numbers >= 256
+ */
+int tplg_parse_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ snd_config_t *cfg, void *private)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ struct snd_soc_tplg_ctl_hdr *hdr = private;
+ const char *id, *value;
+
+ tplg_dbg("\tOps\n");
+ hdr->size = sizeof(*hdr);
+
+ snd_config_for_each(i, next, cfg) {
+
+ n = snd_config_iterator_entry(i);
+
+ /* get id */
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* get value - try strings then ints */
+ if (snd_config_get_string(n, &value) < 0)
+ continue;
+
+ if (strcmp(id, "info") == 0)
+ hdr->ops.info = lookup_ops(value);
+ else if (strcmp(id, "put") == 0)
+ hdr->ops.put = lookup_ops(value);
+ else if (strcmp(id, "get") == 0)
+ hdr->ops.get = lookup_ops(value);
+
+ tplg_dbg("\t\t%s = %s\n", id, value);
+ }
+
+ return 0;
+}
--
2.5.0

View File

@ -0,0 +1,420 @@
From 5b379da2a0a1084349e918a52f471c03e37af703 Mon Sep 17 00:00:00 2001
From: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Date: Wed, 29 Jul 2015 17:45:18 +0100
Subject: [PATCH 40/49] topology: Add private data parser
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>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
src/topology/data.c | 396 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 396 insertions(+)
create mode 100644 src/topology/data.c
diff --git a/src/topology/data.c b/src/topology/data.c
new file mode 100644
index 000000000000..ae664721935d
--- /dev/null
+++ b/src/topology/data.c
@@ -0,0 +1,396 @@
+/*
+ Copyright(c) 2014-2015 Intel Corporation
+ All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ Authors: Mengdong Lin <mengdong.lin@intel.com>
+ Yao Jin <yao.jin@intel.com>
+ Liam Girdwood <liam.r.girdwood@linux.intel.com>
+*/
+
+#include "list.h"
+#include "tplg_local.h"
+
+/* Get Private data from a file. */
+static int tplg_parse_data_file(snd_config_t *cfg, struct tplg_elem *elem)
+{
+ struct snd_soc_tplg_private *priv = NULL;
+ const char *value = NULL;
+ char filename[MAX_FILE];
+ char *env = getenv(ALSA_CONFIG_TPLG_VAR);
+ FILE *fp;
+ size_t size, bytes_read;
+ int ret = 0;
+
+ tplg_dbg("data DataFile: %s\n", elem->id);
+
+ if (snd_config_get_string(cfg, &value) < 0)
+ return -EINVAL;
+
+ /* prepend alsa config directory to path */
+ snprintf(filename, sizeof(filename), "%s/%s",
+ env ? env : ALSA_TPLG_DIR, value);
+
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+ SNDERR("error: invalid data file path '%s'\n",
+ filename);
+ ret = -errno;
+ goto err;
+ }
+
+ fseek(fp, 0L, SEEK_END);
+ size = ftell(fp);
+ fseek(fp, 0L, SEEK_SET);
+ if (size <= 0) {
+ SNDERR("error: invalid data file size %zu\n", size);
+ ret = -EINVAL;
+ goto err;
+ }
+ if (size > TPLG_MAX_PRIV_SIZE) {
+ SNDERR("error: data file too big %zu\n", size);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ priv = calloc(1, sizeof(*priv) + size);
+ if (!priv) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ bytes_read = fread(&priv->data, 1, size, fp);
+ if (bytes_read != size) {
+ ret = -errno;
+ goto err;
+ }
+
+ elem->data = priv;
+ priv->size = size;
+ elem->size = sizeof(*priv) + size;
+ return 0;
+
+err:
+ if (priv)
+ free(priv);
+ return ret;
+}
+
+static void dump_priv_data(struct tplg_elem *elem)
+{
+ struct snd_soc_tplg_private *priv = elem->data;
+ unsigned char *p = (unsigned char *)priv->data;
+ unsigned int i, j = 0;
+
+ tplg_dbg(" elem size = %d, priv data size = %d\n",
+ elem->size, priv->size);
+
+ for (i = 0; i < priv->size; i++) {
+ if (j++ % 8 == 0)
+ tplg_dbg("\n");
+
+ tplg_dbg(" 0x%x", *p++);
+ }
+
+ tplg_dbg("\n\n");
+}
+
+/* get number of hex value elements in CSV list */
+static int get_hex_num(const char *str)
+{
+ int commas = 0, values = 0, len = strlen(str);
+ const char *end = str + len;
+
+ /* we expect "0x0, 0x0, 0x0" */
+ while (str < end) {
+
+ /* skip white space */
+ if (isspace(*str)) {
+ str++;
+ continue;
+ }
+
+ /* find delimeters */
+ if (*str == ',') {
+ commas++;
+ str++;
+ continue;
+ }
+
+ /* find 0x[0-9] values */
+ if (*str == '0' && str + 2 <= end) {
+ if (str[1] == 'x' && str[2] >= '0' && str[2] <= 'f') {
+ values++;
+ str += 3;
+ } else {
+ str++;
+ }
+ }
+
+ str++;
+ }
+
+ /* there should always be one less comma than value */
+ if (values -1 != commas)
+ return -EINVAL;
+
+ return values;
+}
+
+static int write_hex(char *buf, char *str, int width)
+{
+ long val;
+ void *p = &val;
+
+ errno = 0;
+ val = strtol(str, NULL, 16);
+
+ if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
+ || (errno != 0 && val == 0)) {
+ return -EINVAL;
+ }
+
+ switch (width) {
+ case 1:
+ *(unsigned char *)buf = *(unsigned char *)p;
+ break;
+ case 2:
+ *(unsigned short *)buf = *(unsigned short *)p;
+ break;
+ case 4:
+ *(unsigned int *)buf = *(unsigned int *)p;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int copy_data_hex(char *data, int off, const char *str, int width)
+{
+ char *tmp, *s = NULL, *p = data;
+ int ret;
+
+ tmp = strdup(str);
+ if (tmp == NULL)
+ return -ENOMEM;
+
+ p += off;
+ s = strtok(tmp, ",");
+
+ while (s != NULL) {
+ ret = write_hex(p, s, width);
+ if (ret < 0) {
+ free(tmp);
+ return ret;
+ }
+
+ s = strtok(NULL, ",");
+ p += width;
+ }
+
+ free(tmp);
+ return 0;
+}
+
+static int tplg_parse_data_hex(snd_config_t *cfg, struct tplg_elem *elem,
+ int width)
+{
+ struct snd_soc_tplg_private *priv;
+ const char *value = NULL;
+ int size, esize, off, num;
+ int ret;
+
+ tplg_dbg(" data: %s\n", elem->id);
+
+ if (snd_config_get_string(cfg, &value) < 0)
+ return -EINVAL;
+
+ num = get_hex_num(value);
+ if (num <= 0) {
+ SNDERR("error: malformed hex variable list %s\n", value);
+ return -EINVAL;
+ }
+
+ size = num * width;
+ priv = elem->data;
+
+ if (esize > TPLG_MAX_PRIV_SIZE) {
+ SNDERR("error: data too big %d\n", esize);
+ return -EINVAL;
+ }
+
+ if (priv != NULL) {
+ off = priv->size;
+ esize = elem->size + size;
+ priv = realloc(priv, esize);
+ } else {
+ off = 0;
+ esize = sizeof(*priv) + size;
+ priv = calloc(1, esize);
+ }
+
+ if (!priv)
+ return -ENOMEM;
+
+ elem->data = priv;
+ priv->size += size;
+ elem->size = esize;
+
+ ret = copy_data_hex(priv->data, off, value, width);
+
+ dump_priv_data(elem);
+ return ret;
+}
+
+
+/* Parse Private data.
+ *
+ * Object private data can either be from file or defined as bytes, shorts,
+ * words.
+ */
+int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg,
+ void *private ATTRIBUTE_UNUSED)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id, *val = NULL;
+ int err = 0;
+ struct tplg_elem *elem;
+
+ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_DATA);
+ if (!elem)
+ return -ENOMEM;
+
+ snd_config_for_each(i, next, cfg) {
+
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0) {
+ continue;
+ }
+
+ if (strcmp(id, "file") == 0) {
+ err = tplg_parse_data_file(n, elem);
+ if (err < 0) {
+ SNDERR("error: failed to parse data file\n");
+ return err;
+ }
+ continue;
+ }
+
+ if (strcmp(id, "bytes") == 0) {
+ err = tplg_parse_data_hex(n, elem, 1);
+ if (err < 0) {
+ SNDERR("error: failed to parse data bytes\n");
+ return err;
+ }
+ continue;
+ }
+
+ if (strcmp(id, "shorts") == 0) {
+ err = tplg_parse_data_hex(n, elem, 2);
+ if (err < 0) {
+ SNDERR("error: failed to parse data shorts\n");
+ return err;
+ }
+ continue;
+ }
+
+ if (strcmp(id, "words") == 0) {
+ err = tplg_parse_data_hex(n, elem, 4);
+ if (err < 0) {
+ SNDERR("error: failed to parse data words\n");
+ return err;
+ }
+ continue;
+ }
+
+ if (strcmp(id, "index") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ elem->index = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, elem->index);
+ continue;
+ }
+
+ if (strcmp(id, "type") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ elem->vendor_type = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, elem->index);
+ continue;
+ }
+ }
+
+ return err;
+}
+
+/* copy private data into the bytes extended control */
+int tplg_copy_data(struct tplg_elem *elem, struct tplg_elem *ref)
+{
+ struct snd_soc_tplg_private *priv;
+ int priv_data_size;
+
+ if (!ref)
+ return -EINVAL;
+
+ tplg_dbg("Data '%s' used by '%s'\n", ref->id, elem->id);
+ priv_data_size = ref->data->size;
+
+ switch (elem->type) {
+ case OBJECT_TYPE_MIXER:
+ elem->mixer_ctrl = realloc(elem->mixer_ctrl,
+ elem->size + priv_data_size);
+ if (!elem->mixer_ctrl)
+ return -ENOMEM;
+ priv = &elem->mixer_ctrl->priv;
+ break;
+
+ case OBJECT_TYPE_ENUM:
+ elem->enum_ctrl = realloc(elem->enum_ctrl,
+ elem->size + priv_data_size);
+ if (!elem->enum_ctrl)
+ return -ENOMEM;
+ priv = &elem->enum_ctrl->priv;
+ break;
+
+ case OBJECT_TYPE_BYTES:
+ elem->bytes_ext = realloc(elem->bytes_ext,
+ elem->size + priv_data_size);
+ if (!elem->bytes_ext)
+ return -ENOMEM;
+ priv = &elem->bytes_ext->priv;
+ break;
+
+
+ case OBJECT_TYPE_DAPM_WIDGET:
+ elem->widget = realloc(elem->widget,
+ elem->size + priv_data_size);
+ if (!elem->widget)
+ return -ENOMEM;
+ priv = &elem->widget->priv;
+ break;
+
+ default:
+ SNDERR("elem '%s': type %d private data not supported \n",
+ elem->id, elem->type);
+ return -EINVAL;
+ }
+
+ elem->size += priv_data_size;
+ priv->size = priv_data_size;
+ ref->compound_elem = 1;
+ memcpy(priv->data, ref->data->data, priv_data_size);
+ return 0;
+}
--
2.5.0

View File

@ -0,0 +1,585 @@
From 01a0e1a1c2196967d2522092ca993098a7c66613 Mon Sep 17 00:00:00 2001
From: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Date: Wed, 29 Jul 2015 17:45:19 +0100
Subject: [PATCH 41/49] topology: Add DAPM object parser
Parse DAPM objects including widgets and graph elements.
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
src/topology/dapm.c | 562 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 562 insertions(+)
create mode 100644 src/topology/dapm.c
diff --git a/src/topology/dapm.c b/src/topology/dapm.c
new file mode 100644
index 000000000000..1da82adea470
--- /dev/null
+++ b/src/topology/dapm.c
@@ -0,0 +1,562 @@
+/*
+ Copyright(c) 2014-2015 Intel Corporation
+ All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ Authors: Mengdong Lin <mengdong.lin@intel.com>
+ Yao Jin <yao.jin@intel.com>
+ Liam Girdwood <liam.r.girdwood@linux.intel.com>
+*/
+
+#include "list.h"
+#include "tplg_local.h"
+
+/* mapping of widget text names to types */
+static const struct map_elem widget_map[] = {
+ {"input", SND_SOC_TPLG_DAPM_INPUT},
+ {"output", SND_SOC_TPLG_DAPM_OUTPUT},
+ {"mux", SND_SOC_TPLG_DAPM_MUX},
+ {"mixer", SND_SOC_TPLG_DAPM_MIXER},
+ {"pga", SND_SOC_TPLG_DAPM_PGA},
+ {"out_drv", SND_SOC_TPLG_DAPM_OUT_DRV},
+ {"adc", SND_SOC_TPLG_DAPM_ADC},
+ {"dac", SND_SOC_TPLG_DAPM_DAC},
+ {"switch", SND_SOC_TPLG_DAPM_SWITCH},
+ {"pre", SND_SOC_TPLG_DAPM_PRE},
+ {"post", SND_SOC_TPLG_DAPM_POST},
+ {"aif_in", SND_SOC_TPLG_DAPM_AIF_IN},
+ {"aif_out", SND_SOC_TPLG_DAPM_AIF_OUT},
+ {"dai_in", SND_SOC_TPLG_DAPM_DAI_IN},
+ {"dai_out", SND_SOC_TPLG_DAPM_DAI_OUT},
+ {"dai_link", SND_SOC_TPLG_DAPM_DAI_LINK},
+};
+
+/* mapping of widget kcontrol text names to types */
+static const struct map_elem widget_control_map[] = {
+ {"volsw", SND_SOC_TPLG_DAPM_CTL_VOLSW},
+ {"enum_double", SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE},
+ {"enum_virt", SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT},
+ {"enum_value", SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE},
+};
+
+static int lookup_widget(const char *w)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(widget_map); i++) {
+ if (strcmp(widget_map[i].name, w) == 0)
+ return widget_map[i].id;
+ }
+
+ return -EINVAL;
+}
+
+static int tplg_parse_dapm_mixers(snd_config_t *cfg, struct tplg_elem *elem)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *value = NULL;
+
+ tplg_dbg(" DAPM Mixer Controls: %s\n", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+ n = snd_config_iterator_entry(i);
+
+ /* get value */
+ if (snd_config_get_string(n, &value) < 0)
+ continue;
+
+ tplg_ref_add(elem, OBJECT_TYPE_MIXER, value);
+ tplg_dbg("\t\t %s\n", value);
+ }
+
+ return 0;
+}
+
+static int tplg_parse_dapm_enums(snd_config_t *cfg, struct tplg_elem *elem)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *value = NULL;
+
+ tplg_dbg(" DAPM Enum Controls: %s\n", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+ n = snd_config_iterator_entry(i);
+
+ /* get value */
+ if (snd_config_get_string(n, &value) < 0)
+ continue;
+
+ tplg_ref_add(elem, OBJECT_TYPE_ENUM, value);
+ tplg_dbg("\t\t %s\n", value);
+ }
+
+ return 0;
+}
+
+/* move referenced controls to the widget */
+static int copy_dapm_control(struct tplg_elem *elem, struct tplg_elem *ref)
+{
+ struct snd_soc_tplg_dapm_widget *widget = elem->widget;
+ struct snd_soc_tplg_mixer_control *mixer_ctrl = ref->mixer_ctrl;
+ struct snd_soc_tplg_enum_control *enum_ctrl = ref->enum_ctrl;
+
+ tplg_dbg("Control '%s' used by '%s'\n", ref->id, elem->id);
+ tplg_dbg("\tparent size: %d + %d -> %d, priv size -> %d\n",
+ elem->size, ref->size, elem->size + ref->size,
+ widget->priv.size);
+
+ widget = realloc(widget, elem->size + ref->size);
+ if (!widget)
+ return -ENOMEM;
+
+ elem->widget = widget;
+
+ /* copy new widget at the end */
+ if (ref->type == OBJECT_TYPE_MIXER)
+ memcpy((void*)widget + elem->size, mixer_ctrl, ref->size);
+ else if (ref->type == OBJECT_TYPE_ENUM)
+ memcpy((void*)widget + elem->size, enum_ctrl, ref->size);
+
+ elem->size += ref->size;
+ widget->num_kcontrols++;
+ ref->compound_elem = 1;
+ return 0;
+}
+
+/* check referenced controls for a widget */
+static int tplg_build_widget(snd_tplg_t *tplg,
+ struct tplg_elem *elem)
+{
+ struct tplg_ref *ref;
+ struct list_head *base, *pos;
+ int err = 0;
+
+ base = &elem->ref_list;
+
+ /* for each ref in this control elem */
+ list_for_each(pos, base) {
+
+ ref = list_entry(pos, struct tplg_ref, list);
+ if (ref->id == NULL || ref->elem)
+ continue;
+
+ switch (ref->type) {
+ case OBJECT_TYPE_MIXER:
+ ref->elem = tplg_elem_lookup(&tplg->mixer_list,
+ ref->id, OBJECT_TYPE_MIXER);
+ if (ref->elem)
+ err = copy_dapm_control(elem, ref->elem);
+ break;
+
+ case OBJECT_TYPE_ENUM:
+ ref->elem = tplg_elem_lookup(&tplg->enum_list,
+ ref->id, OBJECT_TYPE_ENUM);
+ if (ref->elem)
+ err = copy_dapm_control(elem, ref->elem);
+ break;
+
+ case OBJECT_TYPE_DATA:
+ ref->elem = tplg_elem_lookup(&tplg->pdata_list,
+ ref->id, OBJECT_TYPE_DATA);
+ if (ref->elem)
+ err = tplg_copy_data(elem, ref->elem);
+ break;
+ default:
+ break;
+ }
+
+ if (!ref->elem) {
+ SNDERR("error: cannot find control '%s'"
+ " referenced by widget '%s'\n",
+ ref->id, elem->id);
+ return -EINVAL;
+ }
+
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+int tplg_build_widgets(snd_tplg_t *tplg)
+{
+
+ struct list_head *base, *pos;
+ struct tplg_elem *elem;
+ int err;
+
+ base = &tplg->widget_list;
+ list_for_each(pos, base) {
+
+ elem = list_entry(pos, struct tplg_elem, list);
+ if (!elem->widget || elem->type != OBJECT_TYPE_DAPM_WIDGET) {
+ SNDERR("error: invalid widget '%s'\n",
+ elem->id);
+ return -EINVAL;
+ }
+
+ err = tplg_build_widget(tplg, elem);
+ if (err < 0)
+ return err;
+
+ /* add widget to manifest */
+ tplg->manifest.widget_elems++;
+ }
+
+ return 0;
+}
+
+int tplg_build_routes(snd_tplg_t *tplg)
+{
+ struct list_head *base, *pos;
+ struct tplg_elem *elem;
+ struct snd_soc_tplg_dapm_graph_elem *route;
+
+ base = &tplg->route_list;
+
+ list_for_each(pos, base) {
+ elem = list_entry(pos, struct tplg_elem, list);
+
+ if (!elem->route || elem->type != OBJECT_TYPE_DAPM_GRAPH) {
+ SNDERR("error: invalid route '%s'\n",
+ elem->id);
+ return -EINVAL;
+ }
+
+ route = elem->route;
+ tplg_dbg("\nCheck route: sink '%s', control '%s', source '%s'\n",
+ route->sink, route->control, route->source);
+
+ /* validate sink */
+ if (strlen(route->sink) <= 0) {
+ SNDERR("error: no sink\n");
+ return -EINVAL;
+
+ }
+ if (!tplg_elem_lookup(&tplg->widget_list, route->sink,
+ OBJECT_TYPE_DAPM_WIDGET)) {
+ SNDERR("warning: undefined sink widget/stream '%s'\n",
+ route->sink);
+ }
+
+ /* validate control name */
+ if (strlen(route->control)) {
+ if (!tplg_elem_lookup(&tplg->mixer_list,
+ route->control, OBJECT_TYPE_MIXER) &&
+ !tplg_elem_lookup(&tplg->enum_list,
+ route->control, OBJECT_TYPE_ENUM)) {
+ SNDERR("warning: Undefined mixer/enum control '%s'\n",
+ route->control);
+ }
+ }
+
+ /* validate source */
+ if (strlen(route->source) <= 0) {
+ SNDERR("error: no source\n");
+ return -EINVAL;
+
+ }
+ if (!tplg_elem_lookup(&tplg->widget_list, route->source,
+ OBJECT_TYPE_DAPM_WIDGET)) {
+ SNDERR("warning: Undefined source widget/stream '%s'\n",
+ route->source);
+ }
+
+ /* add graph to manifest */
+ tplg->manifest.graph_elems++;
+ }
+
+ return 0;
+}
+
+#define LINE_SIZE 1024
+
+/* line is defined as '"source, control, sink"' */
+static int tplg_parse_line(const char *text,
+ struct snd_soc_tplg_dapm_graph_elem *line)
+{
+ char buf[LINE_SIZE];
+ unsigned int len, i;
+ const char *source = NULL, *sink = NULL, *control = NULL;
+
+ elem_copy_text(buf, text, LINE_SIZE);
+
+ len = strlen(buf);
+ if (len <= 2) {
+ SNDERR("error: invalid route \"%s\"\n", buf);
+ return -EINVAL;
+ }
+
+ /* find first , */
+ for (i = 1; i < len; i++) {
+ if (buf[i] == ',')
+ goto second;
+ }
+ SNDERR("error: invalid route \"%s\"\n", buf);
+ return -EINVAL;
+
+second:
+ /* find second , */
+ sink = buf;
+ control = &buf[i + 2];
+ buf[i] = 0;
+
+ for (; i < len; i++) {
+ if (buf[i] == ',')
+ goto done;
+ }
+
+ SNDERR("error: invalid route \"%s\"\n", buf);
+ return -EINVAL;
+
+done:
+ buf[i] = 0;
+ source = &buf[i + 2];
+
+ strcpy(line->source, source);
+ strcpy(line->control, control);
+ strcpy(line->sink, sink);
+ return 0;
+}
+
+
+static int tplg_parse_routes(snd_tplg_t *tplg, snd_config_t *cfg)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ struct tplg_elem *elem;
+ struct snd_soc_tplg_dapm_graph_elem *line = NULL;
+ int err;
+
+ snd_config_for_each(i, next, cfg) {
+ const char *val;
+
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_string(n, &val) < 0)
+ continue;
+
+ elem = tplg_elem_new();
+ if (!elem)
+ return -ENOMEM;
+
+ list_add_tail(&elem->list, &tplg->route_list);
+ strcpy(elem->id, "line");
+ elem->type = OBJECT_TYPE_DAPM_GRAPH;
+ elem->size = sizeof(*line);
+
+ line = calloc(1, sizeof(*line));
+ if (!line)
+ return -ENOMEM;
+
+ elem->route = line;
+
+ err = tplg_parse_line(val, line);
+ if (err < 0)
+ return err;
+
+ tplg_dbg("route: sink '%s', control '%s', source '%s'\n",
+ line->sink, line->control, line->source);
+ }
+
+ return 0;
+}
+
+int tplg_parse_dapm_graph(snd_tplg_t *tplg, snd_config_t *cfg,
+ void *private ATTRIBUTE_UNUSED)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ int err;
+ const char *graph_id;
+
+ if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
+ SNDERR("error: compound is expected for dapm graph definition\n");
+ return -EINVAL;
+ }
+
+ snd_config_get_id(cfg, &graph_id);
+
+ snd_config_for_each(i, next, cfg) {
+ const char *id;
+
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0) {
+ continue;
+ }
+
+ if (strcmp(id, "lines") == 0) {
+ err = tplg_parse_routes(tplg, n);
+ if (err < 0) {
+ SNDERR("error: failed to parse dapm graph %s\n",
+ graph_id);
+ return err;
+ }
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/* DAPM Widget */
+int tplg_parse_dapm_widget(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
+{
+ struct snd_soc_tplg_dapm_widget *widget;
+ struct tplg_elem *elem;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id, *val = NULL;
+ int widget_type, err;
+
+ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_DAPM_WIDGET);
+ if (!elem)
+ return -ENOMEM;
+
+ tplg_dbg(" Widget: %s\n", elem->id);
+
+ widget = elem->widget;
+ elem_copy_text(widget->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+ widget->size = elem->size;
+
+ snd_config_for_each(i, next, cfg) {
+
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* skip comments */
+ if (strcmp(id, "comment") == 0)
+ continue;
+ if (id[0] == '#')
+ continue;
+
+ if (strcmp(id, "index") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ elem->index = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, elem->index);
+ continue;
+ }
+
+ if (strcmp(id, "type") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ widget_type = lookup_widget(val);
+ if (widget_type < 0){
+ SNDERR("Widget '%s': Unsupported widget type %s\n",
+ elem->id, val);
+ return -EINVAL;
+ }
+
+ widget->id = widget_type;
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+ }
+
+ if (strcmp(id, "no_pm") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ if (strcmp(val, "true") == 0)
+ widget->reg = -1;
+
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+ }
+
+ if (strcmp(id, "shift") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ widget->shift = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, widget->shift);
+ continue;
+ }
+
+ if (strcmp(id, "reg") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ widget->reg = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, widget->reg);
+ continue;
+ }
+
+ if (strcmp(id, "invert") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ widget->invert = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, widget->invert);
+ continue;
+ }
+
+ if (strcmp(id, "subseq") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ widget->subseq= atoi(val);
+ tplg_dbg("\t%s: %d\n", id, widget->subseq);
+ continue;
+ }
+
+ if (strcmp(id, "event_type") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ widget->event_type = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, widget->event_type);
+ continue;
+ }
+
+ if (strcmp(id, "event_flags") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ widget->event_flags = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, widget->event_flags);
+ continue;
+ }
+
+ if (strcmp(id, "enum") == 0) {
+ err = tplg_parse_dapm_enums(n, elem);
+ if (err < 0)
+ return err;
+
+ continue;
+ }
+
+ if (strcmp(id, "mixer") == 0) {
+ err = tplg_parse_dapm_mixers(n, elem);
+ if (err < 0)
+ return err;
+
+ continue;
+ }
+
+ if (strcmp(id, "data") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ tplg_ref_add(elem, OBJECT_TYPE_DATA, val);
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+ }
+ }
+
+ return 0;
+}
--
2.5.0

View File

@ -0,0 +1,636 @@
From 694b857ce7b44a333c4f5e8b12f1b6cdf1c12388 Mon Sep 17 00:00:00 2001
From: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Date: Wed, 29 Jul 2015 17:45:20 +0100
Subject: [PATCH 42/49] topology: Add CTL parser
Add support to parse mixers, enums and byte controls.
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
src/topology/ctl.c | 613 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 613 insertions(+)
create mode 100644 src/topology/ctl.c
diff --git a/src/topology/ctl.c b/src/topology/ctl.c
new file mode 100644
index 000000000000..9c1333c1fc88
--- /dev/null
+++ b/src/topology/ctl.c
@@ -0,0 +1,613 @@
+/*
+ Copyright(c) 2014-2015 Intel Corporation
+ All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ Authors: Mengdong Lin <mengdong.lin@intel.com>
+ Yao Jin <yao.jin@intel.com>
+ Liam Girdwood <liam.r.girdwood@linux.intel.com>
+*/
+
+#include "list.h"
+#include "tplg_local.h"
+
+/* copy referenced TLV to the mixer control */
+static int copy_tlv(struct tplg_elem *elem, struct tplg_elem *ref)
+{
+ struct snd_soc_tplg_mixer_control *mixer_ctrl = elem->mixer_ctrl;
+ struct snd_soc_tplg_ctl_tlv *tlv = ref->tlv;
+
+ tplg_dbg("TLV '%s' used by '%s\n", ref->id, elem->id);
+
+ /* TLV has a fixed size */
+ mixer_ctrl->tlv = *tlv;
+
+ /* set size of TLV data */
+ mixer_ctrl->hdr.tlv_size = tlv->count * sizeof(uint32_t);
+ return 0;
+}
+
+/* check referenced TLV for a mixer control */
+static int tplg_build_mixer_control(snd_tplg_t *tplg,
+ struct tplg_elem *elem)
+{
+ struct tplg_ref *ref;
+ struct list_head *base, *pos;
+ int err = 0;
+
+ base = &elem->ref_list;
+
+ /* for each ref in this control elem */
+ list_for_each(pos, base) {
+
+ ref = list_entry(pos, struct tplg_ref, list);
+ if (ref->id == NULL || ref->elem)
+ continue;
+
+ if (ref->type == OBJECT_TYPE_TLV) {
+ ref->elem = tplg_elem_lookup(&tplg->tlv_list,
+ ref->id, OBJECT_TYPE_TLV);
+ if (ref->elem)
+ err = copy_tlv(elem, ref->elem);
+
+ } else if (ref->type == OBJECT_TYPE_DATA) {
+ ref->elem = tplg_elem_lookup(&tplg->pdata_list,
+ ref->id, OBJECT_TYPE_DATA);
+ err = tplg_copy_data(elem, ref->elem);
+ }
+
+ if (!ref->elem) {
+ SNDERR("error: cannot find '%s' referenced by"
+ " control '%s'\n", ref->id, elem->id);
+ return -EINVAL;
+ } else if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static void copy_enum_texts(struct tplg_elem *enum_elem,
+ struct tplg_elem *ref_elem)
+{
+ struct snd_soc_tplg_enum_control *ec = enum_elem->enum_ctrl;
+
+ memcpy(ec->texts, ref_elem->texts,
+ SND_SOC_TPLG_NUM_TEXTS * SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+}
+
+/* check referenced text for a enum control */
+static int tplg_build_enum_control(snd_tplg_t *tplg,
+ struct tplg_elem *elem)
+{
+ struct tplg_ref *ref;
+ struct list_head *base, *pos;
+ int err = 0;
+
+ base = &elem->ref_list;
+
+ list_for_each(pos, base) {
+
+ ref = list_entry(pos, struct tplg_ref, list);
+ if (ref->id == NULL || ref->elem)
+ continue;
+
+ if (ref->type == OBJECT_TYPE_TEXT) {
+ ref->elem = tplg_elem_lookup(&tplg->text_list,
+ ref->id, OBJECT_TYPE_TEXT);
+ if (ref->elem)
+ copy_enum_texts(elem, ref->elem);
+
+ } else if (ref->type == OBJECT_TYPE_DATA) {
+ ref->elem = tplg_elem_lookup(&tplg->pdata_list,
+ ref->id, OBJECT_TYPE_DATA);
+ err = tplg_copy_data(elem, ref->elem);
+ }
+ if (!ref->elem) {
+ SNDERR("error: cannot find '%s' referenced by"
+ " control '%s'\n", ref->id, elem->id);
+ return -EINVAL;
+ } else if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/* check referenced private data for a byte control */
+static int tplg_build_bytes_control(snd_tplg_t *tplg, struct tplg_elem *elem)
+{
+ struct tplg_ref *ref;
+ struct list_head *base, *pos;
+
+ base = &elem->ref_list;
+
+ list_for_each(pos, base) {
+
+ ref = list_entry(pos, struct tplg_ref, list);
+ if (ref->id == NULL || ref->elem)
+ continue;
+
+ /* bytes control only reference one private data section */
+ ref->elem = tplg_elem_lookup(&tplg->pdata_list,
+ ref->id, OBJECT_TYPE_DATA);
+ if (!ref->elem) {
+ SNDERR("error: cannot find data '%s'"
+ " referenced by control '%s'\n",
+ ref->id, elem->id);
+ return -EINVAL;
+ }
+
+ /* copy texts to enum elem */
+ return tplg_copy_data(elem, ref->elem);
+ }
+
+ return 0;
+}
+
+int tplg_build_controls(snd_tplg_t *tplg)
+{
+ struct list_head *base, *pos;
+ struct tplg_elem *elem;
+ int err = 0;
+
+ base = &tplg->mixer_list;
+ list_for_each(pos, base) {
+
+ elem = list_entry(pos, struct tplg_elem, list);
+ err = tplg_build_mixer_control(tplg, elem);
+ if (err < 0)
+ return err;
+
+ /* add control to manifest */
+ tplg->manifest.control_elems++;
+ }
+
+ base = &tplg->enum_list;
+ list_for_each(pos, base) {
+
+ elem = list_entry(pos, struct tplg_elem, list);
+ err = tplg_build_enum_control(tplg, elem);
+ if (err < 0)
+ return err;
+
+ /* add control to manifest */
+ tplg->manifest.control_elems++;
+ }
+
+ base = &tplg->bytes_ext_list;
+ list_for_each(pos, base) {
+
+ elem = list_entry(pos, struct tplg_elem, list);
+ err = tplg_build_bytes_control(tplg, elem);
+ if (err < 0)
+ return err;
+
+ /* add control to manifest */
+ tplg->manifest.control_elems++;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Parse TLV of DBScale type.
+ *
+ * Parse DBScale describing min, step, mute in DB.
+ */
+static int tplg_parse_tlv_dbscale(snd_config_t *cfg, struct tplg_elem *elem)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ struct snd_soc_tplg_ctl_tlv *tplg_tlv;
+ const char *id = NULL, *value = NULL;
+ int *data;
+
+ tplg_dbg(" scale: %s\n", elem->id);
+
+ tplg_tlv = calloc(1, sizeof(*tplg_tlv));
+ if (!tplg_tlv)
+ return -ENOMEM;
+ data = (int*)(tplg_tlv->data);
+
+ elem->tlv = tplg_tlv;
+ tplg_tlv->numid = SNDRV_CTL_TLVT_DB_SCALE;
+ tplg_tlv->count = 8;
+ tplg_tlv->size = sizeof(*tplg_tlv);
+
+ snd_config_for_each(i, next, cfg) {
+
+ n = snd_config_iterator_entry(i);
+
+ /* get ID */
+ if (snd_config_get_id(n, &id) < 0) {
+ SNDERR("error: cant get ID\n");
+ return -EINVAL;
+ }
+
+ /* get value */
+ if (snd_config_get_string(n, &value) < 0)
+ continue;
+
+ tplg_dbg("\t%s = %s\n", id, value);
+
+ /* get TLV data */
+ if (strcmp(id, "min") == 0)
+ data[0] = atoi(value);
+ else if (strcmp(id, "step") == 0)
+ data[1] = atoi(value);
+ else if (strcmp(id, "mute") == 0)
+ data[2] = atoi(value);
+ else
+ SNDERR("error: unknown key %s\n", id);
+ }
+
+ return 0;
+}
+
+/* Parse TLV */
+int tplg_parse_tlv(snd_tplg_t *tplg, snd_config_t *cfg,
+ void *private ATTRIBUTE_UNUSED)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id;
+ int err = 0;
+ struct tplg_elem *elem;
+
+ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_TLV);
+ if (!elem)
+ return -ENOMEM;
+
+ snd_config_for_each(i, next, cfg) {
+
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ if (strcmp(id, "scale") == 0) {
+ err = tplg_parse_tlv_dbscale(n, elem);
+ if (err < 0) {
+ SNDERR("error: failed to DBScale");
+ return err;
+ }
+ continue;
+ }
+ }
+
+ return err;
+}
+
+/* Parse Control Bytes */
+int tplg_parse_control_bytes(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
+{
+ struct snd_soc_tplg_bytes_control *be;
+ struct tplg_elem *elem;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id, *val = NULL;
+ int err;
+
+ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_BYTES);
+ if (!elem)
+ return -ENOMEM;
+
+ be = elem->bytes_ext;
+ be->size = elem->size;
+ elem_copy_text(be->hdr.name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+ be->hdr.type = SND_SOC_TPLG_TYPE_BYTES;
+
+ tplg_dbg(" Control Bytes: %s\n", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* skip comments */
+ if (strcmp(id, "comment") == 0)
+ continue;
+ if (id[0] == '#')
+ continue;
+
+ if (strcmp(id, "index") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ elem->index = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, elem->index);
+ continue;
+ }
+
+ if (strcmp(id, "base") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ be->base = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, be->base);
+ continue;
+ }
+
+ if (strcmp(id, "num_regs") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ be->num_regs = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, be->num_regs);
+ continue;
+ }
+
+ if (strcmp(id, "max") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ be->max = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, be->num_regs);
+ continue;
+ }
+
+ if (strcmp(id, "mask") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ be->mask = strtol(val, NULL, 16);
+ tplg_dbg("\t%s: %d\n", id, be->mask);
+ continue;
+ }
+
+ if (strcmp(id, "data") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ tplg_ref_add(elem, OBJECT_TYPE_DATA, val);
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+ }
+
+ if (strcmp(id, "tlv") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ err = tplg_ref_add(elem, OBJECT_TYPE_TLV, val);
+ if (err < 0)
+ return err;
+
+ be->hdr.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/* Parse Control Enums. */
+int tplg_parse_control_enum(snd_tplg_t *tplg, snd_config_t *cfg,
+ void *private ATTRIBUTE_UNUSED)
+{
+ struct snd_soc_tplg_enum_control *ec;
+ struct tplg_elem *elem;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id, *val = NULL;
+ int err, j;
+
+ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_ENUM);
+ if (!elem)
+ return -ENOMEM;
+
+ /* init new mixer */
+ ec = elem->enum_ctrl;
+ elem_copy_text(ec->hdr.name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+ ec->hdr.type = SND_SOC_TPLG_TYPE_ENUM;
+ ec->size = elem->size;
+ tplg->channel_idx = 0;
+
+ /* set channel reg to default state */
+ for (j = 0; j < SND_SOC_TPLG_MAX_CHAN; j++)
+ ec->channel[j].reg = -1;
+
+ tplg_dbg(" Control Enum: %s\n", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* skip comments */
+ if (strcmp(id, "comment") == 0)
+ continue;
+ if (id[0] == '#')
+ continue;
+
+ if (strcmp(id, "index") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ elem->index = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, elem->index);
+ continue;
+ }
+
+ if (strcmp(id, "texts") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ tplg_ref_add(elem, OBJECT_TYPE_TEXT, val);
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+ }
+
+ if (strcmp(id, "channel") == 0) {
+ if (ec->num_channels >= SND_SOC_TPLG_MAX_CHAN) {
+ SNDERR("error: too many channels %s\n",
+ elem->id);
+ return -EINVAL;
+ }
+
+ err = tplg_parse_compound(tplg, n, tplg_parse_channel,
+ ec->channel);
+ if (err < 0)
+ return err;
+
+ ec->num_channels = tplg->channel_idx;
+ continue;
+ }
+
+ if (strcmp(id, "ops") == 0) {
+ err = tplg_parse_compound(tplg, n, tplg_parse_ops,
+ &ec->hdr);
+ if (err < 0)
+ return err;
+ continue;
+ }
+
+ if (strcmp(id, "data") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ tplg_ref_add(elem, OBJECT_TYPE_DATA, val);
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/* Parse Controls.
+ *
+ * Mixer control. Supports multiple channels.
+ */
+int tplg_parse_control_mixer(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
+{
+ struct snd_soc_tplg_mixer_control *mc;
+ struct tplg_elem *elem;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id, *val = NULL;
+ int err, j;
+
+ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_MIXER);
+ if (!elem)
+ return -ENOMEM;
+
+ /* init new mixer */
+ mc = elem->mixer_ctrl;
+ elem_copy_text(mc->hdr.name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+ mc->hdr.type = SND_SOC_TPLG_TYPE_MIXER;
+ mc->size = elem->size;
+ tplg->channel_idx = 0;
+
+ /* set channel reg to default state */
+ for (j = 0; j < SND_SOC_TPLG_MAX_CHAN; j++)
+ mc->channel[j].reg = -1;
+
+ tplg_dbg(" Control Mixer: %s\n", elem->id);
+
+ /* giterate trough each mixer elment */
+ snd_config_for_each(i, next, cfg) {
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* skip comments */
+ if (strcmp(id, "comment") == 0)
+ continue;
+ if (id[0] == '#')
+ continue;
+
+ if (strcmp(id, "index") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ elem->index = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, elem->index);
+ continue;
+ }
+
+ if (strcmp(id, "channel") == 0) {
+ if (mc->num_channels >= SND_SOC_TPLG_MAX_CHAN) {
+ SNDERR("error: too many channels %s\n",
+ elem->id);
+ return -EINVAL;
+ }
+
+ err = tplg_parse_compound(tplg, n, tplg_parse_channel,
+ mc->channel);
+ if (err < 0)
+ return err;
+
+ mc->num_channels = tplg->channel_idx;
+ continue;
+ }
+
+ if (strcmp(id, "max") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ mc->max = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, mc->max);
+ continue;
+ }
+
+ if (strcmp(id, "invert") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ if (strcmp(val, "true") == 0)
+ mc->invert = 1;
+ else if (strcmp(val, "false") == 0)
+ mc->invert = 0;
+
+ tplg_dbg("\t%s: %d\n", id, mc->invert);
+ continue;
+ }
+
+ if (strcmp(id, "ops") == 0) {
+ err = tplg_parse_compound(tplg, n, tplg_parse_ops,
+ &mc->hdr);
+ if (err < 0)
+ return err;
+ continue;
+ }
+
+ if (strcmp(id, "tlv") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ err = tplg_ref_add(elem, OBJECT_TYPE_TLV, val);
+ if (err < 0)
+ return err;
+
+ mc->hdr.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+ }
+
+ if (strcmp(id, "data") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ tplg_ref_add(elem, OBJECT_TYPE_DATA, val);
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+ }
+ }
+
+ return 0;
+}
--
2.5.0

View File

@ -0,0 +1,145 @@
From 9764a4b891737e6b4363c09b5e5ce8384acecc11 Mon Sep 17 00:00:00 2001
From: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Date: Wed, 29 Jul 2015 17:45:21 +0100
Subject: [PATCH 43/49] topology: Add Channel map parser.
Add support for parsing channel map to control registers.
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
src/topology/channel.c | 122 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 122 insertions(+)
create mode 100644 src/topology/channel.c
diff --git a/src/topology/channel.c b/src/topology/channel.c
new file mode 100644
index 000000000000..9bc5d5a77c57
--- /dev/null
+++ b/src/topology/channel.c
@@ -0,0 +1,122 @@
+/*
+ Copyright(c) 2014-2015 Intel Corporation
+ All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ Authors: Mengdong Lin <mengdong.lin@intel.com>
+ Yao Jin <yao.jin@intel.com>
+ Liam Girdwood <liam.r.girdwood@linux.intel.com>
+*/
+
+#include "list.h"
+#include "tplg_local.h"
+
+/* mapping of channel text names to types */
+static const struct map_elem channel_map[] = {
+ {"mono", SNDRV_CHMAP_MONO}, /* mono stream */
+ {"fl", SNDRV_CHMAP_FL}, /* front left */
+ {"fr", SNDRV_CHMAP_FR}, /* front right */
+ {"rl", SNDRV_CHMAP_RL}, /* rear left */
+ {"rr", SNDRV_CHMAP_RR}, /* rear right */
+ {"fc", SNDRV_CHMAP_FC}, /* front center */
+ {"lfe", SNDRV_CHMAP_LFE}, /* LFE */
+ {"sl", SNDRV_CHMAP_SL}, /* side left */
+ {"sr", SNDRV_CHMAP_SR}, /* side right */
+ {"rc", SNDRV_CHMAP_RC}, /* rear center */
+ {"flc", SNDRV_CHMAP_FLC}, /* front left center */
+ {"frc", SNDRV_CHMAP_FRC}, /* front right center */
+ {"rlc", SNDRV_CHMAP_RLC}, /* rear left center */
+ {"rrc", SNDRV_CHMAP_RRC}, /* rear right center */
+ {"flw", SNDRV_CHMAP_FLW}, /* front left wide */
+ {"frw", SNDRV_CHMAP_FRW}, /* front right wide */
+ {"flh", SNDRV_CHMAP_FLH}, /* front left high */
+ {"fch", SNDRV_CHMAP_FCH}, /* front center high */
+ {"frh", SNDRV_CHMAP_FRH}, /* front right high */
+ {"tc", SNDRV_CHMAP_TC}, /* top center */
+ {"tfl", SNDRV_CHMAP_TFL}, /* top front left */
+ {"tfr", SNDRV_CHMAP_TFR}, /* top front right */
+ {"tfc", SNDRV_CHMAP_TFC}, /* top front center */
+ {"trl", SNDRV_CHMAP_TRL}, /* top rear left */
+ {"trr", SNDRV_CHMAP_TRR}, /* top rear right */
+ {"trc", SNDRV_CHMAP_TRC}, /* top rear center */
+ {"tflc", SNDRV_CHMAP_TFLC}, /* top front left center */
+ {"tfrc", SNDRV_CHMAP_TFRC}, /* top front right center */
+ {"tsl", SNDRV_CHMAP_TSL}, /* top side left */
+ {"tsr", SNDRV_CHMAP_TSR}, /* top side right */
+ {"llfe", SNDRV_CHMAP_LLFE}, /* left LFE */
+ {"rlfe", SNDRV_CHMAP_RLFE}, /* right LFE */
+ {"bc", SNDRV_CHMAP_BC}, /* bottom center */
+ {"blc", SNDRV_CHMAP_BLC}, /* bottom left center */
+ {"brc", SNDRV_CHMAP_BRC}, /* bottom right center */
+};
+
+
+static int lookup_channel(const char *c)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(channel_map); i++) {
+ if (strcasecmp(channel_map[i].name, c) == 0) {
+ return channel_map[i].id;
+ }
+ }
+
+ return -EINVAL;
+}
+
+/* Parse a channel mapping. */
+int tplg_parse_channel(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ struct snd_soc_tplg_channel *channel = private;
+ const char *id, *value;
+
+ if (tplg->channel_idx >= SND_SOC_TPLG_MAX_CHAN)
+ return -EINVAL;
+
+ channel += tplg->channel_idx;
+ snd_config_get_id(cfg, &id);
+ tplg_dbg("\tChannel %s at index %d\n", id, tplg->channel_idx);
+
+ channel->id = lookup_channel(id);
+ if (channel->id < 0) {
+ SNDERR("error: invalid channel %s\n", id);
+ return -EINVAL;
+ }
+
+ channel->size = sizeof(*channel);
+ tplg_dbg("\tChan %s = %d\n", id, channel->id);
+
+ snd_config_for_each(i, next, cfg) {
+
+ n = snd_config_iterator_entry(i);
+
+ /* get id */
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* get value */
+ if (snd_config_get_string(n, &value) < 0)
+ continue;
+
+ if (strcmp(id, "reg") == 0)
+ channel->reg = atoi(value);
+ else if (strcmp(id, "shift") == 0)
+ channel->shift = atoi(value);
+
+ tplg_dbg("\t\t%s = %s\n", id, value);
+ }
+
+ tplg->channel_idx++;
+ return 0;
+}
--
2.5.0

View File

@ -0,0 +1,350 @@
From 1d1dff56767842a99d2e06a6997079c0516dec68 Mon Sep 17 00:00:00 2001
From: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Date: Wed, 29 Jul 2015 17:45:22 +0100
Subject: [PATCH 44/49] topology: Add binary file builder.
Build the binary output file from all the locally parsed objects and elements.
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
src/topology/builder.c | 327 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 327 insertions(+)
create mode 100644 src/topology/builder.c
diff --git a/src/topology/builder.c b/src/topology/builder.c
new file mode 100644
index 000000000000..0066b220353c
--- /dev/null
+++ b/src/topology/builder.c
@@ -0,0 +1,327 @@
+/*
+ Copyright(c) 2014-2015 Intel Corporation
+ All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ Authors: Mengdong Lin <mengdong.lin@intel.com>
+ Yao Jin <yao.jin@intel.com>
+ Liam Girdwood <liam.r.girdwood@linux.intel.com>
+*/
+
+#include "list.h"
+#include "tplg_local.h"
+
+/* verbose output detailing each object size and file position */
+static void verbose(snd_tplg_t *tplg, const char *fmt, ...)
+{
+ int offset;
+ va_list va;
+
+ if (!tplg->verbose)
+ return;
+
+ offset = lseek(tplg->out_fd, 0, SEEK_CUR);
+
+ va_start(va, fmt);
+ fprintf(stdout, "0x%6.6x/%6.6d -", offset, offset);
+ vfprintf(stdout, fmt, va);
+ va_end(va);
+}
+
+/* write out block header to output file */
+static int write_block_header(snd_tplg_t *tplg, unsigned int type,
+ unsigned int vendor_type, unsigned int version, unsigned int index,
+ size_t payload_size, int count)
+{
+ struct snd_soc_tplg_hdr hdr;
+ size_t bytes;
+ int offset = lseek(tplg->out_fd, 0, SEEK_CUR);
+
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.magic = SND_SOC_TPLG_MAGIC;
+ hdr.abi = SND_SOC_TPLG_ABI_VERSION;
+ hdr.type = type;
+ hdr.vendor_type = vendor_type;
+ hdr.version = version;
+ hdr.payload_size = payload_size;
+ hdr.index = index;
+ hdr.size = sizeof(hdr);
+ hdr.count = count;
+
+ /* make sure file offset is aligned with the calculated HDR offset */
+ if ((unsigned int)offset != tplg->next_hdr_pos) {
+ SNDERR("error: New header is at offset 0x%x but file"
+ " offset 0x%x is %s by %d bytes\n",
+ tplg->next_hdr_pos, offset,
+ (unsigned int)offset > tplg->next_hdr_pos ? "ahead" : "behind",
+ abs(offset - tplg->next_hdr_pos));
+ exit(-EINVAL);
+ }
+
+ verbose(tplg, " header type %d size 0x%lx/%ld vendor %d "
+ "version %d\n", type, (long unsigned int)payload_size,
+ (long int)payload_size, vendor_type, version);
+
+ tplg->next_hdr_pos += hdr.payload_size + sizeof(hdr);
+
+ bytes = write(tplg->out_fd, &hdr, sizeof(hdr));
+ if (bytes != sizeof(hdr)) {
+ SNDERR("error: can't write section header %lu\n",
+ (long unsigned int)bytes);
+ return bytes;
+ }
+
+ return bytes;
+}
+
+static int write_data_block(snd_tplg_t *tplg, int size, int tplg_type,
+ const char *obj_name, void *data)
+{
+ int ret;
+
+ /* write the header for this block */
+ ret = write_block_header(tplg, tplg_type, 0,
+ SND_SOC_TPLG_ABI_VERSION, 0, size, 1);
+ if (ret < 0) {
+ SNDERR("error: failed to write %s block %d\n", obj_name, ret);
+ return ret;
+ }
+
+ verbose(tplg, " %s : write %d bytes\n", obj_name, size);
+
+ ret = write(tplg->out_fd, data, size);
+ if (ret < 0) {
+ SNDERR("error: failed to write %s %d\n", obj_name, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int write_elem_block(snd_tplg_t *tplg,
+ struct list_head *base, int size, int tplg_type, const char *obj_name)
+{
+ struct list_head *pos;
+ struct tplg_elem *elem;
+ int ret, wsize = 0, count = 0, vendor_type;
+
+ /* count number of elements */
+ list_for_each(pos, base)
+ count++;
+
+ /* write the header for this block */
+ list_for_each(pos, base) {
+ elem = list_entry(pos, struct tplg_elem, list);
+ vendor_type = elem->vendor_type;
+ break;
+ }
+
+ ret = write_block_header(tplg, tplg_type, vendor_type,
+ SND_SOC_TPLG_ABI_VERSION, 0, size, count);
+ if (ret < 0) {
+ SNDERR("error: failed to write %s block %d\n",
+ obj_name, ret);
+ return ret;
+ }
+
+ /* write each elem to block */
+ list_for_each(pos, base) {
+
+ elem = list_entry(pos, struct tplg_elem, list);
+
+ /* compound elems have already been copied to other elems */
+ if (elem->compound_elem)
+ continue;
+
+ if (elem->type != OBJECT_TYPE_DAPM_GRAPH)
+ verbose(tplg, " %s '%s': write %d bytes\n",
+ obj_name, elem->id, elem->size);
+ else
+ verbose(tplg, " %s '%s': write %d bytes\n",
+ obj_name, elem->route->source, elem->size);
+
+ count = write(tplg->out_fd, elem->obj, elem->size);
+ if (count < 0) {
+ SNDERR("error: failed to write %s %d\n",
+ obj_name, ret);
+ return ret;
+ }
+
+ wsize += count;
+ }
+
+ /* make sure we have written the correct size */
+ if (wsize != size) {
+ SNDERR("error: size mismatch. Expected %d wrote %d\n",
+ size, wsize);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int calc_block_size(struct list_head *base)
+{
+ struct list_head *pos;
+ struct tplg_elem *elem;
+ int size = 0;
+
+ list_for_each(pos, base) {
+
+ elem = list_entry(pos, struct tplg_elem, list);
+
+ /* compound elems have already been copied to other elems */
+ if (elem->compound_elem)
+ continue;
+
+ size += elem->size;
+ }
+
+ return size;
+}
+
+static int write_block(snd_tplg_t *tplg, struct list_head *base,
+ int type)
+{
+ int size;
+
+ /* calculate the block size in bytes for all elems in this list */
+ size = calc_block_size(base);
+ if (size <= 0)
+ return size;
+
+ verbose(tplg, " block size for type %d is %d\n", type, size);
+
+ /* write each elem for this block */
+ switch (type) {
+ case OBJECT_TYPE_MIXER:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_MIXER, "mixer");
+ case OBJECT_TYPE_BYTES:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_BYTES, "bytes");
+ case OBJECT_TYPE_ENUM:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_ENUM, "enum");
+ case OBJECT_TYPE_DAPM_GRAPH:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_DAPM_GRAPH, "route");
+ case OBJECT_TYPE_DAPM_WIDGET:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_DAPM_WIDGET, "widget");
+ case OBJECT_TYPE_PCM:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_PCM, "pcm");
+ case OBJECT_TYPE_BE:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_DAI_LINK, "be");
+ case OBJECT_TYPE_CC:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_DAI_LINK, "cc");
+ case OBJECT_TYPE_MANIFEST:
+ return write_data_block(tplg, size, SND_SOC_TPLG_TYPE_MANIFEST,
+ "manifest", &tplg->manifest);
+ case OBJECT_TYPE_DATA:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_PDATA, "data");
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int tplg_write_data(snd_tplg_t *tplg)
+{
+ int ret;
+
+ /* write manifest */
+ ret = write_data_block(tplg, sizeof(tplg->manifest),
+ OBJECT_TYPE_MANIFEST, "manifest", &tplg->manifest);
+ if (ret < 0) {
+ SNDERR("failed to write manifest %d\n", ret);
+ return ret;
+ }
+
+ /* write mixer elems. */
+ ret = write_block(tplg, &tplg->mixer_list,
+ OBJECT_TYPE_MIXER);
+ if (ret < 0) {
+ SNDERR("failed to write control elems %d\n", ret);
+ return ret;
+ }
+
+ /* write enum control elems. */
+ ret = write_block(tplg, &tplg->enum_list,
+ OBJECT_TYPE_ENUM);
+ if (ret < 0) {
+ SNDERR("failed to write control elems %d\n", ret);
+ return ret;
+ }
+
+ /* write bytes extended control elems. */
+ ret = write_block(tplg, &tplg->bytes_ext_list,
+ OBJECT_TYPE_BYTES);
+ if (ret < 0) {
+ SNDERR("failed to write control elems %d\n", ret);
+ return ret;
+ }
+
+ /* write widget elems */
+ ret = write_block(tplg, &tplg->widget_list,
+ OBJECT_TYPE_DAPM_WIDGET);
+ if (ret < 0) {
+ SNDERR("failed to write widget elems %d\n", ret);
+ return ret;
+ }
+
+ /* write pcm elems */
+ ret = write_block(tplg, &tplg->pcm_list,
+ OBJECT_TYPE_PCM);
+ if (ret < 0) {
+ SNDERR("failed to write pcm elems %d\n", ret);
+ return ret;
+ }
+
+ /* write be elems */
+ ret = write_block(tplg, &tplg->be_list,
+ OBJECT_TYPE_BE);
+ if (ret < 0) {
+ SNDERR("failed to write be elems %d\n", ret);
+ return ret;
+ }
+
+ /* write cc elems */
+ ret = write_block(tplg, &tplg->cc_list,
+ OBJECT_TYPE_CC);
+ if (ret < 0) {
+ SNDERR("failed to write cc elems %d\n", ret);
+ return ret;
+ }
+
+ /* write route elems */
+ ret = write_block(tplg, &tplg->route_list,
+ OBJECT_TYPE_DAPM_GRAPH);
+ if (ret < 0) {
+ SNDERR("failed to write graph elems %d\n", ret);
+ return ret;
+ }
+
+ /* write private data */
+ ret = write_block(tplg, &tplg->pdata_list,
+ OBJECT_TYPE_DATA);
+ if (ret < 0) {
+ SNDERR("failed to write private data %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
--
2.5.0

View File

@ -0,0 +1,125 @@
From fec1e8f25374ec8eb4d57ee43e94e9689a748678 Mon Sep 17 00:00:00 2001
From: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Date: Wed, 29 Jul 2015 17:45:23 +0100
Subject: [PATCH 45/49] topology: autotools: Add build support for topology
core
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
configure.ac | 9 ++++++++-
include/Makefile.am | 4 ++++
src/Makefile.am | 7 +++++++
src/topology/Makefile.am | 19 +++++++++++++++++++
4 files changed, 38 insertions(+), 1 deletion(-)
create mode 100644 src/topology/Makefile.am
diff --git a/configure.ac b/configure.ac
index 9621d4e9ec2b..b6bea2dca434 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 \
diff --git a/include/Makefile.am b/include/Makefile.am
index 4baa03af69e1..ff931fda24d5 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 fa255ff43ee0..57686a612fd8 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 000000000000..3fb8bf7a9290
--- /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.5.0

View File

@ -0,0 +1,60 @@
From 22603237b09ed50744030f550248ade135d4f73b Mon Sep 17 00:00:00 2001
From: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Date: Wed, 29 Jul 2015 17:45:24 +0100
Subject: [PATCH 46/49] topology: doxygen: Add doxygen support for topology
core.
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
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 043e75b2d7eb..92bd52ba67a2 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 7d049fe5c32a..b40c75a5239b 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.5.0

View File

@ -0,0 +1,443 @@
From 00a51b5bacb0f966d0e323bd9d3057c0eb0e6f23 Mon Sep 17 00:00:00 2001
From: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Date: Wed, 29 Jul 2015 17:45:25 +0100
Subject: [PATCH 47/49] conf: topology: Add topology file for broadwell audio
DSP
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
configure.ac | 2 +
src/conf/Makefile.am | 2 +-
src/conf/topology/Makefile.am | 1 +
src/conf/topology/broadwell/Makefile.am | 4 +
src/conf/topology/broadwell/broadwell.conf | 375 +++++++++++++++++++++++++++++
5 files changed, 383 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/configure.ac b/configure.ac
index b6bea2dca434..a482b3e7f6ca 100644
--- a/configure.ac
+++ b/configure.ac
@@ -663,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/src/conf/Makefile.am b/src/conf/Makefile.am
index 948d5a1c822e..a04f73fddc65 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 000000000000..f56a96c651e5
--- /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 000000000000..35d1e83cb645
--- /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 000000000000..05b3889bec58
--- /dev/null
+++ b/src/conf/topology/broadwell/broadwell.conf
@@ -0,0 +1,375 @@
+# Dynamic Firmware Configuration for Broadwell
+
+# TLV
+SectionTLV."hsw_vol_tlv" {
+ Comment "TLV used by both global and stream volumes"
+
+ scale {
+ min "-9000"
+ step "300"
+ mute "1"
+ }
+}
+
+# Controls
+SectionControlMixer."Master Playback Volume" {
+ Comment "Global DSP volume"
+
+ # control belongs to this index group
+ index "1"
+
+ # Channel register and shift for Front Left/Right
+ channel."FL" {
+ reg "0"
+ shift "0"
+ }
+ channel."FR" {
+ reg "0"
+ shift "8"
+ }
+
+ # max control value and whether value is inverted
+ max "31"
+ invert "false"
+
+ # control uses bespoke driver get/put/info ID 0
+ ops."ctl" {
+ info "volsw"
+ get "256"
+ put "256"
+ }
+
+ # uses TLV data above
+ tlv "hsw_vol_tlv"
+}
+
+SectionControlMixer."Media0 Playback Volume" {
+ Comment "Offload 0 volume"
+
+ # control belongs to this index group
+ index "1"
+
+ # Channel register and shift for Front Left/Right
+ channel."FL" {
+ reg "1"
+ shift "0"
+ }
+ channel."FR" {
+ reg "1"
+ shift "8"
+ }
+
+ # max control value and whether value is inverted
+ max "31"
+ invert "false"
+
+ # control uses bespoke driver get/put/info ID 0
+ ops."ctl" {
+ info "volsw"
+ get "257"
+ put "257"
+ }
+
+ # uses TLV data above
+ tlv "hsw_vol_tlv"
+}
+
+SectionControlMixer."Media1 Playback Volume" {
+ Comment "Offload 1 volume"
+
+ # control belongs to this index group
+ index "1"
+
+ # Channel register and shift for Front Left/Right
+ channel."FL" {
+ reg "2"
+ shift "0"
+ }
+ channel."FR" {
+ reg "2"
+ shift "8"
+ }
+
+ # max control value and whether value is inverted
+ max "31"
+ invert "false"
+
+ # control uses bespoke driver get/put/info ID 0
+ ops."ctl" {
+ info "volsw"
+ get "257"
+ put "257"
+ }
+
+ # uses TLV data above
+ tlv "hsw_vol_tlv"
+}
+
+SectionControlMixer."Mic Capture Volume" {
+ Comment "Mic Capture volume"
+
+ # control belongs to this index group
+ index "1"
+
+ # Channel register and shift for Front Left/Right
+ channel."FL" {
+ reg "0"
+ shift "0"
+ }
+ channel."FR" {
+ reg "0"
+ shift "8"
+ }
+
+ # max control value and whether value is inverted
+ max "31"
+ invert "false"
+
+ # control uses bespoke driver get/put/info ID 0
+ ops."ctl" {
+ info "volsw"
+ get "257"
+ put "257"
+ }
+
+ # uses TLV data above
+ tlv "hsw_vol_tlv"
+}
+
+SectionWidget."SSP0 CODEC IN" {
+
+ index "1"
+ type "aif_in"
+ no_pm "true"
+ shift "0"
+ invert "0"
+}
+
+SectionWidget."SSP0 CODEC OUT" {
+
+ index "1"
+ type "aif_out"
+ no_pm "true"
+ shift "0"
+ invert "0"
+}
+
+SectionWidget."SSP1 BT IN" {
+
+ index "1"
+ type "aif_in"
+ no_pm "true"
+ shift "0"
+ invert "0"
+}
+
+SectionWidget."SSP1 BT OUT" {
+
+ index "1"
+ type "aif_out"
+ no_pm "true"
+ shift "0"
+ invert "0"
+}
+
+SectionWidget."Playback VMixer" {
+
+ index "1"
+ type "mixer"
+ no_pm "true"
+ shift "0"
+ invert "0"
+}
+
+# PCM Configurations supported by FW
+SectionPCMConfig."PCM 48k Stereo 24bit" {
+
+ config."playback" {
+ format "S24_LE"
+ rate "48000"
+ channels "2"
+ tdm_slot "0xf"
+ }
+
+ config."capture" {
+ format "S24_LE"
+ rate "48000"
+ channels "2"
+ tdm_slot "0xf"
+ }
+}
+
+SectionPCMConfig."PCM 48k Stereo 16bit" {
+
+ config."playback" {
+ format "S16_LE"
+ rate "48000"
+ channels "2"
+ tdm_slot "0xf"
+ }
+
+ config."capture" {
+ format "S16_LE"
+ rate "48000"
+ channels "2"
+ tdm_slot "0xf"
+ }
+}
+
+SectionPCMConfig."PCM 48k 2P/4C 16bit" {
+
+ config."playback" {
+ format "S16_LE"
+ rate "48000"
+ channels "2"
+ tdm_slot "0xf"
+ }
+
+ config."capture" {
+ format "S16_LE"
+ rate "48000"
+ channels "4"
+ tdm_slot "0xf"
+ }
+}
+
+# PCM capabilities supported by FW
+SectionPCMCapabilities."System Playback" {
+
+ formats "S24_LE,S16_LE"
+ rate_min "48000"
+ rate_max "48000"
+ channels_min "2"
+ channels_max "2"
+}
+
+SectionPCMCapabilities."Analog Capture" {
+
+ formats "S24_LE,S16_LE"
+ rate_min "48000"
+ rate_max "48000"
+ channels_min "2"
+ channels_max "4"
+}
+
+SectionPCMCapabilities."Loopback Capture" {
+
+ formats "S24_LE,S16_LE"
+ rate_min "48000"
+ rate_max "48000"
+ channels_min "2"
+ channels_max "2"
+}
+
+SectionPCMCapabilities."Offload0 Playback" {
+ formats "S24_LE,S16_LE"
+ rate_min "8000"
+ rate_max "192000"
+ channels_min "2"
+ channels_max "2"
+}
+
+SectionPCMCapabilities."Offload1 Playback" {
+ formats "S24_LE,S16_LE"
+ rate_min "8000"
+ rate_max "48000"
+ channels_min "2"
+ channels_max "2"
+}
+
+# PCM devices exported by Firmware
+SectionPCM."System Pin" {
+
+ index "1"
+
+ # used for binding to the PCM
+ ID "0"
+
+ pcm."playback" {
+
+ capabilities "System Playback"
+
+ configs [
+ "PCM 48k Stereo 24bit"
+ "PCM 48k Stereo 16bit"
+ ]
+ }
+
+ pcm."capture" {
+
+ capabilities "Analog Capture"
+
+ configs [
+ "PCM 48k Stereo 24bit"
+ "PCM 48k Stereo 16bit"
+ "PCM 48k 2P/4C 16bit"
+ ]
+ }
+}
+
+SectionPCM."Offload0 Pin" {
+
+ index "1"
+
+ # used for binding to the PCM
+ ID "1"
+
+ pcm."playback" {
+
+ capabilities "Offload0 Playback"
+
+ configs [
+ "PCM 48k Stereo 24bit"
+ "PCM 48k Stereo 16bit"
+ ]
+ }
+}
+
+SectionPCM."Offload1 Pin" {
+
+ index "1"
+
+ # used for binding to the PCM
+ ID "2"
+
+ pcm."playback" {
+
+ capabilities "Offload1 Playback"
+
+ configs [
+ "PCM 48k Stereo 24bit"
+ "PCM 48k Stereo 16bit"
+ ]
+ }
+}
+
+SectionPCM."Loopback Pin" {
+
+ index "1"
+
+ # used for binding to the PCM
+ ID "3"
+
+ pcm."capture" {
+
+ capabilities "Loopback Capture"
+
+ configs [
+ "PCM 48k Stereo 24bit"
+ "PCM 48k Stereo 16bit"
+ ]
+ }
+}
+
+SectionGraph."dsp" {
+ index "1"
+
+ lines [
+ "Playback VMixer, , System Playback"
+ "Playback VMixer, , Offload0 Playback"
+ "Playback VMixer, , Offload1 Playback"
+ "SSP0 CODEC OUT, , Playback VMixer"
+ "Loopback Capture, , Playback VMixer"
+ "Analog Capture, , SSP0 CODEC IN"
+ ]
+}
--
2.5.0

View File

@ -0,0 +1,28 @@
From 907e464593a2acf51c2e2be4c3d4e098efdd95ff Mon Sep 17 00:00:00 2001
From: Takashi Iwai <tiwai@suse.de>
Date: Thu, 30 Jul 2015 16:34:50 +0200
Subject: [PATCH 48/49] topology: Fix missing inclusion of ctype.h
Fix a compile warning:
data.c:116:7: warning: implicit declaration of function 'isspace' [-Wimplicit-function-declaration]
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
src/topology/data.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/topology/data.c b/src/topology/data.c
index ae664721935d..13e1e2bb60fb 100644
--- a/src/topology/data.c
+++ b/src/topology/data.c
@@ -18,6 +18,7 @@
#include "list.h"
#include "tplg_local.h"
+#include <ctype.h>
/* Get Private data from a file. */
static int tplg_parse_data_file(snd_config_t *cfg, struct tplg_elem *elem)
--
2.5.0

View File

@ -0,0 +1,96 @@
From 66ce9f9a1177de3b8e8304323b4d3a16d78ead32 Mon Sep 17 00:00:00 2001
From: Takashi Iwai <tiwai@suse.de>
Date: Thu, 30 Jul 2015 16:43:19 +0200
Subject: [PATCH 49/49] topology: Fix typos
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
include/topology.h | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/include/topology.h b/include/topology.h
index f604ed1450d3..0cb2d79e5574 100644
--- a/include/topology.h
+++ b/include/topology.h
@@ -48,7 +48,7 @@ extern "C" {
*
* The topology text format uses the standard ALSA configuration file format to
* describe each topology object type. This allows topology objects to include
- * other topology objects as part of thier definition. i.e. a TLV data object
+ * other topology objects as part of their definition. i.e. a TLV data object
* can be shared amongst many control objects that use the same TLV data.
*
*
@@ -174,7 +174,7 @@ extern "C" {
* words "0xaabbccdd,0x11223344,0x66aa77bb,0xefef1234"
* };
* </pre>
- * The file, bytes, shorts and words keywords are all mutulally exclusive as
+ * The file, bytes, shorts and words keywords are all mutually exclusive as
* the private data should only be taken from one source. The private data can
* either be read from a separate file or defined in the topology file using
* the bytes, shorts or words keywords.
@@ -247,7 +247,7 @@ extern "C" {
* can include channel mapping, callback operations, private data and
* text strings to represent the enumerated control options.<br>
*
- * The text strings for the enumerated controls are defined in a seperate
+ * The text strings for the enumerated controls are defined in a separate
* section as follows :-
*
* <pre>
@@ -306,7 +306,7 @@ extern "C" {
* graph with other graphs, it's not used by the kernel atm.
*
* <h4>DAPM Widgets</h4>
- * DAPM wigets are similar to controls in that they can include many other
+ * DAPM widgets are similar to controls in that they can include many other
* objects. Widgets can contain private data, mixer controls and enum controls.
*
* The following widget types are supported and match the driver types :-
@@ -367,13 +367,13 @@ extern "C" {
*
* formats "S24_LE,S16_LE" # Supported formats
* rate_min "48000" # Max supported sample rate
- * rate_max "48000" # Min suppoprted sample rate
+ * rate_max "48000" # Min supported sample rate
* channels_min "2" # Min number of channels
* channels_max "2" # max number of channels
* }
* </pre>
* The supported formats use the same naming convention as the driver macros.
- * The PCM capabilities name can be reffered to and included by BE, PCM and
+ * The PCM capabilities name can be referred to and included by BE, PCM and
* Codec <-> codec topology sections.
*
* <h4>PCM Configurations</h4>
@@ -400,7 +400,7 @@ extern "C" {
* </pre>
*
* The supported formats use the same naming convention as the driver macros.
- * The PCM configuration name can be reffered to and included by BE, PCM and
+ * The PCM configuration name can be referred to and included by BE, PCM and
* Codec <-> codec topology sections.
*
* <h4>PCM Configurations</h4>
@@ -434,7 +434,7 @@ extern "C" {
* id "0" # used for binding to the PCM
*
* pcm."playback" {
- * capabilities "capabilities1" # capbilities for playback
+ * capabilities "capabilities1" # capabilities for playback
*
* configs [ # supported configs for playback
* "config1"
@@ -476,7 +476,7 @@ void snd_tplg_free(snd_tplg_t *tplg);
* \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
+ * \return Zero on success, otherwise a negative error code
*/
int snd_tplg_build_file(snd_tplg_t *tplg, const char *infile,
const char *outfile);
--
2.5.0

View File

@ -1,3 +1,46 @@
-------------------------------------------------------------------
Tue Aug 4 17:41:39 CEST 2015 - tiwai@suse.de
- Backport upstream fixes: surround41/50 chmap fix, UCM documents,
config string fix, PCM timestamp query API, replacement of list.h
with LGPL:
0023-surround41-50.conf-Use-chmap-syntax-for-better-flexi.patch
0024-ucm-docs-fix-doxygen-exclude-patch-for-UCM-local-hea.patch
0025-ucm-docs-Fix-doxygen-formatting-for-UCM-main-page.patch
0026-docs-Add-UCM-link-to-main-doxygen-page.patch
0027-Replace-unsafe-characters-with-_-in-card-name.patch
0028-pcm-add-helper-functions-to-query-timestamping-capab.patch
0029-pcm-add-support-for-get-set_audio_htstamp_config.patch
0030-pcm-add-support-for-new-STATUS_EXT-ioctl.patch
0031-test-fix-audio_time-with-new-get-set-audio_tstamp_co.patch
0032-test-audio_time-show-report-validity-and-accuracy.patch
0033-pcm-restore-hw-params-on-set-latency-failed.patch
0034-Replace-list.h-with-its-own-version.patch
- Backport topology API addition patches:
0035-topology-uapi-Add-UAPI-headers-for-topology-ABI.patch
0036-topology-Add-topology-core-parser.patch
0037-topology-Add-text-section-parser.patch
0038-topology-Add-PCM-parser.patch
0039-topology-Add-operations-parser.patch
0040-topology-Add-private-data-parser.patch
0041-topology-Add-DAPM-object-parser.patch
0042-topology-Add-CTL-parser.patch
0043-topology-Add-Channel-map-parser.patch
0044-topology-Add-binary-file-builder.patch
0045-topology-autotools-Add-build-support-for-topology-co.patch
0046-topology-doxygen-Add-doxygen-support-for-topology-co.patch
0047-conf-topology-Add-topology-file-for-broadwell-audio-.patch
0048-topology-Fix-missing-inclusion-of-ctype.h.patch
0049-topology-Fix-typos.patch
- Enable autoreconf call to regenerate after patching
-------------------------------------------------------------------
Fri Jul 31 07:35:12 UTC 2015 - dimstar@opensuse.org
- Change libudev-devel BuildRequires to pkgconfig(udev): makes us
less prone to packaging changes, and in the end udev.pc is
exactly what we need to define _udevdir.
-------------------------------------------------------------------
Thu Jun 18 09:32:07 CEST 2015 - tiwai@suse.de

View File

@ -71,6 +71,33 @@ Patch19: 0019-pcm-Don-t-assert-in-_snd_pcm_hw_params_internal.patch
Patch20: 0020-pcm-Fix-snd_pcm_status-for-dmix-co.patch
Patch21: 0021-control-Allow-cset-ing-specific-values-in-the-multi-.patch
Patch22: 0022-PCM-snd_pcm_xxxx_drain-maybe-blocked-after-suspend-a.patch
Patch23: 0023-surround41-50.conf-Use-chmap-syntax-for-better-flexi.patch
Patch24: 0024-ucm-docs-fix-doxygen-exclude-patch-for-UCM-local-hea.patch
Patch25: 0025-ucm-docs-Fix-doxygen-formatting-for-UCM-main-page.patch
Patch26: 0026-docs-Add-UCM-link-to-main-doxygen-page.patch
Patch27: 0027-Replace-unsafe-characters-with-_-in-card-name.patch
Patch28: 0028-pcm-add-helper-functions-to-query-timestamping-capab.patch
Patch29: 0029-pcm-add-support-for-get-set_audio_htstamp_config.patch
Patch30: 0030-pcm-add-support-for-new-STATUS_EXT-ioctl.patch
Patch31: 0031-test-fix-audio_time-with-new-get-set-audio_tstamp_co.patch
Patch32: 0032-test-audio_time-show-report-validity-and-accuracy.patch
Patch33: 0033-pcm-restore-hw-params-on-set-latency-failed.patch
Patch34: 0034-Replace-list.h-with-its-own-version.patch
Patch35: 0035-topology-uapi-Add-UAPI-headers-for-topology-ABI.patch
Patch36: 0036-topology-Add-topology-core-parser.patch
Patch37: 0037-topology-Add-text-section-parser.patch
Patch38: 0038-topology-Add-PCM-parser.patch
Patch39: 0039-topology-Add-operations-parser.patch
Patch40: 0040-topology-Add-private-data-parser.patch
Patch41: 0041-topology-Add-DAPM-object-parser.patch
Patch42: 0042-topology-Add-CTL-parser.patch
Patch43: 0043-topology-Add-Channel-map-parser.patch
Patch44: 0044-topology-Add-binary-file-builder.patch
Patch45: 0045-topology-autotools-Add-build-support-for-topology-co.patch
Patch46: 0046-topology-doxygen-Add-doxygen-support-for-topology-co.patch
Patch47: 0047-conf-topology-Add-topology-file-for-broadwell-audio-.patch
Patch48: 0048-topology-Fix-missing-inclusion-of-ctype.h.patch
Patch49: 0049-topology-Fix-typos.patch
# rest suse patches
Patch99: alsa-lib-doxygen-avoid-crash-for-11.3.diff
# suppress timestamp in documents
@ -86,7 +113,7 @@ Recommends: alsa-oss
Recommends: alsa-plugins
BuildRoot: %{_tmppath}/%{name}-%{version}-build
%if 0%{?suse_version} > 1200
BuildRequires: libudev-devel
BuildRequires: pkgconfig(udev)
%else
BuildRequires: udev
%endif
@ -163,6 +190,33 @@ Architecture.
%patch20 -p1
%patch21 -p1
%patch22 -p1
%patch23 -p1
%patch24 -p1
%patch25 -p1
%patch26 -p1
%patch27 -p1
%patch28 -p1
%patch29 -p1
%patch30 -p1
%patch31 -p1
%patch32 -p1
%patch33 -p1
%patch34 -p1
%patch35 -p1
%patch36 -p1
%patch37 -p1
%patch38 -p1
%patch39 -p1
%patch40 -p1
%patch41 -p1
%patch42 -p1
%patch43 -p1
%patch44 -p1
%patch45 -p1
%patch46 -p1
%patch47 -p1
%patch48 -p1
%patch49 -p1
%if 0%{?suse_version} == 1130
%patch99 -p1
%endif
@ -178,7 +232,7 @@ sed -i -e'/recommends.*alsa-oss/d' $RPM_SOURCE_DIR/baselibs.conf
%build
export AUTOMAKE_JOBS="%{?_smp_mflags}"
# build alsa-lib
# autoreconf -fi
autoreconf -fi
%configure \
--disable-static \
--enable-symbolic-functions \