Hello community, here is the log from the commit of package alsa for openSUSE:Factory checked in at 2015-08-21 07:37:17 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/alsa (Old) and /work/SRC/openSUSE:Factory/.alsa.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "alsa" Changes: -------- --- /work/SRC/openSUSE:Factory/alsa/alsa.changes 2015-07-05 18:00:30.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.alsa.new/alsa.changes 2015-08-21 07:37:19.000000000 +0200 @@ -1,0 +2,43 @@ +Tue Aug 4 17:41:39 CEST 2015 - [email protected] + +- 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 - [email protected] + +- 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. + +------------------------------------------------------------------- New: ---- 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 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 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ alsa.spec ++++++ --- /var/tmp/diff_new_pack.WnaeCZ/_old 2015-08-21 07:37:21.000000000 +0200 +++ /var/tmp/diff_new_pack.WnaeCZ/_new 2015-08-21 07:37:21.000000000 +0200 @@ -71,6 +71,33 @@ 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-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 @@ %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 @@ %build export AUTOMAKE_JOBS="%{?_smp_mflags}" # build alsa-lib -# autoreconf -fi +autoreconf -fi %configure \ --disable-static \ --enable-symbolic-functions \ ++++++ 0023-surround41-50.conf-Use-chmap-syntax-for-better-flexi.patch ++++++ >From bbe9248e6788e7a852af15f2110dbaeace4d1907 Mon Sep 17 00:00:00 2001 From: David Henningsson <[email protected]> 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 <[email protected]> Signed-off-by: Takashi Iwai <[email protected]> --- 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 ++++++ 0024-ucm-docs-fix-doxygen-exclude-patch-for-UCM-local-hea.patch ++++++ >From f66c7cc293b13765ffd67f530008e836f8354b13 Mon Sep 17 00:00:00 2001 From: Liam Girdwood <[email protected]> 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 <[email protected]> Signed-off-by: Takashi Iwai <[email protected]> --- 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 ++++++ 0025-ucm-docs-Fix-doxygen-formatting-for-UCM-main-page.patch ++++++ >From bb92545e064c3e2549878a328277eebe343aae61 Mon Sep 17 00:00:00 2001 From: Liam Girdwood <[email protected]> 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 <[email protected]> Signed-off-by: Takashi Iwai <[email protected]> --- 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 ++++++ 0026-docs-Add-UCM-link-to-main-doxygen-page.patch ++++++ >From c6df8273746645b2c5109537b07af6ed33d268d1 Mon Sep 17 00:00:00 2001 From: Liam Girdwood <[email protected]> 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 <[email protected]> Signed-off-by: Takashi Iwai <[email protected]> --- 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 ++++++ 0027-Replace-unsafe-characters-with-_-in-card-name.patch ++++++ >From 4dc44bb34aab2b23ab45c8e61e4b17bf2cf58959 Mon Sep 17 00:00:00 2001 From: "Alexander E. Patrakov" <[email protected]> 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 <[email protected]> Signed-off-by: Takashi Iwai <[email protected]> --- 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 ++++++ 0028-pcm-add-helper-functions-to-query-timestamping-capab.patch ++++++ >From 6cb31b444442f8ebca939cd78b80993f2ac85350 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart <[email protected]> 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 <[email protected]> Signed-off-by: Takashi Iwai <[email protected]> --- 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 ++++++ 0029-pcm-add-support-for-get-set_audio_htstamp_config.patch ++++++ >From 6ec2464f397ff401c251057499abea77fd80b60b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart <[email protected]> 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 <[email protected]> Signed-off-by: Takashi Iwai <[email protected]> --- 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 ++++++ 0030-pcm-add-support-for-new-STATUS_EXT-ioctl.patch ++++++ >From cc8b73436a90c35beda64bfa13b2196df20cfd81 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart <[email protected]> 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 <[email protected]> Signed-off-by: Takashi Iwai <[email protected]> --- 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 ++++++ 0031-test-fix-audio_time-with-new-get-set-audio_tstamp_co.patch ++++++ >From 7bb3a74c4dbcfb01b0b91d94452d994b845b4ff3 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart <[email protected]> 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 <[email protected]> Signed-off-by: Takashi Iwai <[email protected]> --- 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 ++++++ 0032-test-audio_time-show-report-validity-and-accuracy.patch ++++++ >From 6849d7dc88e0fe22cddb6b2d06ddc967ad4c3a6c Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart <[email protected]> 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 <[email protected]> Signed-off-by: Takashi Iwai <[email protected]> --- 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 ++++++ 0033-pcm-restore-hw-params-on-set-latency-failed.patch ++++++ >From 77b6be63876ee46a95320e77735d977edeedd44a Mon Sep 17 00:00:00 2001 From: Martin Geier <[email protected]> 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 <[email protected]> Signed-off-by: Takashi Iwai <[email protected]> --- 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 ++++++ 0034-Replace-list.h-with-its-own-version.patch ++++++ >From d0e13f87742e923edfb638d4b16b69b8dc2136d8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai <[email protected]> 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 <[email protected]> Signed-off-by: Takashi Iwai <[email protected]> --- 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 <[email protected]> + * + * 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 ++++++ 0035-topology-uapi-Add-UAPI-headers-for-topology-ABI.patch ++++++ >From 227c790c16db17a986df06c9a3ad79edade78db7 Mon Sep 17 00:00:00 2001 From: Liam Girdwood <[email protected]> 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 <[email protected]> Signed-off-by: Takashi Iwai <[email protected]> --- 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 ++++++ 0036-topology-Add-topology-core-parser.patch ++++++ ++++ 1326 lines (skipped) ++++++ 0037-topology-Add-text-section-parser.patch ++++++ >From 408396a8ca092846c840baa79c04b5f7dbe5da69 Mon Sep 17 00:00:00 2001 From: Liam Girdwood <[email protected]> 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 <[email protected]> Signed-off-by: Takashi Iwai <[email protected]> --- 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 <[email protected]> + Yao Jin <[email protected]> + Liam Girdwood <[email protected]> + +*/ + +#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 ++++++ 0038-topology-Add-PCM-parser.patch ++++++ ++++ 664 lines (skipped) ++++++ 0039-topology-Add-operations-parser.patch ++++++ >From 353f1eddb608a837157342155fc061f85bf0a5f8 Mon Sep 17 00:00:00 2001 From: Liam Girdwood <[email protected]> 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 <[email protected]> Signed-off-by: Takashi Iwai <[email protected]> --- 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 <[email protected]> + Yao Jin <[email protected]> + Liam Girdwood <[email protected]> +*/ + +#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 ++++++ 0040-topology-Add-private-data-parser.patch ++++++ >From 5b379da2a0a1084349e918a52f471c03e37af703 Mon Sep 17 00:00:00 2001 From: Liam Girdwood <[email protected]> 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 <[email protected]> Signed-off-by: Takashi Iwai <[email protected]> --- 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 <[email protected]> + Yao Jin <[email protected]> + Liam Girdwood <[email protected]> +*/ + +#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 ++++++ 0041-topology-Add-DAPM-object-parser.patch ++++++ >From 01a0e1a1c2196967d2522092ca993098a7c66613 Mon Sep 17 00:00:00 2001 From: Liam Girdwood <[email protected]> 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 <[email protected]> Signed-off-by: Takashi Iwai <[email protected]> --- 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 <[email protected]> + Yao Jin <[email protected]> + Liam Girdwood <[email protected]> +*/ + +#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 ++++++ 0042-topology-Add-CTL-parser.patch ++++++ ++++ 636 lines (skipped) ++++++ 0043-topology-Add-Channel-map-parser.patch ++++++ >From 9764a4b891737e6b4363c09b5e5ce8384acecc11 Mon Sep 17 00:00:00 2001 From: Liam Girdwood <[email protected]> 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 <[email protected]> Signed-off-by: Takashi Iwai <[email protected]> --- 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 <[email protected]> + Yao Jin <[email protected]> + Liam Girdwood <[email protected]> +*/ + +#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 ++++++ 0044-topology-Add-binary-file-builder.patch ++++++ >From 1d1dff56767842a99d2e06a6997079c0516dec68 Mon Sep 17 00:00:00 2001 From: Liam Girdwood <[email protected]> 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 <[email protected]> Signed-off-by: Takashi Iwai <[email protected]> --- 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 <[email protected]> + Yao Jin <[email protected]> + Liam Girdwood <[email protected]> +*/ + +#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 ++++++ 0045-topology-autotools-Add-build-support-for-topology-co.patch ++++++ >From fec1e8f25374ec8eb4d57ee43e94e9689a748678 Mon Sep 17 00:00:00 2001 From: Liam Girdwood <[email protected]> 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 <[email protected]> Signed-off-by: Takashi Iwai <[email protected]> --- 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 ++++++ 0046-topology-doxygen-Add-doxygen-support-for-topology-co.patch ++++++ >From 22603237b09ed50744030f550248ade135d4f73b Mon Sep 17 00:00:00 2001 From: Liam Girdwood <[email protected]> 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 <[email protected]> Signed-off-by: Takashi Iwai <[email protected]> --- 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 ++++++ 0047-conf-topology-Add-topology-file-for-broadwell-audio-.patch ++++++ >From 00a51b5bacb0f966d0e323bd9d3057c0eb0e6f23 Mon Sep 17 00:00:00 2001 From: Liam Girdwood <[email protected]> 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 <[email protected]> Signed-off-by: Takashi Iwai <[email protected]> --- 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 ++++++ 0048-topology-Fix-missing-inclusion-of-ctype.h.patch ++++++ >From 907e464593a2acf51c2e2be4c3d4e098efdd95ff Mon Sep 17 00:00:00 2001 From: Takashi Iwai <[email protected]> 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 <[email protected]> --- 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 ++++++ 0049-topology-Fix-typos.patch ++++++ >From 66ce9f9a1177de3b8e8304323b4d3a16d78ead32 Mon Sep 17 00:00:00 2001 From: Takashi Iwai <[email protected]> Date: Thu, 30 Jul 2015 16:43:19 +0200 Subject: [PATCH 49/49] topology: Fix typos Signed-off-by: Takashi Iwai <[email protected]> --- 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
