diff --git a/0023-surround41-50.conf-Use-chmap-syntax-for-better-flexi.patch b/0023-surround41-50.conf-Use-chmap-syntax-for-better-flexi.patch new file mode 100644 index 0000000..333f09b --- /dev/null +++ b/0023-surround41-50.conf-Use-chmap-syntax-for-better-flexi.patch @@ -0,0 +1,63 @@ +From bbe9248e6788e7a852af15f2110dbaeace4d1907 Mon Sep 17 00:00:00 2001 +From: David Henningsson +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 +Signed-off-by: Takashi Iwai +--- + 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 + diff --git a/0024-ucm-docs-fix-doxygen-exclude-patch-for-UCM-local-hea.patch b/0024-ucm-docs-fix-doxygen-exclude-patch-for-UCM-local-hea.patch new file mode 100644 index 0000000..6040ae7 --- /dev/null +++ b/0024-ucm-docs-fix-doxygen-exclude-patch-for-UCM-local-hea.patch @@ -0,0 +1,28 @@ +From f66c7cc293b13765ffd67f530008e836f8354b13 Mon Sep 17 00:00:00 2001 +From: Liam Girdwood +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 +Signed-off-by: Takashi Iwai +--- + 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 + diff --git a/0025-ucm-docs-Fix-doxygen-formatting-for-UCM-main-page.patch b/0025-ucm-docs-Fix-doxygen-formatting-for-UCM-main-page.patch new file mode 100644 index 0000000..a6f27e2 --- /dev/null +++ b/0025-ucm-docs-Fix-doxygen-formatting-for-UCM-main-page.patch @@ -0,0 +1,69 @@ +From bb92545e064c3e2549878a328277eebe343aae61 Mon Sep 17 00:00:00 2001 +From: Liam Girdwood +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 +Signed-off-by: Takashi Iwai +--- + 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 + diff --git a/0026-docs-Add-UCM-link-to-main-doxygen-page.patch b/0026-docs-Add-UCM-link-to-main-doxygen-page.patch new file mode 100644 index 0000000..d96948f --- /dev/null +++ b/0026-docs-Add-UCM-link-to-main-doxygen-page.patch @@ -0,0 +1,26 @@ +From c6df8273746645b2c5109537b07af6ed33d268d1 Mon Sep 17 00:00:00 2001 +From: Liam Girdwood +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 +Signed-off-by: Takashi Iwai +--- + 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.

+
  • Page \ref rawmidi explains the design of the RawMidi API. +
  • Page \ref timer explains the design of the Timer API. +
  • Page \ref seq explains the design of the Sequencer API. ++
  • Page \ref ucm explains the use case API. + + +

    Configuration

    +-- +2.5.0 + diff --git a/0027-Replace-unsafe-characters-with-_-in-card-name.patch b/0027-Replace-unsafe-characters-with-_-in-card-name.patch new file mode 100644 index 0000000..bbcc839 --- /dev/null +++ b/0027-Replace-unsafe-characters-with-_-in-card-name.patch @@ -0,0 +1,105 @@ +From 4dc44bb34aab2b23ab45c8e61e4b17bf2cf58959 Mon Sep 17 00:00:00 2001 +From: "Alexander E. Patrakov" +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 +Signed-off-by: Takashi Iwai +--- + 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 + diff --git a/0028-pcm-add-helper-functions-to-query-timestamping-capab.patch b/0028-pcm-add-helper-functions-to-query-timestamping-capab.patch new file mode 100644 index 0000000..72901bd --- /dev/null +++ b/0028-pcm-add-helper-functions-to-query-timestamping-capab.patch @@ -0,0 +1,84 @@ +From 6cb31b444442f8ebca939cd78b80993f2ac85350 Mon Sep 17 00:00:00 2001 +From: Pierre-Louis Bossart +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 +Signed-off-by: Takashi Iwai +--- + 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 + diff --git a/0029-pcm-add-support-for-get-set_audio_htstamp_config.patch b/0029-pcm-add-support-for-get-set_audio_htstamp_config.patch new file mode 100644 index 0000000..a27d69f --- /dev/null +++ b/0029-pcm-add-support-for-get-set_audio_htstamp_config.patch @@ -0,0 +1,129 @@ +From 6ec2464f397ff401c251057499abea77fd80b60b Mon Sep 17 00:00:00 2001 +From: Pierre-Louis Bossart +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 +Signed-off-by: Takashi Iwai +--- + 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 + diff --git a/0030-pcm-add-support-for-new-STATUS_EXT-ioctl.patch b/0030-pcm-add-support-for-new-STATUS_EXT-ioctl.patch new file mode 100644 index 0000000..247af22 --- /dev/null +++ b/0030-pcm-add-support-for-new-STATUS_EXT-ioctl.patch @@ -0,0 +1,45 @@ +From cc8b73436a90c35beda64bfa13b2196df20cfd81 Mon Sep 17 00:00:00 2001 +From: Pierre-Louis Bossart +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 +Signed-off-by: Takashi Iwai +--- + 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 + diff --git a/0031-test-fix-audio_time-with-new-get-set-audio_tstamp_co.patch b/0031-test-fix-audio_time-with-new-get-set-audio_tstamp_co.patch new file mode 100644 index 0000000..a048156 --- /dev/null +++ b/0031-test-fix-audio_time-with-new-get-set-audio_tstamp_co.patch @@ -0,0 +1,596 @@ +From 7bb3a74c4dbcfb01b0b91d94452d994b845b4ff3 Mon Sep 17 00:00:00 2001 +From: Pierre-Louis Bossart +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 +Signed-off-by: Takashi Iwai +--- + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + #include ++#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 + diff --git a/0032-test-audio_time-show-report-validity-and-accuracy.patch b/0032-test-audio_time-show-report-validity-and-accuracy.patch new file mode 100644 index 0000000..8a0b437 --- /dev/null +++ b/0032-test-audio_time-show-report-validity-and-accuracy.patch @@ -0,0 +1,106 @@ +From 6849d7dc88e0fe22cddb6b2d06ddc967ad4c3a6c Mon Sep 17 00:00:00 2001 +From: Pierre-Louis Bossart +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 +Signed-off-by: Takashi Iwai +--- + 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 + diff --git a/0033-pcm-restore-hw-params-on-set-latency-failed.patch b/0033-pcm-restore-hw-params-on-set-latency-failed.patch new file mode 100644 index 0000000..4575a78 --- /dev/null +++ b/0033-pcm-restore-hw-params-on-set-latency-failed.patch @@ -0,0 +1,44 @@ +From 77b6be63876ee46a95320e77735d977edeedd44a Mon Sep 17 00:00:00 2001 +From: Martin Geier +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 +Signed-off-by: Takashi Iwai +--- + 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 + diff --git a/0034-Replace-list.h-with-its-own-version.patch b/0034-Replace-list.h-with-its-own-version.patch new file mode 100644 index 0000000..e4a7a4c --- /dev/null +++ b/0034-Replace-list.h-with-its-own-version.patch @@ -0,0 +1,274 @@ +From d0e13f87742e923edfb638d4b16b69b8dc2136d8 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +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 +Signed-off-by: Takashi Iwai +--- + 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 ++ * ++ * 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 + +-/* +- * 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 + diff --git a/0035-topology-uapi-Add-UAPI-headers-for-topology-ABI.patch b/0035-topology-uapi-Add-UAPI-headers-for-topology-ABI.patch new file mode 100644 index 0000000..16f9bdc --- /dev/null +++ b/0035-topology-uapi-Add-UAPI-headers-for-topology-ABI.patch @@ -0,0 +1,454 @@ +From 227c790c16db17a986df06c9a3ad79edade78db7 Mon Sep 17 00:00:00 2001 +From: Liam Girdwood +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 +Signed-off-by: Takashi Iwai +--- + 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 + diff --git a/0036-topology-Add-topology-core-parser.patch b/0036-topology-Add-topology-core-parser.patch new file mode 100644 index 0000000..05f617d --- /dev/null +++ b/0036-topology-Add-topology-core-parser.patch @@ -0,0 +1,1326 @@ +From 37692bb985bdaa95fbb23e4a12eb61f6a2c63ac0 Mon Sep 17 00:00:00 2001 +From: Liam Girdwood +Date: Wed, 29 Jul 2015 17:45:14 +0100 +Subject: [PATCH 36/49] topology: Add topology core parser. + +The topology core parses the high level topology file and calls the +individual object parsers when any new object element is detected at +the high level. + +Signed-off-by: Liam Girdwood +Signed-off-by: Takashi Iwai +--- + include/topology.h | 497 ++++++++++++++++++++++++++++++++++++++++++++++ + src/topology/elem.c | 187 +++++++++++++++++ + src/topology/parser.c | 359 +++++++++++++++++++++++++++++++++ + src/topology/tplg_local.h | 234 ++++++++++++++++++++++ + 4 files changed, 1277 insertions(+) + create mode 100644 include/topology.h + create mode 100644 src/topology/elem.c + create mode 100644 src/topology/parser.c + create mode 100644 src/topology/tplg_local.h + +diff --git a/include/topology.h b/include/topology.h +new file mode 100644 +index 000000000000..f604ed1450d3 +--- /dev/null ++++ b/include/topology.h +@@ -0,0 +1,497 @@ ++/* ++ * ++ * This library is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License as ++ * published by the Free Software Foundation; either version 2.1 of ++ * the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Copyright (C) 2015 Intel Corporation ++ * ++ */ ++ ++#ifndef __ALSA_TOPOLOGY_H ++#define __ALSA_TOPOLOGY_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** ++ * \defgroup topology Topology Interface ++ * \{ ++ */ ++ ++/*! \page topology ALSA Topology Interface ++ * ++ * The topology interface allows developers to define DSP topologies in a text ++ * file format and to convert the text topology to a binary topology ++ * representation that can be understood by the kernel. The topology core ++ * currently recognises the following object types :- ++ * ++ * * Controls (mixer, enumerated and byte) including TLV data. ++ * * PCMs (FE and BE configurations and capabilities) ++ * * DAPM widgets ++ * * DAPM graph elements. ++ * * Private data for each object type. ++ * * Manifest (containing count of each object type) ++ * ++ *

    Topology File Format

    ++ * ++ * The topology text format uses the standard ALSA configuration file format to ++ * describe each topology object type. This allows topology objects to include ++ * other topology objects as part of thier definition. i.e. a TLV data object ++ * can be shared amongst many control objects that use the same TLV data. ++ * ++ * ++ *

    Controls

    ++ * Topology audio controls can belong to three different types :- ++ * * Mixer control ++ * * Enumerated control ++ * * Byte control ++ * ++ * Each control type can contain TLV data, private data, operations and also ++ * belong to widget objects.
    ++ * ++ *
    Control Operations
    ++ * Driver Kcontrol callback info(), get() and put() operations are mapped with ++ * the CTL ops section in topology configuration files. The ctl ops section can ++ * assign operations using the standard names (listed below) for the standard ++ * kcontrol types or use ID numbers (>256) to map to bespoke driver controls.
    ++ * ++ *
    ++ *
    ++ *	ops."ctl" {
    ++ *		info "volsw"
    ++ *		get "257"
    ++ *		put "257"
    ++ *	}
    ++ *
    ++ * 
    ++ * ++ * This mapping shows info() using the standard "volsw" info callback whilst ++ * the get() and put() are mapped to bespoke driver callbacks.
    ++ * ++ * The Standard operations names for control get(), put() and info calls ++ * are :- ++ * * volsw ++ * * volsw_sx ++ * * volsw_xr_sx ++ * * enum ++ * * bytes ++ * * enum_value ++ * * range ++ * * strobe ++ * ++ *
    Control TLV Data
    ++ * Controls can also use TLV data to represent dB information. This can be done ++ * by defining a TLV section and using the TLV section within the control. ++ * The TLV data for DBScale types are defined as follows :- ++ * ++ *
    ++ *	scale {
    ++ *		min "-9000"
    ++ *		step "300"
    ++ *		mute "1"
    ++ *	}
    ++ * 
    ++ * ++ * Where the meanings and values for min, step and mute are exactly the same ++ * as defined in driver code. ++ * ++ *
    Control Channel Mapping
    ++ * Controls can also specify which channels they are mapped with. This is useful ++ * for userspace as it allows applications to determine the correct control ++ * channel for Left and Right etc. Channel maps are defined as follows :- ++ * ++ *
    ++ *	channel."name" {
    ++ *		reg "0"
    ++ *		shift "0"
    ++ *	}
    ++ * 
    ++ * ++ * The channel map reg is the register offset for the control, shift is the ++ * bit shift within the register for the channel and the section name is the ++ * channel name and can be one of the following :- ++ * ++ *
    ++ *  * mono		# mono stream
    ++ *  * fl 		# front left
    ++ *  * fr		# front right
    ++ *  * rl		# rear left
    ++ *  * rr		# rear right
    ++ *  * fc		# front center
    ++ *  * lfe		# LFE
    ++ *  * sl		# side left
    ++ *  * sr		# side right
    ++ *  * rc		# rear center
    ++ *  * flc		# front left center
    ++ *  * frc		# front right center
    ++ *  * rlc		# rear left center
    ++ *  * rrc		# rear right center
    ++ *  * flw		# front left wide
    ++ *  * frw		# front right wide
    ++ *  * flh		# front left high
    ++ *  * fch		# front center high
    ++ *  * frh		# front right high
    ++ *  * tc		# top center
    ++ *  * tfl		# top front left
    ++ *  * tfr		# top front right
    ++ *  * tfc		# top front center
    ++ *  * trl		# top rear left
    ++ *  * trr		# top rear right
    ++ *  * trc		# top rear center
    ++ *  * tflc		# top front left center
    ++ *  * tfrc		# top front right center
    ++ *  * tsl		# top side left
    ++ *  * tsr		# top side right
    ++ *  * llfe		# left LFE
    ++ *  * rlfe		# right LFE
    ++ *  * bc		# bottom center
    ++ *  * blc		# bottom left center
    ++ *  * brc		# bottom right center
    ++ * 
    ++ * ++ *
    Control Private Data
    ++ * Controls can also have private data. This can be done by defining a private ++ * data section and including the section within the control. The private data ++ * section is defined as follows :- ++ * ++ *
    ++ * SectionData."pdata for EQU1" {
    ++ *	file "/path/to/file"
    ++ *	bytes "0x12,0x34,0x56,0x78"
    ++ *	shorts "0x1122,0x3344,0x5566,0x7788"
    ++ *	words "0xaabbccdd,0x11223344,0x66aa77bb,0xefef1234"
    ++ * };
    ++ * 
    ++ * The file, bytes, shorts and words keywords are all mutulally exclusive as ++ * the private data should only be taken from one source. The private data can ++ * either be read from a separate file or defined in the topology file using ++ * the bytes, shorts or words keywords. ++ * ++ *
    Mixer Controls
    ++ * A mixer control is defined as a new section that can include channel mapping, ++ * TLV data, callback operations and private data. The mixer section also ++ * includes a few other config options that are shown here :- ++ * ++ *
    ++ * SectionControlMixer."mixer name" {
    ++ *	comment "optional comments"
    ++ *
    ++ *	index "1"			# Index number
    ++ *
    ++ *	channel."name" {		# Channel maps
    ++ *	   ....
    ++ *	}
    ++ *
    ++ *	ops."ctl" {			# Ops callback functions
    ++ *	   ....
    ++ *	}
    ++ *
    ++ *	max "32"			# Max control value
    ++ *	invert "0"			# Whether control values are inverted
    ++ *
    ++ *	tlv "tld_data"			# optional TLV data
    ++ *
    ++ *	data "pdata for mixer1"		# optional private data
    ++ * }
    ++ * 
    ++ * ++ * The section name is used to define the mixer name. The index number can be ++ * used to identify topology objects groups. This allows driver operations on ++ * objects with index number N and can be used to add/remove pipelines of ++ * objects whilst other objects are unaffected. ++ * ++ *
    Byte Controls
    ++ * A byte control is defined as a new section that can include channel mapping, ++ * TLV data, callback operations and private data. The bytes section also ++ * includes a few other config options that are shown here :- ++ * ++ *
    ++ * SectionControlBytes."name" {
    ++ *	comment "optional comments"
    ++ *
    ++ *	index "1"			# Index number
    ++ *
    ++ *	channel."name" {		# Channel maps
    ++ *	   ....
    ++ *	}
    ++ *
    ++ *	ops."ctl" {			# Ops callback functions
    ++ *	   ....
    ++ *	}
    ++ *
    ++ *	base "0"			# Register base
    ++ *	num_regs "16"			# Number of registers
    ++ *	mask "0xff"			# Mask
    ++ *	max "255"			# Maximum value
    ++ *
    ++ *	tlv "tld_data"			# optional TLV data
    ++ *
    ++ *	data "pdata for mixer1"		# optional private data
    ++ * }
    ++ * 
    ++ * ++ *
    Enumerated Controls
    ++ * A enumerated control is defined as a new section (like mixer and byte) that ++ * can include channel mapping, callback operations, private data and ++ * text strings to represent the enumerated control options.
    ++ * ++ * The text strings for the enumerated controls are defined in a seperate ++ * section as follows :- ++ * ++ *
    ++ * SectionText."name" {
    ++ *
    ++ *		Values [
    ++ *			"value1"
    ++ *			"value2"
    ++			"value3"
    ++ *		]
    ++ * }
    ++ * 
    ++ * ++ * All the enumerated text values are listed in the values list.
    ++ * The enumerated control is similar to the other controls and defined as ++ * follows :- ++ * ++ *
    ++ * SectionControlMixer."name" {
    ++ *	comment "optional comments"
    ++ *
    ++ *	index "1"			# Index number
    ++ *
    ++ *	texts "EQU1"			# Enumerated text items
    ++ *
    ++ *	channel."name" {		# Channel maps
    ++ *	   ....
    ++ *	}
    ++ *
    ++ *	ops."ctl" {			# Ops callback functions
    ++ *	   ....
    ++ *	}
    ++ *
    ++ *	data "pdata for mixer1"		# optional private data
    ++ * }
    ++ * 
    ++ * ++ *

    DAPM Graph

    ++ * DAPM graphs can easily be defined using the topology file. The format is ++ * very similar to the DAPM graph kernel format. :- ++ * ++ *
    ++ * SectionGraph."dsp" {
    ++ *	index "1"			# Index number
    ++ *
    ++ *	lines [
    ++ *		"sink1, control, source1"
    ++ *		"sink2, , source2"
    ++ *	]
    ++ * }
    ++ * 
    ++ * ++ * The lines in the graph are defined as a variable size list of sinks, ++ * controls and sources. The control name is optional as some graph lines have ++ * no associated controls. The section name can be used to differentiate the ++ * graph with other graphs, it's not used by the kernel atm. ++ * ++ *

    DAPM Widgets

    ++ * DAPM wigets are similar to controls in that they can include many other ++ * objects. Widgets can contain private data, mixer controls and enum controls. ++ * ++ * The following widget types are supported and match the driver types :- ++ * ++ * * input ++ * * output ++ * * mux ++ * * mixer ++ * * pga ++ * * out_drv ++ * * adc ++ * * dac ++ * * switch ++ * * pre ++ * * post ++ * * aif_in ++ * * aif_out ++ * * dai_in ++ * * dai_out ++ * * dai_link ++ * ++ * Widgets are defined as follows :- ++ * ++ *
    ++ * SectionWidget."name" {
    ++ *
    ++ *	index "1"			# Index number
    ++ *
    ++ *	type "aif_in"			# Widget type - detailed above
    ++ *
    ++ *	no_pm "true"			# No PM control bit.
    ++ *	reg "20"			# PM bit register offset
    ++ *	shift "0"			# PM bit register shift
    ++ *	invert "1			# PM bit is inverted
    ++ *	subseq "8"			# subsequence number
    ++ *
    ++ *	event_type "1"			# DAPM widget event type
    ++ *	event_flags "1"			# DAPM widget event flags
    ++ *
    ++ *	mixer "name"			# Optional Mixer Control
    ++ *	enum "name"			# Optional Enum Control
    ++ *
    ++ *	data "name"			# optional private data
    ++ * }
    ++ * 
    ++ * ++ * The section name is the widget name. The mixer and enum fields are mutually ++ * exclusive and used to include controls into the widget. The index and data ++ * fields are the same for widgets as they are for controls whilst the other ++ * fields map on very closely to the driver widget fields. ++ * ++ *

    PCM Capabilities

    ++ * Topology can also define the capabilities of FE and BE PCMs. Capabilities ++ * can be defined with the following section :- ++ * ++ *
    ++ * SectionPCMCapabilities."name" {
    ++ *
    ++ *	formats "S24_LE,S16_LE"		# Supported formats
    ++ *	rate_min "48000"		# Max supported sample rate
    ++ *	rate_max "48000"		# Min suppoprted sample rate
    ++ *	channels_min "2"		# Min number of channels
    ++ *	channels_max "2"		# max number of channels
    ++ * }
    ++ * 
    ++ * The supported formats use the same naming convention as the driver macros. ++ * The PCM capabilities name can be reffered to and included by BE, PCM and ++ * Codec <-> codec topology sections. ++ * ++ *

    PCM Configurations

    ++ * PCM runtime configurations can be defined for playback and capture stream ++ * directions with the following section :- ++ * ++ *
    ++ * SectionPCMConfig."name" {
    ++ *
    ++ *	config."playback" {		# playback config
    ++ *		format "S16_LE"		# playback format
    ++ *		rate "48000"		# playback sample rate
    ++ *		channels "2"		# playback channels
    ++ *		tdm_slot "0xf"		# playback TDM slot
    ++ *	}
    ++ *
    ++ *	config."capture" {		# capture config
    ++ *		format "S16_LE"		# capture format
    ++ *		rate "48000"		# capture sample rate
    ++ *		channels "2"		# capture channels
    ++ *		tdm_slot "0xf"		# capture TDM slot
    ++ *	}
    ++ * }
    ++ * 
    ++ * ++ * The supported formats use the same naming convention as the driver macros. ++ * The PCM configuration name can be reffered to and included by BE, PCM and ++ * Codec <-> codec topology sections. ++ * ++ *

    PCM Configurations

    ++ * PCM, BE and Codec to Codec link sections define the supported capabilities ++ * and configurations for supported playback and capture streams. The ++ * definitions and content for PCMs, BE and Codec links are the same with the ++ * exception of the section type :- ++ * ++ *
    ++ * SectionPCM."name" {
    ++ *	....
    ++ * }
    ++ * SectionBE."name" {
    ++ *	....
    ++ * }
    ++ * SectionCC."name" {
    ++ *	....
    ++ * }
    ++ * 
    ++ * ++ * The section types above should be used for PCMs, Back Ends and Codec to Codec ++ * links respectively.
    ++ * ++ * The data for each section is defined as follows :- ++ * ++ *
    ++ * SectionPCM."name" {
    ++ *
    ++ *	index "1"			# Index number
    ++ *
    ++ *	id "0"				# used for binding to the PCM
    ++ *
    ++ *	pcm."playback" {
    ++ *		capabilities "capabilities1"	# capbilities for playback
    ++ *
    ++ *		configs [		# supported configs for playback
    ++ *			"config1"
    ++ *			"config2"
    ++ *		]
    ++ *	}
    ++ *
    ++ *	pcm."capture" {
    ++ *		capabilities "capabilities2"	# capabilities for capture
    ++ *
    ++ *		configs [		# supported configs for capture
    ++ *			"config1"
    ++ *			"config2"
    ++ *			"config3"
    ++ *		]
    ++ *	}
    ++ * }
    ++ * 
    ++ * ++ */ ++ ++/** Topology context */ ++typedef struct snd_tplg snd_tplg_t; ++ ++/** ++ * \brief Create a new topology parser instance. ++ * \return New topology parser instance ++ */ ++snd_tplg_t *snd_tplg_new(void); ++ ++/** ++ * \brief Free a topology parser instance. ++ * \param tplg Topology parser instance ++ */ ++void snd_tplg_free(snd_tplg_t *tplg); ++ ++/** ++ * \brief Parse and build topology text file into binary file. ++ * \param tplg Topology instance. ++ * \param infile Topology text input file to be parsed ++ * \param outfile Binary topology output file. ++ * \return Zero on sucess, otherwise a negative error code ++ */ ++int snd_tplg_build_file(snd_tplg_t *tplg, const char *infile, ++ const char *outfile); ++ ++/** ++ * \brief Enable verbose reporting of binary file output ++ * \param tplg Topology Instance ++ * \param verbose Enable verbose output level if non zero ++ */ ++void snd_tplg_verbose(snd_tplg_t *tplg, int verbose); ++ ++/* \} */ ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* __ALSA_TOPOLOGY_H */ +diff --git a/src/topology/elem.c b/src/topology/elem.c +new file mode 100644 +index 000000000000..32ba2c12375b +--- /dev/null ++++ b/src/topology/elem.c +@@ -0,0 +1,187 @@ ++/* ++ Copyright(c) 2014-2015 Intel Corporation ++ All rights reserved. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of version 2 of the GNU General Public License as ++ published by the Free Software Foundation. ++ ++ This program is distributed in the hope that it will be useful, but ++ WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ General Public License for more details. ++ ++ Authors: Mengdong Lin ++ Yao Jin ++ Liam Girdwood ++*/ ++ ++#include "list.h" ++#include "tplg_local.h" ++ ++int tplg_ref_add(struct tplg_elem *elem, int type, const char* id) ++{ ++ struct tplg_ref *ref; ++ ++ ref = calloc(1, sizeof(*ref)); ++ if (!ref) ++ return -ENOMEM; ++ ++ strncpy(ref->id, id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); ++ ref->id[SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1] = 0; ++ ref->type = type; ++ ++ list_add_tail(&ref->list, &elem->ref_list); ++ return 0; ++} ++ ++void tplg_ref_free_list(struct list_head *base) ++{ ++ struct list_head *pos, *npos; ++ struct tplg_ref *ref; ++ ++ list_for_each_safe(pos, npos, base) { ++ ref = list_entry(pos, struct tplg_ref, list); ++ list_del(&ref->list); ++ free(ref); ++ } ++} ++ ++struct tplg_elem *tplg_elem_new(void) ++{ ++ struct tplg_elem *elem; ++ ++ elem = calloc(1, sizeof(*elem)); ++ if (!elem) ++ return NULL; ++ ++ INIT_LIST_HEAD(&elem->ref_list); ++ return elem; ++} ++ ++void tplg_elem_free(struct tplg_elem *elem) ++{ ++ tplg_ref_free_list(&elem->ref_list); ++ ++ /* free struct snd_tplg_ object, ++ * the union pointers share the same address ++ */ ++ if (elem->mixer_ctrl) ++ free(elem->mixer_ctrl); ++ ++ free(elem); ++} ++ ++void tplg_elem_free_list(struct list_head *base) ++{ ++ struct list_head *pos, *npos; ++ struct tplg_elem *elem; ++ ++ list_for_each_safe(pos, npos, base) { ++ elem = list_entry(pos, struct tplg_elem, list); ++ list_del(&elem->list); ++ tplg_elem_free(elem); ++ } ++} ++ ++struct tplg_elem *tplg_elem_lookup(struct list_head *base, const char* id, ++ unsigned int type) ++{ ++ struct list_head *pos; ++ struct tplg_elem *elem; ++ ++ list_for_each(pos, base) { ++ ++ elem = list_entry(pos, struct tplg_elem, list); ++ ++ if (!strcmp(elem->id, id) && elem->type == type) ++ return elem; ++ } ++ ++ return NULL; ++} ++ ++/* create a new common element and object */ ++struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg, ++ snd_config_t *cfg, enum object_type type) ++{ ++ struct tplg_elem *elem; ++ const char *id; ++ int obj_size = 0; ++ void *obj; ++ ++ elem = tplg_elem_new(); ++ if (!elem) ++ return NULL; ++ ++ snd_config_get_id(cfg, &id); ++ strncpy(elem->id, id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); ++ elem->id[SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1] = 0; ++ ++ switch (type) { ++ case OBJECT_TYPE_DATA: ++ list_add_tail(&elem->list, &tplg->pdata_list); ++ break; ++ case OBJECT_TYPE_TEXT: ++ list_add_tail(&elem->list, &tplg->text_list); ++ break; ++ case OBJECT_TYPE_TLV: ++ list_add_tail(&elem->list, &tplg->tlv_list); ++ elem->size = sizeof(struct snd_soc_tplg_ctl_tlv); ++ break; ++ case OBJECT_TYPE_BYTES: ++ list_add_tail(&elem->list, &tplg->bytes_ext_list); ++ obj_size = sizeof(struct snd_soc_tplg_bytes_control); ++ break; ++ case OBJECT_TYPE_ENUM: ++ list_add_tail(&elem->list, &tplg->enum_list); ++ obj_size = sizeof(struct snd_soc_tplg_enum_control); ++ break; ++ case SND_SOC_TPLG_TYPE_MIXER: ++ list_add_tail(&elem->list, &tplg->mixer_list); ++ obj_size = sizeof(struct snd_soc_tplg_mixer_control); ++ break; ++ case OBJECT_TYPE_DAPM_WIDGET: ++ list_add_tail(&elem->list, &tplg->widget_list); ++ obj_size = sizeof(struct snd_soc_tplg_dapm_widget); ++ break; ++ case OBJECT_TYPE_STREAM_CONFIG: ++ list_add_tail(&elem->list, &tplg->pcm_config_list); ++ obj_size = sizeof(struct snd_soc_tplg_stream_config); ++ break; ++ case OBJECT_TYPE_STREAM_CAPS: ++ list_add_tail(&elem->list, &tplg->pcm_caps_list); ++ obj_size = sizeof(struct snd_soc_tplg_stream_caps); ++ break; ++ case OBJECT_TYPE_PCM: ++ list_add_tail(&elem->list, &tplg->pcm_list); ++ obj_size = sizeof(struct snd_soc_tplg_pcm_dai); ++ break; ++ case OBJECT_TYPE_BE: ++ list_add_tail(&elem->list, &tplg->be_list); ++ obj_size = sizeof(struct snd_soc_tplg_pcm_dai); ++ break; ++ case OBJECT_TYPE_CC: ++ list_add_tail(&elem->list, &tplg->cc_list); ++ obj_size = sizeof(struct snd_soc_tplg_pcm_dai); ++ break; ++ default: ++ free(elem); ++ return NULL; ++ } ++ ++ /* create new object too if required */ ++ if (obj_size > 0) { ++ obj = calloc(1, obj_size); ++ if (obj == NULL) { ++ free(elem); ++ return NULL; ++ } ++ ++ elem->obj = obj; ++ elem->size = obj_size; ++ } ++ ++ elem->type = type; ++ return elem; ++} +diff --git a/src/topology/parser.c b/src/topology/parser.c +new file mode 100644 +index 000000000000..ed25bb88d446 +--- /dev/null ++++ b/src/topology/parser.c +@@ -0,0 +1,359 @@ ++/* ++ Copyright(c) 2014-2015 Intel Corporation ++ All rights reserved. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of version 2 of the GNU General Public License as ++ published by the Free Software Foundation. ++ ++ This program is distributed in the hope that it will be useful, but ++ WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ General Public License for more details. ++ ++ Authors: Mengdong Lin ++ Yao Jin ++ Liam Girdwood ++*/ ++ ++#include "list.h" ++#include "tplg_local.h" ++ ++/* ++ * Parse compound ++ */ ++int tplg_parse_compound(snd_tplg_t *tplg, snd_config_t *cfg, ++ int (*fcn)(snd_tplg_t *, snd_config_t *, void *), ++ void *private) ++{ ++ const char *id; ++ snd_config_iterator_t i, next; ++ snd_config_t *n; ++ int err = -EINVAL; ++ ++ if (snd_config_get_id(cfg, &id) < 0) ++ return -EINVAL; ++ ++ if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { ++ SNDERR("error: compound type expected for %s", id); ++ return -EINVAL; ++ } ++ ++ /* parse compound */ ++ snd_config_for_each(i, next, cfg) { ++ n = snd_config_iterator_entry(i); ++ ++ if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { ++ SNDERR("error: compound type expected for %s, is %d", ++ id, snd_config_get_type(cfg)); ++ return -EINVAL; ++ } ++ ++ err = fcn(tplg, n, private); ++ if (err < 0) ++ return err; ++ } ++ ++ return err; ++} ++ ++static int tplg_parse_config(snd_tplg_t *tplg, snd_config_t *cfg) ++{ ++ snd_config_iterator_t i, next; ++ snd_config_t *n; ++ const char *id; ++ int err; ++ ++ if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { ++ SNDERR("error: compound type expected at top level"); ++ return -EINVAL; ++ } ++ ++ /* parse topology config sections */ ++ snd_config_for_each(i, next, cfg) { ++ ++ n = snd_config_iterator_entry(i); ++ if (snd_config_get_id(n, &id) < 0) ++ continue; ++ ++ if (strcmp(id, "SectionTLV") == 0) { ++ err = tplg_parse_compound(tplg, n, tplg_parse_tlv, ++ NULL); ++ if (err < 0) ++ return err; ++ continue; ++ } ++ ++ if (strcmp(id, "SectionControlMixer") == 0) { ++ err = tplg_parse_compound(tplg, n, ++ tplg_parse_control_mixer, NULL); ++ if (err < 0) ++ return err; ++ continue; ++ } ++ ++ if (strcmp(id, "SectionControlEnum") == 0) { ++ err = tplg_parse_compound(tplg, n, ++ tplg_parse_control_enum, NULL); ++ if (err < 0) ++ return err; ++ continue; ++ } ++ ++ if (strcmp(id, "SectionControlBytes") == 0) { ++ err = tplg_parse_compound(tplg, n, ++ tplg_parse_control_bytes, NULL); ++ if (err < 0) ++ return err; ++ continue; ++ } ++ ++ if (strcmp(id, "SectionWidget") == 0) { ++ err = tplg_parse_compound(tplg, n, ++ tplg_parse_dapm_widget, NULL); ++ if (err < 0) ++ return err; ++ continue; ++ } ++ ++ if (strcmp(id, "SectionPCMConfig") == 0) { ++ err = tplg_parse_compound(tplg, n, ++ tplg_parse_pcm_config, NULL); ++ if (err < 0) ++ return err; ++ continue; ++ } ++ ++ if (strcmp(id, "SectionPCMCapabilities") == 0) { ++ err = tplg_parse_compound(tplg, n, ++ tplg_parse_pcm_caps, NULL); ++ if (err < 0) ++ return err; ++ continue; ++ } ++ ++ if (strcmp(id, "SectionPCM") == 0) { ++ err = tplg_parse_compound(tplg, n, ++ tplg_parse_pcm, NULL); ++ if (err < 0) ++ return err; ++ continue; ++ } ++ ++ if (strcmp(id, "SectionBE") == 0) { ++ err = tplg_parse_compound(tplg, n, tplg_parse_be, ++ NULL); ++ if (err < 0) ++ return err; ++ continue; ++ } ++ ++ if (strcmp(id, "SectionCC") == 0) { ++ err = tplg_parse_compound(tplg, n, tplg_parse_cc, ++ NULL); ++ if (err < 0) ++ return err; ++ continue; ++ } ++ ++ if (strcmp(id, "SectionGraph") == 0) { ++ err = tplg_parse_compound(tplg, n, ++ tplg_parse_dapm_graph, NULL); ++ if (err < 0) ++ return err; ++ continue; ++ } ++ ++ if (strcmp(id, "SectionText") == 0) { ++ err = tplg_parse_compound(tplg, n, tplg_parse_text, ++ NULL); ++ if (err < 0) ++ return err; ++ continue; ++ } ++ ++ if (strcmp(id, "SectionData") == 0) { ++ err = tplg_parse_compound(tplg, n, tplg_parse_data, ++ NULL); ++ if (err < 0) ++ return err; ++ continue; ++ } ++ ++ SNDERR("error: unknown section %s\n", id); ++ } ++ return 0; ++} ++ ++static int tplg_load_config(const char *file, snd_config_t **cfg) ++{ ++ FILE *fp; ++ snd_input_t *in; ++ snd_config_t *top; ++ int ret; ++ ++ fp = fopen(file, "r"); ++ if (fp == NULL) { ++ SNDERR("error: could not open configuration file %s", ++ file); ++ return -errno; ++ } ++ ++ ret = snd_input_stdio_attach(&in, fp, 1); ++ if (ret < 0) { ++ SNDERR("error: could not attach stdio %s", file); ++ goto err; ++ } ++ ret = snd_config_top(&top); ++ if (ret < 0) ++ goto err; ++ ++ ret = snd_config_load(top, in); ++ if (ret < 0) { ++ SNDERR("error: could not load configuration file %s", ++ file); ++ goto err_load; ++ } ++ ++ ret = snd_input_close(in); ++ if (ret < 0) ++ goto err_load; ++ ++ *cfg = top; ++ return 0; ++ ++err_load: ++ snd_config_delete(top); ++err: ++ fclose(fp); ++ return ret; ++} ++ ++static int tplg_build_integ(snd_tplg_t *tplg) ++{ ++ int err; ++ ++ err = tplg_build_controls(tplg); ++ if (err < 0) ++ return err; ++ ++ err = tplg_build_widgets(tplg); ++ if (err < 0) ++ return err; ++ ++ err = tplg_build_pcm_dai(tplg, OBJECT_TYPE_PCM); ++ if (err < 0) ++ return err; ++ ++ err = tplg_build_pcm_dai(tplg, OBJECT_TYPE_BE); ++ if (err < 0) ++ return err; ++ ++ err = tplg_build_pcm_dai(tplg, OBJECT_TYPE_CC); ++ if (err < 0) ++ return err; ++ ++ err = tplg_build_routes(tplg); ++ if (err < 0) ++ return err; ++ ++ return err; ++} ++ ++int snd_tplg_build_file(snd_tplg_t *tplg, const char *infile, ++ const char *outfile) ++{ ++ snd_config_t *cfg = NULL; ++ int err = 0; ++ ++ /* delete any old output files */ ++ unlink(outfile); ++ ++ tplg->out_fd = ++ open(outfile, O_RDWR | O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO); ++ if (tplg->out_fd < 0) { ++ SNDERR("error: failed to open %s err %d\n", ++ outfile, -errno); ++ return -errno; ++ } ++ ++ err = tplg_load_config(infile, &cfg); ++ if (err < 0) { ++ SNDERR("error: failed to load topology file %s\n", ++ infile); ++ goto out_close; ++ } ++ ++ err = tplg_parse_config(tplg, cfg); ++ if (err < 0) { ++ SNDERR("error: failed to parse topology\n"); ++ goto out; ++ } ++ ++ err = tplg_build_integ(tplg); ++ if (err < 0) { ++ SNDERR("error: failed to check topology integrity\n"); ++ goto out; ++ } ++ ++ err = tplg_write_data(tplg); ++ if (err < 0) { ++ SNDERR("error: failed to write data %d\n", err); ++ goto out; ++ } ++ ++out: ++ snd_config_delete(cfg); ++out_close: ++ close(tplg->out_fd); ++ return err; ++} ++ ++void snd_tplg_verbose(snd_tplg_t *tplg, int verbose) ++{ ++ tplg->verbose = verbose; ++} ++ ++snd_tplg_t *snd_tplg_new(void) ++{ ++ snd_tplg_t *tplg; ++ ++ tplg = calloc(1, sizeof(snd_tplg_t)); ++ if (!tplg) ++ return NULL; ++ ++ INIT_LIST_HEAD(&tplg->tlv_list); ++ INIT_LIST_HEAD(&tplg->widget_list); ++ INIT_LIST_HEAD(&tplg->pcm_list); ++ INIT_LIST_HEAD(&tplg->be_list); ++ INIT_LIST_HEAD(&tplg->cc_list); ++ INIT_LIST_HEAD(&tplg->route_list); ++ INIT_LIST_HEAD(&tplg->pdata_list); ++ INIT_LIST_HEAD(&tplg->text_list); ++ INIT_LIST_HEAD(&tplg->pcm_config_list); ++ INIT_LIST_HEAD(&tplg->pcm_caps_list); ++ INIT_LIST_HEAD(&tplg->mixer_list); ++ INIT_LIST_HEAD(&tplg->enum_list); ++ INIT_LIST_HEAD(&tplg->bytes_ext_list); ++ ++ return tplg; ++} ++ ++void snd_tplg_free(snd_tplg_t *tplg) ++{ ++ tplg_elem_free_list(&tplg->tlv_list); ++ tplg_elem_free_list(&tplg->widget_list); ++ tplg_elem_free_list(&tplg->pcm_list); ++ tplg_elem_free_list(&tplg->be_list); ++ tplg_elem_free_list(&tplg->cc_list); ++ tplg_elem_free_list(&tplg->route_list); ++ tplg_elem_free_list(&tplg->pdata_list); ++ tplg_elem_free_list(&tplg->text_list); ++ tplg_elem_free_list(&tplg->pcm_config_list); ++ tplg_elem_free_list(&tplg->pcm_caps_list); ++ tplg_elem_free_list(&tplg->mixer_list); ++ tplg_elem_free_list(&tplg->enum_list); ++ tplg_elem_free_list(&tplg->bytes_ext_list); ++ ++ free(tplg); ++} +diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h +new file mode 100644 +index 000000000000..688c78f3a6a4 +--- /dev/null ++++ b/src/topology/tplg_local.h +@@ -0,0 +1,234 @@ ++/* ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++ ++#include "local.h" ++#include "list.h" ++#include "topology.h" ++ ++#include ++#include ++#include ++ ++#define TPLG_DEBUG ++#ifdef TPLG_DEBUG ++#define tplg_dbg SNDERR ++#else ++#define tplg_dbg(fmt, arg...) do { } while (0) ++#endif ++ ++#define MAX_FILE 256 ++#define TPLG_MAX_PRIV_SIZE (1024 * 128) ++#define ALSA_TPLG_DIR ALSA_CONFIG_DIR "/topology" ++#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) ++ ++/** The name of the environment variable containing the tplg directory */ ++#define ALSA_CONFIG_TPLG_VAR "ALSA_CONFIG_TPLG" ++ ++struct tplg_ref; ++struct tplg_elem; ++ ++/* internal topology object type not used by kernel */ ++enum object_type { ++ OBJECT_TYPE_TLV = 0, ++ OBJECT_TYPE_MIXER, ++ OBJECT_TYPE_ENUM, ++ OBJECT_TYPE_TEXT, ++ OBJECT_TYPE_DATA, ++ OBJECT_TYPE_BYTES, ++ OBJECT_TYPE_STREAM_CONFIG, ++ OBJECT_TYPE_STREAM_CAPS, ++ OBJECT_TYPE_PCM, ++ OBJECT_TYPE_DAPM_WIDGET, ++ OBJECT_TYPE_DAPM_GRAPH, ++ OBJECT_TYPE_BE, ++ OBJECT_TYPE_CC, ++ OBJECT_TYPE_MANIFEST, ++}; ++ ++struct snd_tplg { ++ ++ /* opaque vendor data */ ++ int vendor_fd; ++ char *vendor_name; ++ ++ /* out file */ ++ int out_fd; ++ ++ int verbose; ++ unsigned int version; ++ ++ /* runtime state */ ++ unsigned int next_hdr_pos; ++ int index; ++ int channel_idx; ++ ++ /* manifest */ ++ struct snd_soc_tplg_manifest manifest; ++ ++ /* list of each element type */ ++ struct list_head tlv_list; ++ struct list_head widget_list; ++ struct list_head pcm_list; ++ struct list_head be_list; ++ struct list_head cc_list; ++ struct list_head route_list; ++ struct list_head text_list; ++ struct list_head pdata_list; ++ struct list_head pcm_config_list; ++ struct list_head pcm_caps_list; ++ ++ /* type-specific control lists */ ++ struct list_head mixer_list; ++ struct list_head enum_list; ++ struct list_head bytes_ext_list; ++}; ++ ++/* object text references */ ++struct tplg_ref { ++ unsigned int type; ++ struct tplg_elem *elem; ++ char id[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; ++ struct list_head list; ++}; ++ ++/* topology element */ ++struct tplg_elem { ++ ++ char id[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; ++ ++ /* storage for texts and data if this is text or data elem*/ ++ char texts[SND_SOC_TPLG_NUM_TEXTS][SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; ++ ++ int index; ++ enum object_type type; ++ ++ int size; /* total size of this object inc pdata and ref objects */ ++ int compound_elem; /* dont write this element as individual elem */ ++ int vendor_type; /* vendor type for private data */ ++ ++ /* UAPI object for this elem */ ++ union { ++ void *obj; ++ struct snd_soc_tplg_mixer_control *mixer_ctrl; ++ struct snd_soc_tplg_enum_control *enum_ctrl; ++ struct snd_soc_tplg_bytes_control *bytes_ext; ++ struct snd_soc_tplg_dapm_widget *widget; ++ struct snd_soc_tplg_pcm_dai *pcm; ++ struct snd_soc_tplg_pcm_dai *be; ++ struct snd_soc_tplg_pcm_dai *cc; ++ struct snd_soc_tplg_dapm_graph_elem *route; ++ struct snd_soc_tplg_stream_config *stream_cfg; ++ struct snd_soc_tplg_stream_caps *stream_caps; ++ ++ /* these do not map to UAPI structs but are internal only */ ++ struct snd_soc_tplg_ctl_tlv *tlv; ++ struct snd_soc_tplg_private *data; ++ }; ++ ++ /* an element may refer to other elements: ++ * a mixer control may refer to a tlv, ++ * a widget may refer to a mixer control array, ++ * a graph may refer to some widgets. ++ */ ++ struct list_head ref_list; ++ struct list_head list; /* list of all elements with same type */ ++}; ++ ++struct map_elem { ++ const char *name; ++ int id; ++}; ++ ++int tplg_parse_compound(snd_tplg_t *tplg, snd_config_t *cfg, ++ int (*fcn)(snd_tplg_t *, snd_config_t *, void *), ++ void *private); ++ ++int tplg_write_data(snd_tplg_t *tplg); ++ ++int tplg_parse_tlv(snd_tplg_t *tplg, snd_config_t *cfg, ++ void *private ATTRIBUTE_UNUSED); ++ ++int tplg_parse_text(snd_tplg_t *tplg, snd_config_t *cfg, ++ void *private ATTRIBUTE_UNUSED); ++ ++int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg, ++ void *private ATTRIBUTE_UNUSED); ++ ++int tplg_parse_control_bytes(snd_tplg_t *tplg, ++ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED); ++ ++int tplg_parse_control_enum(snd_tplg_t *tplg, snd_config_t *cfg, ++ void *private ATTRIBUTE_UNUSED); ++ ++int tplg_parse_control_mixer(snd_tplg_t *tplg, ++ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED); ++ ++int tplg_parse_dapm_graph(snd_tplg_t *tplg, snd_config_t *cfg, ++ void *private ATTRIBUTE_UNUSED); ++ ++int tplg_parse_dapm_widget(snd_tplg_t *tplg, ++ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED); ++ ++int tplg_parse_pcm_config(snd_tplg_t *tplg, ++ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED); ++ ++int tplg_parse_pcm_caps(snd_tplg_t *tplg, ++ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED); ++ ++int tplg_parse_pcm_cap_cfg(snd_tplg_t *tplg, snd_config_t *cfg, ++ void *private); ++ ++int tplg_parse_pcm(snd_tplg_t *tplg, ++ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED); ++ ++int tplg_parse_be(snd_tplg_t *tplg, ++ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED); ++ ++int tplg_parse_cc(snd_tplg_t *tplg, ++ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED); ++ ++int tplg_build_controls(snd_tplg_t *tplg); ++int tplg_build_widgets(snd_tplg_t *tplg); ++int tplg_build_routes(snd_tplg_t *tplg); ++int tplg_build_pcm_dai(snd_tplg_t *tplg, unsigned int type); ++ ++int tplg_copy_data(struct tplg_elem *elem, struct tplg_elem *ref); ++ ++int tplg_ref_add(struct tplg_elem *elem, int type, const char* id); ++ ++struct tplg_elem *tplg_elem_new(void); ++void tplg_elem_free(struct tplg_elem *elem); ++void tplg_elem_free_list(struct list_head *base); ++struct tplg_elem *tplg_elem_lookup(struct list_head *base, ++ const char* id, ++ unsigned int type); ++struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg, ++ snd_config_t *cfg, enum object_type type); ++ ++static inline void elem_copy_text(char *dest, const char *src, int len) ++{ ++ strncpy(dest, src, len); ++ dest[len - 1] = 0; ++} ++ ++int tplg_parse_channel(snd_tplg_t *tplg ATTRIBUTE_UNUSED, ++ snd_config_t *cfg, void *private); ++ ++int tplg_parse_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED, ++ snd_config_t *cfg, void *private); ++ ++struct tplg_elem *lookup_pcm_dai_stream(struct list_head *base, ++ const char* id); +-- +2.5.0 + diff --git a/0037-topology-Add-text-section-parser.patch b/0037-topology-Add-text-section-parser.patch new file mode 100644 index 0000000..5b4a39f --- /dev/null +++ b/0037-topology-Add-text-section-parser.patch @@ -0,0 +1,112 @@ +From 408396a8ca092846c840baa79c04b5f7dbe5da69 Mon Sep 17 00:00:00 2001 +From: Liam Girdwood +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 +Signed-off-by: Takashi Iwai +--- + 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 ++ Yao Jin ++ Liam Girdwood ++ ++*/ ++ ++#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 + diff --git a/0038-topology-Add-PCM-parser.patch b/0038-topology-Add-PCM-parser.patch new file mode 100644 index 0000000..1a3c518 --- /dev/null +++ b/0038-topology-Add-PCM-parser.patch @@ -0,0 +1,664 @@ +From 4db19506c3e7a68a0d0be40422172f22605c58d8 Mon Sep 17 00:00:00 2001 +From: Liam Girdwood +Date: Wed, 29 Jul 2015 17:45:16 +0100 +Subject: [PATCH 38/49] topology: Add PCM parser. + +Parse PCM configurations and capabilities. These can then be used to define +the capabilities and config for FE DAI links, PCM devices and +codec <-> codec style links. + +Signed-off-by: Liam Girdwood +Signed-off-by: Takashi Iwai +--- + src/topology/pcm.c | 639 +++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 639 insertions(+) + create mode 100644 src/topology/pcm.c + +diff --git a/src/topology/pcm.c b/src/topology/pcm.c +new file mode 100644 +index 000000000000..8f23a6f12ec4 +--- /dev/null ++++ b/src/topology/pcm.c +@@ -0,0 +1,639 @@ ++/* ++ Copyright(c) 2014-2015 Intel Corporation ++ All rights reserved. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of version 2 of the GNU General Public License as ++ published by the Free Software Foundation. ++ ++ This program is distributed in the hope that it will be useful, but ++ WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ General Public License for more details. ++ ++ Authors: Mengdong Lin ++ Yao Jin ++ Liam Girdwood ++*/ ++ ++#include "list.h" ++#include "tplg_local.h" ++ ++struct tplg_elem *lookup_pcm_dai_stream(struct list_head *base, const char* id) ++{ ++ struct list_head *pos; ++ struct tplg_elem *elem; ++ struct snd_soc_tplg_pcm_dai *pcm_dai; ++ ++ list_for_each(pos, base) { ++ ++ elem = list_entry(pos, struct tplg_elem, list); ++ if (elem->type != OBJECT_TYPE_PCM) ++ return NULL; ++ ++ pcm_dai = elem->pcm; ++ ++ if (pcm_dai && (!strcmp(pcm_dai->capconf[0].caps.name, id) ++ || !strcmp(pcm_dai->capconf[1].caps.name, id))) ++ return elem; ++ } ++ ++ return NULL; ++} ++ ++/* copy referenced caps to the pcm */ ++static void copy_pcm_caps(const char *id, struct snd_soc_tplg_stream_caps *caps, ++ struct tplg_elem *ref_elem) ++{ ++ struct snd_soc_tplg_stream_caps *ref_caps = ref_elem->stream_caps; ++ ++ tplg_dbg("Copy pcm caps (%ld bytes) from '%s' to '%s' \n", ++ sizeof(*caps), ref_elem->id, id); ++ ++ *caps = *ref_caps; ++} ++ ++/* copy referenced config to the pcm */ ++static void copy_pcm_config(const char *id, ++ struct snd_soc_tplg_stream_config *cfg, struct tplg_elem *ref_elem) ++{ ++ struct snd_soc_tplg_stream_config *ref_cfg = ref_elem->stream_cfg; ++ ++ tplg_dbg("Copy pcm config (%ld bytes) from '%s' to '%s' \n", ++ sizeof(*cfg), ref_elem->id, id); ++ ++ *cfg = *ref_cfg; ++} ++ ++/* check referenced config and caps for a pcm */ ++static int tplg_build_pcm_cfg_caps(snd_tplg_t *tplg, struct tplg_elem *elem) ++{ ++ struct tplg_elem *ref_elem = NULL; ++ struct snd_soc_tplg_pcm_cfg_caps *capconf; ++ struct snd_soc_tplg_pcm_dai *pcm_dai; ++ unsigned int i, j; ++ ++ switch (elem->type) { ++ case OBJECT_TYPE_PCM: ++ pcm_dai = elem->pcm; ++ break; ++ case OBJECT_TYPE_BE: ++ pcm_dai = elem->be; ++ break; ++ case OBJECT_TYPE_CC: ++ pcm_dai = elem->cc; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < 2; i++) { ++ capconf = &pcm_dai->capconf[i]; ++ ++ ref_elem = tplg_elem_lookup(&tplg->pcm_caps_list, ++ capconf->caps.name, OBJECT_TYPE_STREAM_CAPS); ++ ++ if (ref_elem != NULL) ++ copy_pcm_caps(elem->id, &capconf->caps, ref_elem); ++ ++ for (j = 0; j < capconf->num_configs; j++) { ++ ref_elem = tplg_elem_lookup(&tplg->pcm_config_list, ++ capconf->configs[j].name, ++ OBJECT_TYPE_STREAM_CONFIG); ++ ++ if (ref_elem != NULL) ++ copy_pcm_config(elem->id, ++ &capconf->configs[j], ++ ref_elem); ++ } ++ } ++ ++ return 0; ++} ++ ++int tplg_build_pcm_dai(snd_tplg_t *tplg, unsigned int type) ++{ ++ struct list_head *base, *pos; ++ struct tplg_elem *elem; ++ int err = 0; ++ ++ switch (type) { ++ case OBJECT_TYPE_PCM: ++ base = &tplg->pcm_list; ++ break; ++ case OBJECT_TYPE_BE: ++ base = &tplg->be_list; ++ break; ++ case OBJECT_TYPE_CC: ++ base = &tplg->cc_list; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ list_for_each(pos, base) { ++ ++ elem = list_entry(pos, struct tplg_elem, list); ++ if (elem->type != type) { ++ SNDERR("error: invalid elem '%s'\n", elem->id); ++ return -EINVAL; ++ } ++ ++ err = tplg_build_pcm_cfg_caps(tplg, elem); ++ if (err < 0) ++ return err; ++ } ++ ++ return 0; ++} ++ ++/* PCM stream configuration */ ++static int tplg_parse_stream_cfg(snd_tplg_t *tplg ATTRIBUTE_UNUSED, ++ snd_config_t *cfg, void *private) ++{ ++ snd_config_iterator_t i, next; ++ snd_config_t *n; ++ struct snd_soc_tplg_stream_config *sc = private; ++ struct snd_soc_tplg_stream *stream; ++ const char *id, *val; ++ snd_pcm_format_t format; ++ int ret; ++ ++ snd_config_get_id(cfg, &id); ++ ++ if (strcmp(id, "playback") == 0) ++ stream = &sc->playback; ++ else if (strcmp(id, "capture") == 0) ++ stream = &sc->capture; ++ else ++ return -EINVAL; ++ ++ tplg_dbg("\t%s:\n", id); ++ ++ stream->size = sizeof(*stream); ++ ++ snd_config_for_each(i, next, cfg) { ++ ++ n = snd_config_iterator_entry(i); ++ ++ if (snd_config_get_id(n, &id) < 0) ++ return -EINVAL; ++ ++ if (snd_config_get_string(n, &val) < 0) ++ return -EINVAL; ++ ++ if (strcmp(id, "format") == 0) { ++ format = snd_pcm_format_value(val); ++ if (format == SND_PCM_FORMAT_UNKNOWN) { ++ SNDERR("error: unsupported stream format %s\n", ++ val); ++ return -EINVAL; ++ } ++ ++ stream->format = format; ++ tplg_dbg("\t\t%s: %s\n", id, val); ++ continue; ++ } ++ ++ if (strcmp(id, "rate") == 0) { ++ stream->rate = atoi(val); ++ tplg_dbg("\t\t%s: %d\n", id, stream->rate); ++ continue; ++ } ++ ++ if (strcmp(id, "channels") == 0) { ++ stream->channels = atoi(val); ++ tplg_dbg("\t\t%s: %d\n", id, stream->channels); ++ continue; ++ } ++ ++ if (strcmp(id, "tdm_slot") == 0) { ++ stream->tdm_slot = strtol(val, NULL, 16); ++ tplg_dbg("\t\t%s: 0x%x\n", id, stream->tdm_slot); ++ continue; ++ } ++ } ++ ++ return 0; ++} ++ ++/* Parse pcm configuration */ ++int tplg_parse_pcm_config(snd_tplg_t *tplg, ++ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED) ++{ ++ struct snd_soc_tplg_stream_config *sc; ++ struct tplg_elem *elem; ++ snd_config_iterator_t i, next; ++ snd_config_t *n; ++ const char *id; ++ int err; ++ ++ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_STREAM_CONFIG); ++ if (!elem) ++ return -ENOMEM; ++ ++ sc = elem->stream_cfg; ++ sc->size = elem->size; ++ ++ tplg_dbg(" PCM Config: %s\n", elem->id); ++ ++ snd_config_for_each(i, next, cfg) { ++ n = snd_config_iterator_entry(i); ++ if (snd_config_get_id(n, &id) < 0) ++ continue; ++ ++ /* skip comments */ ++ if (strcmp(id, "comment") == 0) ++ continue; ++ if (id[0] == '#') ++ continue; ++ ++ if (strcmp(id, "config") == 0) { ++ err = tplg_parse_compound(tplg, n, ++ tplg_parse_stream_cfg, sc); ++ if (err < 0) ++ return err; ++ continue; ++ } ++ } ++ ++ return 0; ++} ++ ++static int split_format(struct snd_soc_tplg_stream_caps *caps, char *str) ++{ ++ char *s = NULL; ++ snd_pcm_format_t format; ++ int i = 0, ret; ++ ++ s = strtok(str, ","); ++ while ((s != NULL) && (i < SND_SOC_TPLG_MAX_FORMATS)) { ++ format = snd_pcm_format_value(s); ++ if (format == SND_PCM_FORMAT_UNKNOWN) { ++ SNDERR("error: unsupported stream format %s\n", s); ++ return -EINVAL; ++ } ++ ++ caps->formats[i] = format; ++ s = strtok(NULL, ", "); ++ i++; ++ } ++ ++ return 0; ++} ++ ++/* Parse pcm Capabilities */ ++int tplg_parse_pcm_caps(snd_tplg_t *tplg, ++ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED) ++{ ++ struct snd_soc_tplg_stream_caps *sc; ++ struct tplg_elem *elem; ++ snd_config_iterator_t i, next; ++ snd_config_t *n; ++ const char *id, *val; ++ char *s; ++ int err; ++ ++ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_STREAM_CAPS); ++ if (!elem) ++ return -ENOMEM; ++ ++ sc = elem->stream_caps; ++ sc->size = elem->size; ++ elem_copy_text(sc->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); ++ ++ tplg_dbg(" PCM Capabilities: %s\n", elem->id); ++ ++ snd_config_for_each(i, next, cfg) { ++ n = snd_config_iterator_entry(i); ++ if (snd_config_get_id(n, &id) < 0) ++ continue; ++ ++ /* skip comments */ ++ if (strcmp(id, "comment") == 0) ++ continue; ++ if (id[0] == '#') ++ continue; ++ ++ if (snd_config_get_string(n, &val) < 0) ++ return -EINVAL; ++ ++ if (strcmp(id, "formats") == 0) { ++ s = strdup(val); ++ if (s == NULL) ++ return -ENOMEM; ++ ++ err = split_format(sc, s); ++ free(s); ++ ++ if (err < 0) ++ return err; ++ ++ tplg_dbg("\t\t%s: %s\n", id, val); ++ continue; ++ } ++ ++ if (strcmp(id, "rate_min") == 0) { ++ sc->rate_min = atoi(val); ++ tplg_dbg("\t\t%s: %d\n", id, sc->rate_min); ++ continue; ++ } ++ ++ if (strcmp(id, "rate_max") == 0) { ++ sc->rate_max = atoi(val); ++ tplg_dbg("\t\t%s: %d\n", id, sc->rate_max); ++ continue; ++ } ++ ++ if (strcmp(id, "channels_min") == 0) { ++ sc->channels_min = atoi(val); ++ tplg_dbg("\t\t%s: %d\n", id, sc->channels_min); ++ continue; ++ } ++ ++ if (strcmp(id, "channels_max") == 0) { ++ sc->channels_max = atoi(val); ++ tplg_dbg("\t\t%s: %d\n", id, sc->channels_max); ++ continue; ++ } ++ } ++ ++ return 0; ++} ++ ++static int tplg_parse_pcm_cfg(snd_tplg_t *tplg ATTRIBUTE_UNUSED, ++ snd_config_t *cfg, void *private) ++{ ++ struct snd_soc_tplg_pcm_cfg_caps *capconf = private; ++ struct snd_soc_tplg_stream_config *configs = capconf->configs; ++ unsigned int *num_configs = &capconf->num_configs; ++ const char *value; ++ ++ if (*num_configs == SND_SOC_TPLG_STREAM_CONFIG_MAX) ++ return -EINVAL; ++ ++ if (snd_config_get_string(cfg, &value) < 0) ++ return EINVAL; ++ ++ elem_copy_text(configs[*num_configs].name, value, ++ SNDRV_CTL_ELEM_ID_NAME_MAXLEN); ++ ++ *num_configs += 1; ++ ++ tplg_dbg("\t\t\t%s\n", value); ++ ++ return 0; ++} ++ ++/* Parse the cap and config of a pcm */ ++int tplg_parse_pcm_cap_cfg(snd_tplg_t *tplg, snd_config_t *cfg, ++ void *private) ++{ ++ snd_config_iterator_t i, next; ++ snd_config_t *n; ++ struct tplg_elem *elem = private; ++ struct snd_soc_tplg_pcm_dai *pcm_dai; ++ const char *id, *value; ++ int err, stream; ++ ++ if (elem->type == OBJECT_TYPE_PCM) ++ pcm_dai = elem->pcm; ++ else if (elem->type == OBJECT_TYPE_BE) ++ pcm_dai = elem->be; ++ else if (elem->type == OBJECT_TYPE_CC) ++ pcm_dai = elem->cc; ++ else ++ return -EINVAL; ++ ++ snd_config_get_id(cfg, &id); ++ ++ tplg_dbg("\t%s:\n", id); ++ ++ if (strcmp(id, "playback") == 0) { ++ stream = SND_SOC_TPLG_STREAM_PLAYBACK; ++ pcm_dai->playback = 1; ++ } else if (strcmp(id, "capture") == 0) { ++ stream = SND_SOC_TPLG_STREAM_CAPTURE; ++ pcm_dai->capture = 1; ++ } else ++ return -EINVAL; ++ ++ snd_config_for_each(i, next, cfg) { ++ ++ n = snd_config_iterator_entry(i); ++ ++ /* get id */ ++ if (snd_config_get_id(n, &id) < 0) ++ continue; ++ ++ if (strcmp(id, "capabilities") == 0) { ++ if (snd_config_get_string(n, &value) < 0) ++ continue; ++ ++ elem_copy_text(pcm_dai->capconf[stream].caps.name, value, ++ SNDRV_CTL_ELEM_ID_NAME_MAXLEN); ++ ++ tplg_dbg("\t\t%s\n\t\t\t%s\n", id, value); ++ continue; ++ } ++ ++ if (strcmp(id, "configs") == 0) { ++ tplg_dbg("\t\tconfigs:\n"); ++ err = tplg_parse_compound(tplg, n, tplg_parse_pcm_cfg, ++ &pcm_dai->capconf[stream]); ++ if (err < 0) ++ return err; ++ continue; ++ } ++ } ++ ++ return 0; ++} ++ ++/* Parse pcm */ ++int tplg_parse_pcm(snd_tplg_t *tplg, ++ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED) ++{ ++ struct snd_soc_tplg_pcm_dai *pcm_dai; ++ struct tplg_elem *elem; ++ snd_config_iterator_t i, next; ++ snd_config_t *n; ++ const char *id, *val = NULL; ++ int err; ++ ++ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_PCM); ++ if (!elem) ++ return -ENOMEM; ++ ++ pcm_dai = elem->pcm; ++ pcm_dai->size = elem->size; ++ elem_copy_text(pcm_dai->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); ++ ++ tplg_dbg(" PCM: %s\n", elem->id); ++ ++ snd_config_for_each(i, next, cfg) { ++ ++ n = snd_config_iterator_entry(i); ++ if (snd_config_get_id(n, &id) < 0) ++ continue; ++ ++ /* skip comments */ ++ if (strcmp(id, "comment") == 0) ++ continue; ++ if (id[0] == '#') ++ continue; ++ ++ if (strcmp(id, "index") == 0) { ++ if (snd_config_get_string(n, &val) < 0) ++ return -EINVAL; ++ ++ elem->index = atoi(val); ++ tplg_dbg("\t%s: %d\n", id, elem->index); ++ continue; ++ } ++ ++ if (strcmp(id, "id") == 0) { ++ if (snd_config_get_string(n, &val) < 0) ++ return -EINVAL; ++ ++ pcm_dai->id = atoi(val); ++ tplg_dbg("\t%s: %d\n", id, pcm_dai->id); ++ continue; ++ } ++ ++ if (strcmp(id, "pcm") == 0) { ++ err = tplg_parse_compound(tplg, n, ++ tplg_parse_pcm_cap_cfg, elem); ++ if (err < 0) ++ return err; ++ continue; ++ } ++ } ++ ++ return 0; ++} ++ ++/* Parse be */ ++int tplg_parse_be(snd_tplg_t *tplg, ++ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED) ++{ ++ struct snd_soc_tplg_pcm_dai *pcm_dai; ++ struct tplg_elem *elem; ++ snd_config_iterator_t i, next; ++ snd_config_t *n; ++ const char *id, *val = NULL; ++ int err; ++ ++ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_BE); ++ if (!elem) ++ return -ENOMEM; ++ ++ pcm_dai = elem->be; ++ pcm_dai->size = elem->size; ++ elem_copy_text(pcm_dai->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); ++ ++ tplg_dbg(" BE: %s\n", elem->id); ++ ++ snd_config_for_each(i, next, cfg) { ++ ++ n = snd_config_iterator_entry(i); ++ if (snd_config_get_id(n, &id) < 0) ++ continue; ++ ++ /* skip comments */ ++ if (strcmp(id, "comment") == 0) ++ continue; ++ if (id[0] == '#') ++ continue; ++ ++ if (strcmp(id, "index") == 0) { ++ if (snd_config_get_string(n, &val) < 0) ++ return -EINVAL; ++ ++ elem->index = atoi(val); ++ tplg_dbg("\t%s: %d\n", id, elem->index); ++ continue; ++ } ++ ++ if (strcmp(id, "id") == 0) { ++ if (snd_config_get_string(n, &val) < 0) ++ return -EINVAL; ++ ++ pcm_dai->id = atoi(val); ++ tplg_dbg("\t%s: %d\n", id, pcm_dai->id); ++ continue; ++ } ++ ++ if (strcmp(id, "be") == 0) { ++ err = tplg_parse_compound(tplg, n, ++ tplg_parse_pcm_cap_cfg, elem); ++ if (err < 0) ++ return err; ++ continue; ++ } ++ } ++ ++ return 0; ++} ++ ++/* Parse cc */ ++int tplg_parse_cc(snd_tplg_t *tplg, ++ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED) ++{ ++ struct snd_soc_tplg_pcm_dai *pcm_dai; ++ struct tplg_elem *elem; ++ snd_config_iterator_t i, next; ++ snd_config_t *n; ++ const char *id, *val = NULL; ++ int err; ++ ++ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_CC); ++ if (!elem) ++ return -ENOMEM; ++ ++ pcm_dai = elem->cc; ++ pcm_dai->size = elem->size; ++ ++ tplg_dbg(" CC: %s\n", elem->id); ++ ++ snd_config_for_each(i, next, cfg) { ++ ++ n = snd_config_iterator_entry(i); ++ if (snd_config_get_id(n, &id) < 0) ++ continue; ++ ++ /* skip comments */ ++ if (strcmp(id, "comment") == 0) ++ continue; ++ if (id[0] == '#') ++ continue; ++ ++ if (strcmp(id, "index") == 0) { ++ if (snd_config_get_string(n, &val) < 0) ++ return -EINVAL; ++ ++ elem->index = atoi(val); ++ tplg_dbg("\t%s: %d\n", id, elem->index); ++ continue; ++ } ++ ++ if (strcmp(id, "id") == 0) { ++ if (snd_config_get_string(n, &val) < 0) ++ return -EINVAL; ++ ++ pcm_dai->id = atoi(val); ++ tplg_dbg("\t%s: %d\n", id, pcm_dai->id); ++ continue; ++ } ++ ++ if (strcmp(id, "cc") == 0) { ++ err = tplg_parse_compound(tplg, n, ++ tplg_parse_pcm_cap_cfg, elem); ++ if (err < 0) ++ return err; ++ continue; ++ } ++ } ++ ++ return 0; ++} +-- +2.5.0 + diff --git a/0039-topology-Add-operations-parser.patch b/0039-topology-Add-operations-parser.patch new file mode 100644 index 0000000..4eb238b --- /dev/null +++ b/0039-topology-Add-operations-parser.patch @@ -0,0 +1,107 @@ +From 353f1eddb608a837157342155fc061f85bf0a5f8 Mon Sep 17 00:00:00 2001 +From: Liam Girdwood +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 +Signed-off-by: Takashi Iwai +--- + 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 ++ Yao Jin ++ Liam Girdwood ++*/ ++ ++#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 + diff --git a/0040-topology-Add-private-data-parser.patch b/0040-topology-Add-private-data-parser.patch new file mode 100644 index 0000000..a4098c5 --- /dev/null +++ b/0040-topology-Add-private-data-parser.patch @@ -0,0 +1,420 @@ +From 5b379da2a0a1084349e918a52f471c03e37af703 Mon Sep 17 00:00:00 2001 +From: Liam Girdwood +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 +Signed-off-by: Takashi Iwai +--- + 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 ++ Yao Jin ++ Liam Girdwood ++*/ ++ ++#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 + diff --git a/0041-topology-Add-DAPM-object-parser.patch b/0041-topology-Add-DAPM-object-parser.patch new file mode 100644 index 0000000..da57cb7 --- /dev/null +++ b/0041-topology-Add-DAPM-object-parser.patch @@ -0,0 +1,585 @@ +From 01a0e1a1c2196967d2522092ca993098a7c66613 Mon Sep 17 00:00:00 2001 +From: Liam Girdwood +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 +Signed-off-by: Takashi Iwai +--- + 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 ++ Yao Jin ++ Liam Girdwood ++*/ ++ ++#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 + diff --git a/0042-topology-Add-CTL-parser.patch b/0042-topology-Add-CTL-parser.patch new file mode 100644 index 0000000..03c75a4 --- /dev/null +++ b/0042-topology-Add-CTL-parser.patch @@ -0,0 +1,636 @@ +From 694b857ce7b44a333c4f5e8b12f1b6cdf1c12388 Mon Sep 17 00:00:00 2001 +From: Liam Girdwood +Date: Wed, 29 Jul 2015 17:45:20 +0100 +Subject: [PATCH 42/49] topology: Add CTL parser + +Add support to parse mixers, enums and byte controls. + +Signed-off-by: Liam Girdwood +Signed-off-by: Takashi Iwai +--- + src/topology/ctl.c | 613 +++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 613 insertions(+) + create mode 100644 src/topology/ctl.c + +diff --git a/src/topology/ctl.c b/src/topology/ctl.c +new file mode 100644 +index 000000000000..9c1333c1fc88 +--- /dev/null ++++ b/src/topology/ctl.c +@@ -0,0 +1,613 @@ ++/* ++ Copyright(c) 2014-2015 Intel Corporation ++ All rights reserved. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of version 2 of the GNU General Public License as ++ published by the Free Software Foundation. ++ ++ This program is distributed in the hope that it will be useful, but ++ WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ General Public License for more details. ++ ++ Authors: Mengdong Lin ++ Yao Jin ++ Liam Girdwood ++*/ ++ ++#include "list.h" ++#include "tplg_local.h" ++ ++/* copy referenced TLV to the mixer control */ ++static int copy_tlv(struct tplg_elem *elem, struct tplg_elem *ref) ++{ ++ struct snd_soc_tplg_mixer_control *mixer_ctrl = elem->mixer_ctrl; ++ struct snd_soc_tplg_ctl_tlv *tlv = ref->tlv; ++ ++ tplg_dbg("TLV '%s' used by '%s\n", ref->id, elem->id); ++ ++ /* TLV has a fixed size */ ++ mixer_ctrl->tlv = *tlv; ++ ++ /* set size of TLV data */ ++ mixer_ctrl->hdr.tlv_size = tlv->count * sizeof(uint32_t); ++ return 0; ++} ++ ++/* check referenced TLV for a mixer control */ ++static int tplg_build_mixer_control(snd_tplg_t *tplg, ++ struct tplg_elem *elem) ++{ ++ struct tplg_ref *ref; ++ struct list_head *base, *pos; ++ int err = 0; ++ ++ base = &elem->ref_list; ++ ++ /* for each ref in this control elem */ ++ list_for_each(pos, base) { ++ ++ ref = list_entry(pos, struct tplg_ref, list); ++ if (ref->id == NULL || ref->elem) ++ continue; ++ ++ if (ref->type == OBJECT_TYPE_TLV) { ++ ref->elem = tplg_elem_lookup(&tplg->tlv_list, ++ ref->id, OBJECT_TYPE_TLV); ++ if (ref->elem) ++ err = copy_tlv(elem, ref->elem); ++ ++ } else if (ref->type == OBJECT_TYPE_DATA) { ++ ref->elem = tplg_elem_lookup(&tplg->pdata_list, ++ ref->id, OBJECT_TYPE_DATA); ++ err = tplg_copy_data(elem, ref->elem); ++ } ++ ++ if (!ref->elem) { ++ SNDERR("error: cannot find '%s' referenced by" ++ " control '%s'\n", ref->id, elem->id); ++ return -EINVAL; ++ } else if (err < 0) ++ return err; ++ } ++ ++ return 0; ++} ++ ++static void copy_enum_texts(struct tplg_elem *enum_elem, ++ struct tplg_elem *ref_elem) ++{ ++ struct snd_soc_tplg_enum_control *ec = enum_elem->enum_ctrl; ++ ++ memcpy(ec->texts, ref_elem->texts, ++ SND_SOC_TPLG_NUM_TEXTS * SNDRV_CTL_ELEM_ID_NAME_MAXLEN); ++} ++ ++/* check referenced text for a enum control */ ++static int tplg_build_enum_control(snd_tplg_t *tplg, ++ struct tplg_elem *elem) ++{ ++ struct tplg_ref *ref; ++ struct list_head *base, *pos; ++ int err = 0; ++ ++ base = &elem->ref_list; ++ ++ list_for_each(pos, base) { ++ ++ ref = list_entry(pos, struct tplg_ref, list); ++ if (ref->id == NULL || ref->elem) ++ continue; ++ ++ if (ref->type == OBJECT_TYPE_TEXT) { ++ ref->elem = tplg_elem_lookup(&tplg->text_list, ++ ref->id, OBJECT_TYPE_TEXT); ++ if (ref->elem) ++ copy_enum_texts(elem, ref->elem); ++ ++ } else if (ref->type == OBJECT_TYPE_DATA) { ++ ref->elem = tplg_elem_lookup(&tplg->pdata_list, ++ ref->id, OBJECT_TYPE_DATA); ++ err = tplg_copy_data(elem, ref->elem); ++ } ++ if (!ref->elem) { ++ SNDERR("error: cannot find '%s' referenced by" ++ " control '%s'\n", ref->id, elem->id); ++ return -EINVAL; ++ } else if (err < 0) ++ return err; ++ } ++ ++ return 0; ++} ++ ++/* check referenced private data for a byte control */ ++static int tplg_build_bytes_control(snd_tplg_t *tplg, struct tplg_elem *elem) ++{ ++ struct tplg_ref *ref; ++ struct list_head *base, *pos; ++ ++ base = &elem->ref_list; ++ ++ list_for_each(pos, base) { ++ ++ ref = list_entry(pos, struct tplg_ref, list); ++ if (ref->id == NULL || ref->elem) ++ continue; ++ ++ /* bytes control only reference one private data section */ ++ ref->elem = tplg_elem_lookup(&tplg->pdata_list, ++ ref->id, OBJECT_TYPE_DATA); ++ if (!ref->elem) { ++ SNDERR("error: cannot find data '%s'" ++ " referenced by control '%s'\n", ++ ref->id, elem->id); ++ return -EINVAL; ++ } ++ ++ /* copy texts to enum elem */ ++ return tplg_copy_data(elem, ref->elem); ++ } ++ ++ return 0; ++} ++ ++int tplg_build_controls(snd_tplg_t *tplg) ++{ ++ struct list_head *base, *pos; ++ struct tplg_elem *elem; ++ int err = 0; ++ ++ base = &tplg->mixer_list; ++ list_for_each(pos, base) { ++ ++ elem = list_entry(pos, struct tplg_elem, list); ++ err = tplg_build_mixer_control(tplg, elem); ++ if (err < 0) ++ return err; ++ ++ /* add control to manifest */ ++ tplg->manifest.control_elems++; ++ } ++ ++ base = &tplg->enum_list; ++ list_for_each(pos, base) { ++ ++ elem = list_entry(pos, struct tplg_elem, list); ++ err = tplg_build_enum_control(tplg, elem); ++ if (err < 0) ++ return err; ++ ++ /* add control to manifest */ ++ tplg->manifest.control_elems++; ++ } ++ ++ base = &tplg->bytes_ext_list; ++ list_for_each(pos, base) { ++ ++ elem = list_entry(pos, struct tplg_elem, list); ++ err = tplg_build_bytes_control(tplg, elem); ++ if (err < 0) ++ return err; ++ ++ /* add control to manifest */ ++ tplg->manifest.control_elems++; ++ } ++ ++ return 0; ++} ++ ++ ++/* ++ * Parse TLV of DBScale type. ++ * ++ * Parse DBScale describing min, step, mute in DB. ++ */ ++static int tplg_parse_tlv_dbscale(snd_config_t *cfg, struct tplg_elem *elem) ++{ ++ snd_config_iterator_t i, next; ++ snd_config_t *n; ++ struct snd_soc_tplg_ctl_tlv *tplg_tlv; ++ const char *id = NULL, *value = NULL; ++ int *data; ++ ++ tplg_dbg(" scale: %s\n", elem->id); ++ ++ tplg_tlv = calloc(1, sizeof(*tplg_tlv)); ++ if (!tplg_tlv) ++ return -ENOMEM; ++ data = (int*)(tplg_tlv->data); ++ ++ elem->tlv = tplg_tlv; ++ tplg_tlv->numid = SNDRV_CTL_TLVT_DB_SCALE; ++ tplg_tlv->count = 8; ++ tplg_tlv->size = sizeof(*tplg_tlv); ++ ++ snd_config_for_each(i, next, cfg) { ++ ++ n = snd_config_iterator_entry(i); ++ ++ /* get ID */ ++ if (snd_config_get_id(n, &id) < 0) { ++ SNDERR("error: cant get ID\n"); ++ return -EINVAL; ++ } ++ ++ /* get value */ ++ if (snd_config_get_string(n, &value) < 0) ++ continue; ++ ++ tplg_dbg("\t%s = %s\n", id, value); ++ ++ /* get TLV data */ ++ if (strcmp(id, "min") == 0) ++ data[0] = atoi(value); ++ else if (strcmp(id, "step") == 0) ++ data[1] = atoi(value); ++ else if (strcmp(id, "mute") == 0) ++ data[2] = atoi(value); ++ else ++ SNDERR("error: unknown key %s\n", id); ++ } ++ ++ return 0; ++} ++ ++/* Parse TLV */ ++int tplg_parse_tlv(snd_tplg_t *tplg, snd_config_t *cfg, ++ void *private ATTRIBUTE_UNUSED) ++{ ++ snd_config_iterator_t i, next; ++ snd_config_t *n; ++ const char *id; ++ int err = 0; ++ struct tplg_elem *elem; ++ ++ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_TLV); ++ if (!elem) ++ return -ENOMEM; ++ ++ snd_config_for_each(i, next, cfg) { ++ ++ n = snd_config_iterator_entry(i); ++ if (snd_config_get_id(n, &id) < 0) ++ continue; ++ ++ if (strcmp(id, "scale") == 0) { ++ err = tplg_parse_tlv_dbscale(n, elem); ++ if (err < 0) { ++ SNDERR("error: failed to DBScale"); ++ return err; ++ } ++ continue; ++ } ++ } ++ ++ return err; ++} ++ ++/* Parse Control Bytes */ ++int tplg_parse_control_bytes(snd_tplg_t *tplg, ++ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED) ++{ ++ struct snd_soc_tplg_bytes_control *be; ++ struct tplg_elem *elem; ++ snd_config_iterator_t i, next; ++ snd_config_t *n; ++ const char *id, *val = NULL; ++ int err; ++ ++ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_BYTES); ++ if (!elem) ++ return -ENOMEM; ++ ++ be = elem->bytes_ext; ++ be->size = elem->size; ++ elem_copy_text(be->hdr.name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); ++ be->hdr.type = SND_SOC_TPLG_TYPE_BYTES; ++ ++ tplg_dbg(" Control Bytes: %s\n", elem->id); ++ ++ snd_config_for_each(i, next, cfg) { ++ n = snd_config_iterator_entry(i); ++ if (snd_config_get_id(n, &id) < 0) ++ continue; ++ ++ /* skip comments */ ++ if (strcmp(id, "comment") == 0) ++ continue; ++ if (id[0] == '#') ++ continue; ++ ++ if (strcmp(id, "index") == 0) { ++ if (snd_config_get_string(n, &val) < 0) ++ return -EINVAL; ++ ++ elem->index = atoi(val); ++ tplg_dbg("\t%s: %d\n", id, elem->index); ++ continue; ++ } ++ ++ if (strcmp(id, "base") == 0) { ++ if (snd_config_get_string(n, &val) < 0) ++ return -EINVAL; ++ ++ be->base = atoi(val); ++ tplg_dbg("\t%s: %d\n", id, be->base); ++ continue; ++ } ++ ++ if (strcmp(id, "num_regs") == 0) { ++ if (snd_config_get_string(n, &val) < 0) ++ return -EINVAL; ++ ++ be->num_regs = atoi(val); ++ tplg_dbg("\t%s: %d\n", id, be->num_regs); ++ continue; ++ } ++ ++ if (strcmp(id, "max") == 0) { ++ if (snd_config_get_string(n, &val) < 0) ++ return -EINVAL; ++ ++ be->max = atoi(val); ++ tplg_dbg("\t%s: %d\n", id, be->num_regs); ++ continue; ++ } ++ ++ if (strcmp(id, "mask") == 0) { ++ if (snd_config_get_string(n, &val) < 0) ++ return -EINVAL; ++ ++ be->mask = strtol(val, NULL, 16); ++ tplg_dbg("\t%s: %d\n", id, be->mask); ++ continue; ++ } ++ ++ if (strcmp(id, "data") == 0) { ++ if (snd_config_get_string(n, &val) < 0) ++ return -EINVAL; ++ ++ tplg_ref_add(elem, OBJECT_TYPE_DATA, val); ++ tplg_dbg("\t%s: %s\n", id, val); ++ continue; ++ } ++ ++ if (strcmp(id, "tlv") == 0) { ++ if (snd_config_get_string(n, &val) < 0) ++ return -EINVAL; ++ ++ err = tplg_ref_add(elem, OBJECT_TYPE_TLV, val); ++ if (err < 0) ++ return err; ++ ++ be->hdr.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | ++ SNDRV_CTL_ELEM_ACCESS_READWRITE; ++ tplg_dbg("\t%s: %s\n", id, val); ++ continue; ++ } ++ } ++ ++ return 0; ++} ++ ++/* Parse Control Enums. */ ++int tplg_parse_control_enum(snd_tplg_t *tplg, snd_config_t *cfg, ++ void *private ATTRIBUTE_UNUSED) ++{ ++ struct snd_soc_tplg_enum_control *ec; ++ struct tplg_elem *elem; ++ snd_config_iterator_t i, next; ++ snd_config_t *n; ++ const char *id, *val = NULL; ++ int err, j; ++ ++ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_ENUM); ++ if (!elem) ++ return -ENOMEM; ++ ++ /* init new mixer */ ++ ec = elem->enum_ctrl; ++ elem_copy_text(ec->hdr.name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); ++ ec->hdr.type = SND_SOC_TPLG_TYPE_ENUM; ++ ec->size = elem->size; ++ tplg->channel_idx = 0; ++ ++ /* set channel reg to default state */ ++ for (j = 0; j < SND_SOC_TPLG_MAX_CHAN; j++) ++ ec->channel[j].reg = -1; ++ ++ tplg_dbg(" Control Enum: %s\n", elem->id); ++ ++ snd_config_for_each(i, next, cfg) { ++ ++ n = snd_config_iterator_entry(i); ++ if (snd_config_get_id(n, &id) < 0) ++ continue; ++ ++ /* skip comments */ ++ if (strcmp(id, "comment") == 0) ++ continue; ++ if (id[0] == '#') ++ continue; ++ ++ if (strcmp(id, "index") == 0) { ++ if (snd_config_get_string(n, &val) < 0) ++ return -EINVAL; ++ ++ elem->index = atoi(val); ++ tplg_dbg("\t%s: %d\n", id, elem->index); ++ continue; ++ } ++ ++ if (strcmp(id, "texts") == 0) { ++ if (snd_config_get_string(n, &val) < 0) ++ return -EINVAL; ++ ++ tplg_ref_add(elem, OBJECT_TYPE_TEXT, val); ++ tplg_dbg("\t%s: %s\n", id, val); ++ continue; ++ } ++ ++ if (strcmp(id, "channel") == 0) { ++ if (ec->num_channels >= SND_SOC_TPLG_MAX_CHAN) { ++ SNDERR("error: too many channels %s\n", ++ elem->id); ++ return -EINVAL; ++ } ++ ++ err = tplg_parse_compound(tplg, n, tplg_parse_channel, ++ ec->channel); ++ if (err < 0) ++ return err; ++ ++ ec->num_channels = tplg->channel_idx; ++ continue; ++ } ++ ++ if (strcmp(id, "ops") == 0) { ++ err = tplg_parse_compound(tplg, n, tplg_parse_ops, ++ &ec->hdr); ++ if (err < 0) ++ return err; ++ continue; ++ } ++ ++ if (strcmp(id, "data") == 0) { ++ if (snd_config_get_string(n, &val) < 0) ++ return -EINVAL; ++ ++ tplg_ref_add(elem, OBJECT_TYPE_DATA, val); ++ tplg_dbg("\t%s: %s\n", id, val); ++ continue; ++ } ++ } ++ ++ return 0; ++} ++ ++/* Parse Controls. ++ * ++ * Mixer control. Supports multiple channels. ++ */ ++int tplg_parse_control_mixer(snd_tplg_t *tplg, ++ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED) ++{ ++ struct snd_soc_tplg_mixer_control *mc; ++ struct tplg_elem *elem; ++ snd_config_iterator_t i, next; ++ snd_config_t *n; ++ const char *id, *val = NULL; ++ int err, j; ++ ++ elem = tplg_elem_new_common(tplg, cfg, OBJECT_TYPE_MIXER); ++ if (!elem) ++ return -ENOMEM; ++ ++ /* init new mixer */ ++ mc = elem->mixer_ctrl; ++ elem_copy_text(mc->hdr.name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); ++ mc->hdr.type = SND_SOC_TPLG_TYPE_MIXER; ++ mc->size = elem->size; ++ tplg->channel_idx = 0; ++ ++ /* set channel reg to default state */ ++ for (j = 0; j < SND_SOC_TPLG_MAX_CHAN; j++) ++ mc->channel[j].reg = -1; ++ ++ tplg_dbg(" Control Mixer: %s\n", elem->id); ++ ++ /* giterate trough each mixer elment */ ++ snd_config_for_each(i, next, cfg) { ++ n = snd_config_iterator_entry(i); ++ if (snd_config_get_id(n, &id) < 0) ++ continue; ++ ++ /* skip comments */ ++ if (strcmp(id, "comment") == 0) ++ continue; ++ if (id[0] == '#') ++ continue; ++ ++ if (strcmp(id, "index") == 0) { ++ if (snd_config_get_string(n, &val) < 0) ++ return -EINVAL; ++ ++ elem->index = atoi(val); ++ tplg_dbg("\t%s: %d\n", id, elem->index); ++ continue; ++ } ++ ++ if (strcmp(id, "channel") == 0) { ++ if (mc->num_channels >= SND_SOC_TPLG_MAX_CHAN) { ++ SNDERR("error: too many channels %s\n", ++ elem->id); ++ return -EINVAL; ++ } ++ ++ err = tplg_parse_compound(tplg, n, tplg_parse_channel, ++ mc->channel); ++ if (err < 0) ++ return err; ++ ++ mc->num_channels = tplg->channel_idx; ++ continue; ++ } ++ ++ if (strcmp(id, "max") == 0) { ++ if (snd_config_get_string(n, &val) < 0) ++ return -EINVAL; ++ ++ mc->max = atoi(val); ++ tplg_dbg("\t%s: %d\n", id, mc->max); ++ continue; ++ } ++ ++ if (strcmp(id, "invert") == 0) { ++ if (snd_config_get_string(n, &val) < 0) ++ return -EINVAL; ++ ++ if (strcmp(val, "true") == 0) ++ mc->invert = 1; ++ else if (strcmp(val, "false") == 0) ++ mc->invert = 0; ++ ++ tplg_dbg("\t%s: %d\n", id, mc->invert); ++ continue; ++ } ++ ++ if (strcmp(id, "ops") == 0) { ++ err = tplg_parse_compound(tplg, n, tplg_parse_ops, ++ &mc->hdr); ++ if (err < 0) ++ return err; ++ continue; ++ } ++ ++ if (strcmp(id, "tlv") == 0) { ++ if (snd_config_get_string(n, &val) < 0) ++ return -EINVAL; ++ ++ err = tplg_ref_add(elem, OBJECT_TYPE_TLV, val); ++ if (err < 0) ++ return err; ++ ++ mc->hdr.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | ++ SNDRV_CTL_ELEM_ACCESS_READWRITE; ++ tplg_dbg("\t%s: %s\n", id, val); ++ continue; ++ } ++ ++ if (strcmp(id, "data") == 0) { ++ if (snd_config_get_string(n, &val) < 0) ++ return -EINVAL; ++ ++ tplg_ref_add(elem, OBJECT_TYPE_DATA, val); ++ tplg_dbg("\t%s: %s\n", id, val); ++ continue; ++ } ++ } ++ ++ return 0; ++} +-- +2.5.0 + diff --git a/0043-topology-Add-Channel-map-parser.patch b/0043-topology-Add-Channel-map-parser.patch new file mode 100644 index 0000000..ac04d59 --- /dev/null +++ b/0043-topology-Add-Channel-map-parser.patch @@ -0,0 +1,145 @@ +From 9764a4b891737e6b4363c09b5e5ce8384acecc11 Mon Sep 17 00:00:00 2001 +From: Liam Girdwood +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 +Signed-off-by: Takashi Iwai +--- + 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 ++ Yao Jin ++ Liam Girdwood ++*/ ++ ++#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 + diff --git a/0044-topology-Add-binary-file-builder.patch b/0044-topology-Add-binary-file-builder.patch new file mode 100644 index 0000000..6df02f0 --- /dev/null +++ b/0044-topology-Add-binary-file-builder.patch @@ -0,0 +1,350 @@ +From 1d1dff56767842a99d2e06a6997079c0516dec68 Mon Sep 17 00:00:00 2001 +From: Liam Girdwood +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 +Signed-off-by: Takashi Iwai +--- + 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 ++ Yao Jin ++ Liam Girdwood ++*/ ++ ++#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 + diff --git a/0045-topology-autotools-Add-build-support-for-topology-co.patch b/0045-topology-autotools-Add-build-support-for-topology-co.patch new file mode 100644 index 0000000..6291ac3 --- /dev/null +++ b/0045-topology-autotools-Add-build-support-for-topology-co.patch @@ -0,0 +1,125 @@ +From fec1e8f25374ec8eb4d57ee43e94e9689a748678 Mon Sep 17 00:00:00 2001 +From: Liam Girdwood +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 +Signed-off-by: Takashi Iwai +--- + 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 + diff --git a/0046-topology-doxygen-Add-doxygen-support-for-topology-co.patch b/0046-topology-doxygen-Add-doxygen-support-for-topology-co.patch new file mode 100644 index 0000000..3b7830f --- /dev/null +++ b/0046-topology-doxygen-Add-doxygen-support-for-topology-co.patch @@ -0,0 +1,60 @@ +From 22603237b09ed50744030f550248ade135d4f73b Mon Sep 17 00:00:00 2001 +From: Liam Girdwood +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 +Signed-off-by: Takashi Iwai +--- + 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.

    +
  • Page \ref timer explains the design of the Timer API. +
  • Page \ref seq explains the design of the Sequencer API. +
  • Page \ref ucm explains the use case API. ++
  • Page \ref topology explains the DSP topology API. + + +

    Configuration

    +-- +2.5.0 + diff --git a/0047-conf-topology-Add-topology-file-for-broadwell-audio-.patch b/0047-conf-topology-Add-topology-file-for-broadwell-audio-.patch new file mode 100644 index 0000000..42758e3 --- /dev/null +++ b/0047-conf-topology-Add-topology-file-for-broadwell-audio-.patch @@ -0,0 +1,443 @@ +From 00a51b5bacb0f966d0e323bd9d3057c0eb0e6f23 Mon Sep 17 00:00:00 2001 +From: Liam Girdwood +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 +Signed-off-by: Takashi Iwai +--- + 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 + diff --git a/0048-topology-Fix-missing-inclusion-of-ctype.h.patch b/0048-topology-Fix-missing-inclusion-of-ctype.h.patch new file mode 100644 index 0000000..cd5f832 --- /dev/null +++ b/0048-topology-Fix-missing-inclusion-of-ctype.h.patch @@ -0,0 +1,28 @@ +From 907e464593a2acf51c2e2be4c3d4e098efdd95ff Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +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 +--- + 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 + + /* Get Private data from a file. */ + static int tplg_parse_data_file(snd_config_t *cfg, struct tplg_elem *elem) +-- +2.5.0 + diff --git a/0049-topology-Fix-typos.patch b/0049-topology-Fix-typos.patch new file mode 100644 index 0000000..2744878 --- /dev/null +++ b/0049-topology-Fix-typos.patch @@ -0,0 +1,96 @@ +From 66ce9f9a1177de3b8e8304323b4d3a16d78ead32 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Thu, 30 Jul 2015 16:43:19 +0200 +Subject: [PATCH 49/49] topology: Fix typos + +Signed-off-by: Takashi Iwai +--- + 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" + * }; + * +- * 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.
    + * +- * 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 :- + * + *
    +@@ -306,7 +306,7 @@ extern "C" {
    +  * graph with other graphs, it's not used by the kernel atm.
    +  *
    +  * 

    DAPM Widgets

    +- * 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 + * } + *
    + * 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. + * + *

    PCM Configurations

    +@@ -400,7 +400,7 @@ extern "C" { + * + * + * 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. + * + *

    PCM Configurations

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