Hello community,

here is the log from the commit of package pulseaudio for openSUSE:Factory 
checked in at 2020-02-21 16:40:33
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/pulseaudio (Old)
 and      /work/SRC/openSUSE:Factory/.pulseaudio.new.26092 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "pulseaudio"

Fri Feb 21 16:40:33 2020 rev:167 rq:774846 version:13.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/pulseaudio/pulseaudio.changes    2020-02-09 
21:01:46.903331907 +0100
+++ /work/SRC/openSUSE:Factory/.pulseaudio.new.26092/pulseaudio.changes 
2020-02-21 16:41:18.261906731 +0100
@@ -1,0 +2,38 @@
+Wed Feb 12 22:01:05 CET 2020 - [email protected]
+
+- Backport upstream fixes / enhancements about alsa modules:
+  mainly for UCM support (boo#1160914):
+  0001-alsa-mixer-path-test-Hide-unused-functions-when-buil.patch
+  0002-alsa-mixer-recognize-the-Speaker-Jack-control.patch
+  0003-alsa-mixer-add-support-for-SteelSeries-Arctis-Pro-20.patch
+  0004-alsa-mixer-Add-support-for-SteelSeries-Arctis-5-2019.patch
+  0005-alsa-mixer-add-support-for-LucidSound-LS31-and-creat.patch
+  0006-alsa-ucm-use-ucm2-name-for-the-direct-card-index-ope.patch
+  0007-alsa-ucm-add-mixer-IDs-to-ucm_items.patch
+  0008-alsa-mixer-handle-the-index-for-ALSA-mixer-element-i.patch
+  0009-alsa-mixer-improve-alsa_id_decode-function.patch
+  0010-alsa-ucm-Support-Playback-CaptureVolume.patch
+  0011-alsa-ucm-Fix-volume-control-based-on-review.patch
+  0012-alsa-ucm-use-the-correct-mixer-identifiers-as-first.patch
+  0013-alsa-ucm-add-support-for-master-volume.patch
+  0014-alsa-ucm-split-correctly-JackHWMute-device-names.patch
+  0015-alsa-ucm-fix-parsing-for-JackControl.patch
+  0016-alsa-ucm-add-comments-to-ucm_get_mixer_id.patch
+  0017-alsa-ucm-validate-access-to-PA_DEVICE_PORT_DATA.patch
+  0018-alsa-Skip-resume-PCM-if-hardware-doesn-t-support-it.patch
+  0019-alsa-ucm-parse-correctly-the-device-values.patch
+  0020-alsa-ucm-do-not-try-to-use-UCM-device-name-as-jack-n.patch
+  0021-alsa-util-do-not-try-to-guess-the-mixer-name-from-th.patch
+  0022-alsa-ucm-add-control-and-mixer-device-items.patch
+  0023-alsa-ucm-get-the-mixer-names-from-ucm-don-t-guess.patch
+  0024-alsa-ucm-use-the-proper-mixer-name-for-ucm-pcm-sink-.patch
+  0025-alsa-mixer-handle-interface-type-CARD-PCM-for-mixer-.patch
+  0026-alsa-mixer-Add-the-ability-to-pass-the-intended-role.patch
+  0027-alsa-mixer-Set-the-intended-role-of-Steelseries-Arct.patch
+  0028-alsa-rewrite-mixer-open-close-cache-mixer-accesses-i.patch
+  0029-alsa-ucm-add-support-for-HDMI-ELD.patch
+  0030-alsa-mixer-do-the-quick-card-number-lookup-to-save-m.patch
+  0031-alsa-mixer-improve-check-for-the-empty-path-set-for-.patch
+  0032-alsa-ucm-allow-to-set-profile-priority-from-UCM-valu.patch
+
+-------------------------------------------------------------------

New:
----
  0001-alsa-mixer-path-test-Hide-unused-functions-when-buil.patch
  0002-alsa-mixer-recognize-the-Speaker-Jack-control.patch
  0003-alsa-mixer-add-support-for-SteelSeries-Arctis-Pro-20.patch
  0004-alsa-mixer-Add-support-for-SteelSeries-Arctis-5-2019.patch
  0005-alsa-mixer-add-support-for-LucidSound-LS31-and-creat.patch
  0006-alsa-ucm-use-ucm2-name-for-the-direct-card-index-ope.patch
  0007-alsa-ucm-add-mixer-IDs-to-ucm_items.patch
  0008-alsa-mixer-handle-the-index-for-ALSA-mixer-element-i.patch
  0009-alsa-mixer-improve-alsa_id_decode-function.patch
  0010-alsa-ucm-Support-Playback-CaptureVolume.patch
  0011-alsa-ucm-Fix-volume-control-based-on-review.patch
  0012-alsa-ucm-use-the-correct-mixer-identifiers-as-first.patch
  0013-alsa-ucm-add-support-for-master-volume.patch
  0014-alsa-ucm-split-correctly-JackHWMute-device-names.patch
  0015-alsa-ucm-fix-parsing-for-JackControl.patch
  0016-alsa-ucm-add-comments-to-ucm_get_mixer_id.patch
  0017-alsa-ucm-validate-access-to-PA_DEVICE_PORT_DATA.patch
  0018-alsa-Skip-resume-PCM-if-hardware-doesn-t-support-it.patch
  0019-alsa-ucm-parse-correctly-the-device-values.patch
  0020-alsa-ucm-do-not-try-to-use-UCM-device-name-as-jack-n.patch
  0021-alsa-util-do-not-try-to-guess-the-mixer-name-from-th.patch
  0022-alsa-ucm-add-control-and-mixer-device-items.patch
  0023-alsa-ucm-get-the-mixer-names-from-ucm-don-t-guess.patch
  0024-alsa-ucm-use-the-proper-mixer-name-for-ucm-pcm-sink-.patch
  0025-alsa-mixer-handle-interface-type-CARD-PCM-for-mixer-.patch
  0026-alsa-mixer-Add-the-ability-to-pass-the-intended-role.patch
  0027-alsa-mixer-Set-the-intended-role-of-Steelseries-Arct.patch
  0028-alsa-rewrite-mixer-open-close-cache-mixer-accesses-i.patch
  0029-alsa-ucm-add-support-for-HDMI-ELD.patch
  0030-alsa-mixer-do-the-quick-card-number-lookup-to-save-m.patch
  0031-alsa-mixer-improve-check-for-the-empty-path-set-for-.patch
  0032-alsa-ucm-allow-to-set-profile-priority-from-UCM-valu.patch

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ pulseaudio.spec ++++++
--- /var/tmp/diff_new_pack.vpuNfm/_old  2020-02-21 16:41:19.073908353 +0100
+++ /var/tmp/diff_new_pack.vpuNfm/_new  2020-02-21 16:41:19.081908369 +0100
@@ -50,6 +50,39 @@
 Patch5:         qpaeq-shebang.patch
 # PATCH-FIX-OPENSUSE Workaround for old systemd on Leap 15.x
 Patch6:         pulseaudio-old-systemd-workaround.patch
+# PATCH-FIX-UPSTREAM
+Patch1001:      0001-alsa-mixer-path-test-Hide-unused-functions-when-buil.patch
+Patch1002:      0002-alsa-mixer-recognize-the-Speaker-Jack-control.patch
+Patch1003:      0003-alsa-mixer-add-support-for-SteelSeries-Arctis-Pro-20.patch
+Patch1004:      0004-alsa-mixer-Add-support-for-SteelSeries-Arctis-5-2019.patch
+Patch1005:      0005-alsa-mixer-add-support-for-LucidSound-LS31-and-creat.patch
+Patch1006:      0006-alsa-ucm-use-ucm2-name-for-the-direct-card-index-ope.patch
+Patch1007:      0007-alsa-ucm-add-mixer-IDs-to-ucm_items.patch
+Patch1008:      0008-alsa-mixer-handle-the-index-for-ALSA-mixer-element-i.patch
+Patch1009:      0009-alsa-mixer-improve-alsa_id_decode-function.patch
+Patch1010:      0010-alsa-ucm-Support-Playback-CaptureVolume.patch
+Patch1011:      0011-alsa-ucm-Fix-volume-control-based-on-review.patch
+Patch1012:      0012-alsa-ucm-use-the-correct-mixer-identifiers-as-first.patch
+Patch1013:      0013-alsa-ucm-add-support-for-master-volume.patch
+Patch1014:      0014-alsa-ucm-split-correctly-JackHWMute-device-names.patch
+Patch1015:      0015-alsa-ucm-fix-parsing-for-JackControl.patch
+Patch1016:      0016-alsa-ucm-add-comments-to-ucm_get_mixer_id.patch
+Patch1017:      0017-alsa-ucm-validate-access-to-PA_DEVICE_PORT_DATA.patch
+Patch1018:      0018-alsa-Skip-resume-PCM-if-hardware-doesn-t-support-it.patch
+Patch1019:      0019-alsa-ucm-parse-correctly-the-device-values.patch
+Patch1020:      0020-alsa-ucm-do-not-try-to-use-UCM-device-name-as-jack-n.patch
+Patch1021:      0021-alsa-util-do-not-try-to-guess-the-mixer-name-from-th.patch
+Patch1022:      0022-alsa-ucm-add-control-and-mixer-device-items.patch
+Patch1023:      0023-alsa-ucm-get-the-mixer-names-from-ucm-don-t-guess.patch
+Patch1024:      0024-alsa-ucm-use-the-proper-mixer-name-for-ucm-pcm-sink-.patch
+Patch1025:      0025-alsa-mixer-handle-interface-type-CARD-PCM-for-mixer-.patch
+Patch1026:      0026-alsa-mixer-Add-the-ability-to-pass-the-intended-role.patch
+Patch1027:      0027-alsa-mixer-Set-the-intended-role-of-Steelseries-Arct.patch
+Patch1028:      0028-alsa-rewrite-mixer-open-close-cache-mixer-accesses-i.patch
+Patch1029:      0029-alsa-ucm-add-support-for-HDMI-ELD.patch
+Patch1030:      0030-alsa-mixer-do-the-quick-card-number-lookup-to-save-m.patch
+Patch1031:      0031-alsa-mixer-improve-check-for-the-empty-path-set-for-.patch
+Patch1032:      0032-alsa-ucm-allow-to-set-profile-priority-from-UCM-valu.patch
 BuildRequires:  alsa-devel >= 1.0.19
 BuildRequires:  bluez-devel >= 5
 BuildRequires:  doxygen
@@ -338,6 +371,38 @@
 %prep
 %setup -q -T -b0
 %patch0
+%patch1001 -p1
+%patch1002 -p1
+%patch1003 -p1
+%patch1004 -p1
+%patch1005 -p1
+%patch1006 -p1
+%patch1007 -p1
+%patch1008 -p1
+%patch1009 -p1
+%patch1010 -p1
+%patch1011 -p1
+%patch1012 -p1
+%patch1013 -p1
+%patch1014 -p1
+%patch1015 -p1
+%patch1016 -p1
+%patch1017 -p1
+%patch1018 -p1
+%patch1019 -p1
+%patch1020 -p1
+%patch1021 -p1
+%patch1022 -p1
+%patch1023 -p1
+%patch1024 -p1
+%patch1025 -p1
+%patch1026 -p1
+%patch1027 -p1
+%patch1028 -p1
+%patch1029 -p1
+%patch1030 -p1
+%patch1031 -p1
+%patch1032 -p1
 %patch1 -p1
 %patch2
 %patch5

++++++ 0001-alsa-mixer-path-test-Hide-unused-functions-when-buil.patch ++++++
>From 3dff31e19ca627fc4e0a8f13aeb44923118ecfa1 Mon Sep 17 00:00:00 2001
From: Tanu Kaskinen <[email protected]>
Date: Fri, 20 Sep 2019 17:09:40 +0300
Subject: [PATCH] alsa-mixer-path-test: Hide unused functions when building
 with Meson
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Silences these warnings:

[509/574] Compiling C object 
'src/tests/a4ccf2d@@alsa-mixer-path-test@exe/alsa-mixer-path-test.c.o'.
../src/tests/alsa-mixer-path-test.c:24:20: warning: ‘load_makefile’ defined but 
not used [-Wunused-function]
 static pa_strlist *load_makefile() {
                    ^~~~~~~~~~~~~
../src/tests/alsa-mixer-path-test.c:17:20: warning: ‘get_default_paths_dir’ 
defined but not used [-Wunused-function]
 static const char *get_default_paths_dir(void) {
                    ^~~~~~~~~~~~~~~~~~~~~
---
 src/tests/alsa-mixer-path-test.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/src/tests/alsa-mixer-path-test.c b/src/tests/alsa-mixer-path-test.c
index ee40587b7b13..75cf086138cc 100644
--- a/src/tests/alsa-mixer-path-test.c
+++ b/src/tests/alsa-mixer-path-test.c
@@ -13,6 +13,10 @@
 #include <pulsecore/strlist.h>
 #include <modules/alsa/alsa-mixer.h>
 
+/* This test inspects the Makefile, so this is not applicable when using
+ * Meson. */
+#ifndef MESON_BUILD
+
 /* This function was copied from alsa-mixer.c */
 static const char *get_default_paths_dir(void) {
     if (pa_run_from_build_tree())
@@ -52,6 +56,7 @@ static pa_strlist *load_makefile() {
     fclose(f);
     return result;
 }
+#endif /* end of #ifndef MESON_BUILD */
 
 START_TEST (mixer_path_test) {
 #ifdef MESON_BUILD
-- 
2.16.4

++++++ 0002-alsa-mixer-recognize-the-Speaker-Jack-control.patch ++++++
>From 248a77c7fd2f9682c58d3d3dd6f3c2ba3ad2c111 Mon Sep 17 00:00:00 2001
From: Tanu Kaskinen <[email protected]>
Date: Sat, 2 Sep 2017 15:35:01 +0300
Subject: [PATCH] alsa-mixer: recognize the "Speaker Jack" control

This control has been seen in the wild:
https://lists.freedesktop.org/archives/pulseaudio-discuss/2017-August/028595.html
(The pastebin link in that mail might not work anymore, but the paste
just shows that there's a Speaker Jack control).
---
 src/modules/alsa/mixer/paths/analog-output-speaker.conf | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/modules/alsa/mixer/paths/analog-output-speaker.conf 
b/src/modules/alsa/mixer/paths/analog-output-speaker.conf
index 9f4dac414134..6f9968e1f537 100644
--- a/src/modules/alsa/mixer/paths/analog-output-speaker.conf
+++ b/src/modules/alsa/mixer/paths/analog-output-speaker.conf
@@ -56,6 +56,9 @@ state.unplugged = unknown
 state.plugged = no
 state.unplugged = unknown
 
+[Jack Speaker]
+required-any = any
+
 [Jack Speaker Phantom]
 required-any = any
 state.plugged = unknown
-- 
2.16.4

++++++ 0003-alsa-mixer-add-support-for-SteelSeries-Arctis-Pro-20.patch ++++++
>From 7259e8c22fb97b1ae80ac4909713e51b293afc4a Mon Sep 17 00:00:00 2001
From: Josh <[email protected]>
Date: Sat, 25 May 2019 02:35:01 -0700
Subject: [PATCH] alsa-mixer: add support for SteelSeries Arctis Pro 2019
 headset

Signed-off-by: Dave Chiluk <[email protected]>
---
 src/Makefile.am                                                |  6 +++---
 src/modules/alsa/90-pulseaudio.rules                           | 10 +++++++---
 ...ut-chat.conf => steelseries-arctis-output-chat-common.conf} |  0
 ...ut-game.conf => steelseries-arctis-output-game-common.conf} |  0
 ...usb-audio.conf => steelseries-arctis-common-usb-audio.conf} |  4 ++--
 5 files changed, 12 insertions(+), 8 deletions(-)
 rename src/modules/alsa/mixer/paths/{steelseries-arctis-5-output-chat.conf => 
steelseries-arctis-output-chat-common.conf} (100%)
 rename src/modules/alsa/mixer/paths/{steelseries-arctis-5-output-game.conf => 
steelseries-arctis-output-game-common.conf} (100%)
 rename 
src/modules/alsa/mixer/profile-sets/{steelseries-arctis-5-usb-audio.conf => 
steelseries-arctis-common-usb-audio.conf} (80%)

diff --git a/src/Makefile.am b/src/Makefile.am
index e3baf587a8a3..3db921a07e1b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1353,7 +1353,7 @@ dist_alsaprofilesets_DATA = \
                
modules/alsa/mixer/profile-sets/native-instruments-korecontroller.conf \
                modules/alsa/mixer/profile-sets/kinect-audio.conf \
                modules/alsa/mixer/profile-sets/sb-omni-surround-5.1.conf \
-               
modules/alsa/mixer/profile-sets/steelseries-arctis-5-usb-audio.conf \
+               
modules/alsa/mixer/profile-sets/steelseries-arctis-common-usb-audio.conf \
                
modules/alsa/mixer/profile-sets/steelseries-arctis-7-usb-audio.conf \
                modules/alsa/mixer/profile-sets/dell-dock-tb16-usb-audio.conf \
                
modules/alsa/mixer/profile-sets/cmedia-high-speed-true-hdaudio.conf
@@ -1399,8 +1399,8 @@ dist_alsapaths_DATA = \
                modules/alsa/mixer/paths/hdmi-output-5.conf \
                modules/alsa/mixer/paths/hdmi-output-6.conf \
                modules/alsa/mixer/paths/hdmi-output-7.conf \
-               modules/alsa/mixer/paths/steelseries-arctis-5-output-chat.conf \
-               modules/alsa/mixer/paths/steelseries-arctis-5-output-game.conf \
+               
modules/alsa/mixer/paths/steelseries-arctis-output-chat-common.conf \
+               
modules/alsa/mixer/paths/steelseries-arctis-output-game-common.conf \
                modules/alsa/mixer/paths/steelseries-arctis-7-input.conf \
                modules/alsa/mixer/paths/steelseries-arctis-7-output-mono.conf \
                modules/alsa/mixer/paths/steelseries-arctis-7-output-stereo.conf
diff --git a/src/modules/alsa/90-pulseaudio.rules 
b/src/modules/alsa/90-pulseaudio.rules
index d85763917d76..9d34e6dfdd38 100644
--- a/src/modules/alsa/90-pulseaudio.rules
+++ b/src/modules/alsa/90-pulseaudio.rules
@@ -109,16 +109,20 @@ ATTRS{idVendor}=="0763", ATTRS{idProduct}=="2012", 
ENV{PULSE_PROFILE_SET}="maudi
 ATTRS{idVendor}=="045e", ATTRS{idProduct}=="02bb", 
ENV{PULSE_PROFILE_SET}="kinect-audio.conf"
 ATTRS{idVendor}=="041e", ATTRS{idProduct}=="322c", 
ENV{PULSE_PROFILE_SET}="sb-omni-surround-5.1.conf"
 ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="4014", 
ENV{PULSE_PROFILE_SET}="dell-dock-tb16-usb-audio.conf"
-ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1250", 
ENV{PULSE_PROFILE_SET}="steelseries-arctis-5-usb-audio.conf"
+
 
 # ID 1038:12ad is for the 2018 refresh of the Arctis 7.
-# ID 1038:1294 is for Arctis Pro Wireless (which works with the Arctis 7
-# configuration).
+# ID 1038:1294 is for Arctis Pro Wireless (which works with the Arctis 7 
configuration).
 ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1260", 
ENV{PULSE_PROFILE_SET}="steelseries-arctis-7-usb-audio.conf"
 ATTRS{idVendor}=="1038", ATTRS{idProduct}=="12ad", 
ENV{PULSE_PROFILE_SET}="steelseries-arctis-7-usb-audio.conf"
 ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1294", 
ENV{PULSE_PROFILE_SET}="steelseries-arctis-7-usb-audio.conf"
 ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1730", 
ENV{PULSE_PROFILE_SET}="steelseries-arctis-7-usb-audio.conf"
 
+# ID 1038:1250 is for the Arctis 5
+# ID 1038:1252 is for Arctis Pro 2019 edition
+ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1250", 
ENV{PULSE_PROFILE_SET}="steelseries-arctis-common-usb-audio.conf"
+ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1252", 
ENV{PULSE_PROFILE_SET}="steelseries-arctis-common-usb-audio.conf"
+
 ATTRS{idVendor}=="147a", ATTRS{idProduct}=="e055", 
ENV{PULSE_PROFILE_SET}="cmedia-high-speed-true-hdaudio.conf"
 
 GOTO="pulseaudio_end"
diff --git a/src/modules/alsa/mixer/paths/steelseries-arctis-5-output-chat.conf 
b/src/modules/alsa/mixer/paths/steelseries-arctis-output-chat-common.conf
similarity index 100%
rename from src/modules/alsa/mixer/paths/steelseries-arctis-5-output-chat.conf
rename to 
src/modules/alsa/mixer/paths/steelseries-arctis-output-chat-common.conf
diff --git a/src/modules/alsa/mixer/paths/steelseries-arctis-5-output-game.conf 
b/src/modules/alsa/mixer/paths/steelseries-arctis-output-game-common.conf
similarity index 100%
rename from src/modules/alsa/mixer/paths/steelseries-arctis-5-output-game.conf
rename to 
src/modules/alsa/mixer/paths/steelseries-arctis-output-game-common.conf
diff --git 
a/src/modules/alsa/mixer/profile-sets/steelseries-arctis-5-usb-audio.conf 
b/src/modules/alsa/mixer/profile-sets/steelseries-arctis-common-usb-audio.conf
similarity index 80%
rename from 
src/modules/alsa/mixer/profile-sets/steelseries-arctis-5-usb-audio.conf
rename to 
src/modules/alsa/mixer/profile-sets/steelseries-arctis-common-usb-audio.conf
index fe353c38f09c..80c33707aea4 100644
--- a/src/modules/alsa/mixer/profile-sets/steelseries-arctis-5-usb-audio.conf
+++ 
b/src/modules/alsa/mixer/profile-sets/steelseries-arctis-common-usb-audio.conf
@@ -6,13 +6,13 @@ description = Chat
 device-strings = hw:%f,0,0
 channel-map = left,right
 paths-input = analog-input-mic
-paths-output = steelseries-arctis-5-output-chat
+paths-output = steelseries-arctis-output-chat-common
 
 [Mapping analog-game]
 description = Game
 device-strings = hw:%f,1,0
 channel-map = left,right
-paths-output = steelseries-arctis-5-output-game
+paths-output = steelseries-arctis-output-game-common
 direction = output
 
 [Profile output:analog-chat+output:analog-game+input:analog-chat]
-- 
2.16.4

++++++ 0004-alsa-mixer-Add-support-for-SteelSeries-Arctis-5-2019.patch ++++++
>From 79d3b99ba4773651c4ebc082233f9d0b5f68bfad Mon Sep 17 00:00:00 2001
From: Krzysztof Stasiowski <[email protected]>
Date: Tue, 18 Jun 2019 11:50:35 +0000
Subject: [PATCH] alsa-mixer: Add support for SteelSeries Arctis 5 2019 headset

Signed-off-by: Dave Chiluk <[email protected]>
---
 src/modules/alsa/90-pulseaudio.rules | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/modules/alsa/90-pulseaudio.rules 
b/src/modules/alsa/90-pulseaudio.rules
index 9d34e6dfdd38..61e7692543e8 100644
--- a/src/modules/alsa/90-pulseaudio.rules
+++ b/src/modules/alsa/90-pulseaudio.rules
@@ -119,8 +119,10 @@ ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1294", 
ENV{PULSE_PROFILE_SET}="steel
 ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1730", 
ENV{PULSE_PROFILE_SET}="steelseries-arctis-7-usb-audio.conf"
 
 # ID 1038:1250 is for the Arctis 5
-# ID 1038:1252 is for Arctis Pro 2019 edition
+# ID 1037:12aa is for the Arctis 5 2019
+# ID 1038:1252 is for the Arctis Pro 2019 edition
 ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1250", 
ENV{PULSE_PROFILE_SET}="steelseries-arctis-common-usb-audio.conf"
+ATTRS{idVendor}=="1038", ATTRS{idProduct}=="12aa", 
ENV{PULSE_PROFILE_SET}="steelseries-arctis-common-usb-audio.conf"
 ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1252", 
ENV{PULSE_PROFILE_SET}="steelseries-arctis-common-usb-audio.conf"
 
 ATTRS{idVendor}=="147a", ATTRS{idProduct}=="e055", 
ENV{PULSE_PROFILE_SET}="cmedia-high-speed-true-hdaudio.conf"
-- 
2.16.4

++++++ 0005-alsa-mixer-add-support-for-LucidSound-LS31-and-creat.patch ++++++
>From 1ee1f749e154d2f64b4661f833eebaa18ae1a081 Mon Sep 17 00:00:00 2001
From: Dave Chiluk <[email protected]>
Date: Thu, 8 Aug 2019 23:10:01 -0500
Subject: [PATCH] alsa-mixer: add support for LucidSound LS31, and create
 usb-gaming-headset profile

---
 src/Makefile.am                                       |  8 ++++----
 src/modules/alsa/90-pulseaudio.rules                  | 10 ++++++----
 ...tis-7-input.conf => usb-gaming-headset-input.conf} | 12 ++++++++++--
 ...-mono.conf => usb-gaming-headset-output-mono.conf} | 15 ++++++++++-----
 ...reo.conf => usb-gaming-headset-output-stereo.conf} | 15 ++++++++++-----
 ...rctis-7-usb-audio.conf => usb-gaming-headset.conf} | 19 ++++++++++++-------
 6 files changed, 52 insertions(+), 27 deletions(-)
 rename src/modules/alsa/mixer/paths/{steelseries-arctis-7-input.conf => 
usb-gaming-headset-input.conf} (66%)
 rename src/modules/alsa/mixer/paths/{steelseries-arctis-7-output-mono.conf => 
usb-gaming-headset-output-mono.conf} (66%)
 rename src/modules/alsa/mixer/paths/{steelseries-arctis-7-output-stereo.conf 
=> usb-gaming-headset-output-stereo.conf} (68%)
 rename 
src/modules/alsa/mixer/profile-sets/{steelseries-arctis-7-usb-audio.conf => 
usb-gaming-headset.conf} (79%)

diff --git a/src/Makefile.am b/src/Makefile.am
index 3db921a07e1b..0f1ded7f96c5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1354,7 +1354,7 @@ dist_alsaprofilesets_DATA = \
                modules/alsa/mixer/profile-sets/kinect-audio.conf \
                modules/alsa/mixer/profile-sets/sb-omni-surround-5.1.conf \
                
modules/alsa/mixer/profile-sets/steelseries-arctis-common-usb-audio.conf \
-               
modules/alsa/mixer/profile-sets/steelseries-arctis-7-usb-audio.conf \
+               modules/alsa/mixer/profile-sets/usb-gaming-headset.conf \
                modules/alsa/mixer/profile-sets/dell-dock-tb16-usb-audio.conf \
                
modules/alsa/mixer/profile-sets/cmedia-high-speed-true-hdaudio.conf
 
@@ -1401,9 +1401,9 @@ dist_alsapaths_DATA = \
                modules/alsa/mixer/paths/hdmi-output-7.conf \
                
modules/alsa/mixer/paths/steelseries-arctis-output-chat-common.conf \
                
modules/alsa/mixer/paths/steelseries-arctis-output-game-common.conf \
-               modules/alsa/mixer/paths/steelseries-arctis-7-input.conf \
-               modules/alsa/mixer/paths/steelseries-arctis-7-output-mono.conf \
-               modules/alsa/mixer/paths/steelseries-arctis-7-output-stereo.conf
+               modules/alsa/mixer/paths/usb-gaming-headset-input.conf \
+               modules/alsa/mixer/paths/usb-gaming-headset-output-mono.conf \
+               modules/alsa/mixer/paths/usb-gaming-headset-output-stereo.conf
 
 endif
 
diff --git a/src/modules/alsa/90-pulseaudio.rules 
b/src/modules/alsa/90-pulseaudio.rules
index 61e7692543e8..fa43cb802a8e 100644
--- a/src/modules/alsa/90-pulseaudio.rules
+++ b/src/modules/alsa/90-pulseaudio.rules
@@ -113,10 +113,12 @@ ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="4014", 
ENV{PULSE_PROFILE_SET}="dell-
 
 # ID 1038:12ad is for the 2018 refresh of the Arctis 7.
 # ID 1038:1294 is for Arctis Pro Wireless (which works with the Arctis 7 
configuration).
-ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1260", 
ENV{PULSE_PROFILE_SET}="steelseries-arctis-7-usb-audio.conf"
-ATTRS{idVendor}=="1038", ATTRS{idProduct}=="12ad", 
ENV{PULSE_PROFILE_SET}="steelseries-arctis-7-usb-audio.conf"
-ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1294", 
ENV{PULSE_PROFILE_SET}="steelseries-arctis-7-usb-audio.conf"
-ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1730", 
ENV{PULSE_PROFILE_SET}="steelseries-arctis-7-usb-audio.conf"
+ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1260", 
ENV{PULSE_PROFILE_SET}="usb-gaming-headset.conf"
+ATTRS{idVendor}=="1038", ATTRS{idProduct}=="12ad", 
ENV{PULSE_PROFILE_SET}="usb-gaming-headset.conf"
+ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1294", 
ENV{PULSE_PROFILE_SET}="usb-gaming-headset.conf"
+ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1730", 
ENV{PULSE_PROFILE_SET}="usb-gaming-headset.conf"
+# Lucidsound LS31
+ATTRS{idVendor}=="2f12", ATTRS{idProduct}=="0109", 
ENV{PULSE_PROFILE_SET}="usb-gaming-headset.conf"
 
 # ID 1038:1250 is for the Arctis 5
 # ID 1037:12aa is for the Arctis 5 2019
diff --git a/src/modules/alsa/mixer/paths/steelseries-arctis-7-input.conf 
b/src/modules/alsa/mixer/paths/usb-gaming-headset-input.conf
similarity index 66%
rename from src/modules/alsa/mixer/paths/steelseries-arctis-7-input.conf
rename to src/modules/alsa/mixer/paths/usb-gaming-headset-input.conf
index 3fa36e9385a5..9fa7fe908587 100644
--- a/src/modules/alsa/mixer/paths/steelseries-arctis-7-input.conf
+++ b/src/modules/alsa/mixer/paths/usb-gaming-headset-input.conf
@@ -13,8 +13,16 @@
 # You should have received a copy of the GNU Lesser General Public License
 # along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
 
-; Steelseries Arctis 7 USB headset microphone path. Works also with Arctis Pro
-; Wireless.
+; USB gaming headset microphone input path. These headsets usually have two
+; output devices. The first one is mono, meant for voice audio, and the second
+; one is stereo, meant for everything else. The purpose of this unusual design
+; is to provide separate volume controls for voice and other audio, which can
+; be useful in gaming.
+;
+; Works with:
+; Steelseries Arctis 7
+; Steelseries Arctis Pro Wireless.
+; Lucidsound LS31
 
 [General]
 description-key = analog-input-microphone-headset
diff --git a/src/modules/alsa/mixer/paths/steelseries-arctis-7-output-mono.conf 
b/src/modules/alsa/mixer/paths/usb-gaming-headset-output-mono.conf
similarity index 66%
rename from src/modules/alsa/mixer/paths/steelseries-arctis-7-output-mono.conf
rename to src/modules/alsa/mixer/paths/usb-gaming-headset-output-mono.conf
index d8b24a2fbdf2..6df662f069e5 100644
--- a/src/modules/alsa/mixer/paths/steelseries-arctis-7-output-mono.conf
+++ b/src/modules/alsa/mixer/paths/usb-gaming-headset-output-mono.conf
@@ -13,11 +13,16 @@
 # You should have received a copy of the GNU Lesser General Public License
 # along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
 
-; Steelseries Arctis 7 USB headset mono output path. Works also with Arctis Pro
-; Wireless. The headset has two output devices. The first one is mono, meant
-; for voice audio, and the second one is stereo, meant for everything else. The
-; purpose of this unusual design is to provide separate volume controls for
-; voice and other audio, which can be useful in gaming.
+; USB gaming headset mono output path. These headsets usually have two
+; output devices. The first one is mono, meant for voice audio, and the second
+; one is stereo, meant for everything else. The purpose of this unusual design
+; is to provide separate volume controls for voice and other audio, which can
+; be useful in gaming.
+;
+; Works with:
+; Steelseries Arctis 7
+; Steelseries Arctis Pro Wireless.
+; Lucidsound LS31
 
 [General]
 description-key = analog-output-headphones-mono
diff --git 
a/src/modules/alsa/mixer/paths/steelseries-arctis-7-output-stereo.conf 
b/src/modules/alsa/mixer/paths/usb-gaming-headset-output-stereo.conf
similarity index 68%
rename from src/modules/alsa/mixer/paths/steelseries-arctis-7-output-stereo.conf
rename to src/modules/alsa/mixer/paths/usb-gaming-headset-output-stereo.conf
index fcc58a033e2f..e3f91cd6cd2e 100644
--- a/src/modules/alsa/mixer/paths/steelseries-arctis-7-output-stereo.conf
+++ b/src/modules/alsa/mixer/paths/usb-gaming-headset-output-stereo.conf
@@ -13,11 +13,16 @@
 # You should have received a copy of the GNU Lesser General Public License
 # along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
 
-; Steelseries Arctis 7 USB headset stereo output path. Works also with Arctis
-; Pro Wireless. The headset has two output devices. The first one is mono,
-; meant for voice audio, and the second one is stereo, meant for everything
-; else. The purpose of this unusual design is to provide separate volume
-; controls for voice and other audio, which can be useful in gaming.
+; USB gaming headset mono output path. These headsets usually have two
+; output devices. The first one is mono, meant for voice audio, and the second
+; one is stereo, meant for everything else. The purpose of this unusual design
+; is to provide separate volume controls for voice and other audio, which can
+; be useful in gaming.
+;
+; Works with:
+; Steelseries Arctis 7
+; Steelseries Arctis Pro Wireless.
+; Lucidsound LS31
 ;
 ; This path doesn't provide hardware volume control, because the stereo
 ; output is controlled by the PCM element with index 1, and currently
diff --git 
a/src/modules/alsa/mixer/profile-sets/steelseries-arctis-7-usb-audio.conf 
b/src/modules/alsa/mixer/profile-sets/usb-gaming-headset.conf
similarity index 79%
rename from 
src/modules/alsa/mixer/profile-sets/steelseries-arctis-7-usb-audio.conf
rename to src/modules/alsa/mixer/profile-sets/usb-gaming-headset.conf
index e1394dcfcc3b..01ecf864bcf2 100644
--- a/src/modules/alsa/mixer/profile-sets/steelseries-arctis-7-usb-audio.conf
+++ b/src/modules/alsa/mixer/profile-sets/usb-gaming-headset.conf
@@ -13,12 +13,17 @@
 # You should have received a copy of the GNU Lesser General Public License
 # along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
 
-; Steelseries Arctis 7 USB and Arctis Pro Wireless USB headset. These headsets 
-; have a microphone and two output devices. The first output device is mono, 
-; meant for voice audio, and the second one is stereo, meant for everything 
-; else. The purpose of this unusual design is to provide separate volume 
+; USB gaming headset.
+; These headsets usually have two output devices. The first one is mono,
+; meant for voice audio, and the second one is stereo, meant for everything
+; else. The purpose of this unusual design is to provide separate volume
 ; controls for voice and other audio, which can be useful in gaming.
 ;
+; Works with:
+; Steelseries Arctis 7
+; Steelseries Arctis Pro Wireless.
+; Lucidsound LS31
+;
 ; See default.conf for an explanation on the directives used here.
 
 [General]
@@ -27,13 +32,13 @@ auto-profiles = yes
 [Mapping analog-mono]
 device-strings = hw:%f,0,0
 channel-map = mono
-paths-output = steelseries-arctis-7-output-mono
-paths-input = steelseries-arctis-7-input
+paths-output = usb-gaming-headset-output-mono
+paths-input = usb-gaming-headset-input
 
 [Mapping analog-stereo]
 device-strings = hw:%f,1,0
 channel-map = left,right
-paths-output = steelseries-arctis-7-output-stereo
+paths-output = usb-gaming-headset-output-stereo
 direction = output
 
 [Profile output:analog-mono+output:analog-stereo+input:analog-mono]
-- 
2.16.4

++++++ 0006-alsa-ucm-use-ucm2-name-for-the-direct-card-index-ope.patch ++++++
>From c8f065250dde966825f171ff817f7301f423a42e Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <[email protected]>
Date: Sat, 23 Nov 2019 15:17:30 +0100
Subject: [PATCH] alsa-ucm: use ucm2 name for the direct card index open

Signed-off-by: Jaroslav Kysela <[email protected]>
---
 src/modules/alsa/alsa-ucm.c | 28 ++++++++++++++++++----------
 1 file changed, 18 insertions(+), 10 deletions(-)

diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index 0a40ca8fe654..aeb4e59e323c 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -576,17 +576,25 @@ int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, 
int card_index) {
     const char **verb_list;
     int num_verbs, i, err = 0;
 
-    /* is UCM available for this card ? */
-    err = snd_card_get_name(card_index, &card_name);
-    if (err < 0) {
-        pa_log("Card can't get card_name from card_index %d", card_index);
-        goto name_fail;
-    }
-
+    /* support multiple card instances, address card directly by index */
+    card_name = pa_sprintf_malloc("hw:%i", card_index);
+    if (card_name == NULL)
+        return -ENOMEM;
     err = snd_use_case_mgr_open(&ucm->ucm_mgr, card_name);
     if (err < 0) {
-        pa_log_info("UCM not available for card %s", card_name);
-        goto ucm_mgr_fail;
+        /* fallback longname: is UCM available for this card ? */
+        pa_xfree(card_name);
+        err = snd_card_get_name(card_index, &card_name);
+        if (err < 0) {
+            pa_log("Card can't get card_name from card_index %d", card_index);
+            goto name_fail;
+        }
+
+        err = snd_use_case_mgr_open(&ucm->ucm_mgr, card_name);
+        if (err < 0) {
+            pa_log_info("UCM not available for card %s", card_name);
+            goto ucm_mgr_fail;
+        }
     }
 
     pa_log_info("UCM available for card %s", card_name);
@@ -626,7 +634,7 @@ ucm_verb_fail:
     }
 
 ucm_mgr_fail:
-    free(card_name);
+    pa_xfree(card_name);
 
 name_fail:
     return err;
-- 
2.16.4

++++++ 0007-alsa-ucm-add-mixer-IDs-to-ucm_items.patch ++++++
>From ab5be56a10a83bfdfd7f40c02245db039e6eb730 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <[email protected]>
Date: Sat, 23 Nov 2019 15:50:29 +0100
Subject: [PATCH] alsa-ucm: add mixer IDs to ucm_items

Signed-off-by: Jaroslav Kysela <[email protected]>
---
 src/modules/alsa/alsa-ucm.c |  6 ++++++
 src/modules/alsa/alsa-ucm.h | 18 ++++++++++++++++++
 2 files changed, 24 insertions(+)

diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index aeb4e59e323c..14056825a25f 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -100,11 +100,17 @@ static struct ucm_items item[] = {
     {"CapturePCM", PA_ALSA_PROP_UCM_SOURCE},
     {"PlaybackVolume", PA_ALSA_PROP_UCM_PLAYBACK_VOLUME},
     {"PlaybackSwitch", PA_ALSA_PROP_UCM_PLAYBACK_SWITCH},
+    {"PlaybackMixerElem", PA_ALSA_PROP_UCM_PLAYBACK_MIXER_ELEM},
+    {"PlaybackMasterElem", PA_ALSA_PROP_UCM_PLAYBACK_MASTER_ELEM},
+    {"PlaybackMasterType", PA_ALSA_PROP_UCM_PLAYBACK_MASTER_TYPE},
     {"PlaybackPriority", PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY},
     {"PlaybackRate", PA_ALSA_PROP_UCM_PLAYBACK_RATE},
     {"PlaybackChannels", PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS},
     {"CaptureVolume", PA_ALSA_PROP_UCM_CAPTURE_VOLUME},
     {"CaptureSwitch", PA_ALSA_PROP_UCM_CAPTURE_SWITCH},
+    {"CaptureMixerElem", PA_ALSA_PROP_UCM_CAPTURE_MIXER_ELEM},
+    {"CaptureMasterElem", PA_ALSA_PROP_UCM_CAPTURE_MASTER_ELEM},
+    {"CaptureMasterType", PA_ALSA_PROP_UCM_CAPTURE_MASTER_TYPE},
     {"CapturePriority", PA_ALSA_PROP_UCM_CAPTURE_PRIORITY},
     {"CaptureRate", PA_ALSA_PROP_UCM_CAPTURE_RATE},
     {"CaptureChannels", PA_ALSA_PROP_UCM_CAPTURE_CHANNELS},
diff --git a/src/modules/alsa/alsa-ucm.h b/src/modules/alsa/alsa-ucm.h
index c926f3cc39a6..4feb8c0bfc3f 100644
--- a/src/modules/alsa/alsa-ucm.h
+++ b/src/modules/alsa/alsa-ucm.h
@@ -51,6 +51,15 @@ typedef void snd_use_case_mgr_t;
 /** For devices: Playback switch e.g PlaybackSwitch */
 #define PA_ALSA_PROP_UCM_PLAYBACK_SWITCH            "alsa.ucm.playback.switch"
 
+/** For devices: Playback mixer identifier */
+#define PA_ALSA_PROP_UCM_PLAYBACK_MIXER_ELEM        
"alsa.ucm.playback.mixer.element"
+
+/** For devices: Playback mixer master identifier */
+#define PA_ALSA_PROP_UCM_PLAYBACK_MASTER_ELEM       
"alsa.ucm.playback.master.element"
+
+/** For devices: Playback mixer master type */
+#define PA_ALSA_PROP_UCM_PLAYBACK_MASTER_TYPE       
"alsa.ucm.playback.master.type"
+
 /** For devices: Playback priority */
 #define PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY          
"alsa.ucm.playback.priority"
 
@@ -69,6 +78,15 @@ typedef void snd_use_case_mgr_t;
 /** For devices: Capture switch e.g CaptureSwitch */
 #define PA_ALSA_PROP_UCM_CAPTURE_SWITCH             "alsa.ucm.capture.switch"
 
+/** For devices: Capture mixer identifier */
+#define PA_ALSA_PROP_UCM_CAPTURE_MIXER_ELEM         
"alsa.ucm.capture.mixer.element"
+
+/** For devices: Capture mixer identifier */
+#define PA_ALSA_PROP_UCM_CAPTURE_MASTER_ELEM        
"alsa.ucm.capture.master.element"
+
+/** For devices: Capture mixer identifier */
+#define PA_ALSA_PROP_UCM_CAPTURE_MASTER_TYPE        
"alsa.ucm.capture.master.type"
+
 /** For devices: Capture priority */
 #define PA_ALSA_PROP_UCM_CAPTURE_PRIORITY           "alsa.ucm.capture.priority"
 
-- 
2.16.4

++++++ 0008-alsa-mixer-handle-the-index-for-ALSA-mixer-element-i.patch ++++++
++++ 820 lines (skipped)

++++++ 0009-alsa-mixer-improve-alsa_id_decode-function.patch ++++++
>From 1c240b7a12e9e2f7c2266d18cbb74130bb81277e Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <[email protected]>
Date: Tue, 26 Nov 2019 10:35:14 +0100
Subject: [PATCH] alsa-mixer: improve alsa_id_decode() function

Accept those identifiers:

        Speaker,1
        'Speaker',1
        "Speaker",1

Signed-off-by: Jaroslav Kysela <[email protected]>
---
 src/modules/alsa/alsa-mixer.c | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)

diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index 38ace783a997..f57aabe5d885 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -117,11 +117,24 @@ static char *alsa_id_str(char *dst, size_t dst_len, 
pa_alsa_mixer_id *id) {
 }
 
 static int alsa_id_decode(const char *src, char *name, int *index) {
-    char *idx;
+    char *idx, c;
+    int i;
 
     *index = 0;
-    strcpy(name, src);
-    idx = strchr(name, ',');
+    c = src[0];
+    /* Strip quotes in entries such as 'Speaker',1 or "Speaker",1 */
+    if (c == '\'' || c == '"') {
+        strcpy(name, src + 1);
+        for (i = 0; name[i] != '\0' && name[i] != c; i++);
+        idx = NULL;
+        if (name[i]) {
+                name[i] = '\0';
+                idx = strchr(name + i + 1, ',');
+        }
+    } else {
+        strcpy(name, src);
+        idx = strchr(name, ',');
+    }
     if (idx == NULL)
         return 0;
     *idx = '\0';
-- 
2.16.4

++++++ 0010-alsa-ucm-Support-Playback-CaptureVolume.patch ++++++
++++ 795 lines (skipped)

++++++ 0011-alsa-ucm-Fix-volume-control-based-on-review.patch ++++++
>From 9acacd9ba3b9f4df0957e9ddaacbcee00396175c Mon Sep 17 00:00:00 2001
From: Jaska Uimonen <[email protected]>
Date: Tue, 1 Oct 2019 18:34:17 +0300
Subject: [PATCH] alsa-ucm: Fix volume control based on review

- sync mixer logic added
- mixer path creation, empty set in mapping creation, paths added in path 
creation
- path creation moved inside new port creation as it might be called twice 
otherwise
- some comments added
---
 src/modules/alsa/alsa-sink.c   | 31 ++++++++--------------------
 src/modules/alsa/alsa-source.c | 29 ++++++++------------------
 src/modules/alsa/alsa-ucm.c    | 47 +++++++++++++++++++++++++++++-------------
 src/modules/alsa/alsa-ucm.h    |  2 +-
 4 files changed, 52 insertions(+), 57 deletions(-)

diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 08d4d1f38b80..0a5c92529af3 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -1266,7 +1266,7 @@ static void sync_mixer(struct userdata *u, pa_device_port 
*port) {
 
     /* port may be NULL, because if we use a synthesized mixer path, then the
      * sink has no ports. */
-    if (port) {
+    if (port && !u->ucm_context) {
         pa_alsa_port_data *data;
 
         data = PA_DEVICE_PORT_DATA(port);
@@ -1648,28 +1648,19 @@ static int sink_set_port_ucm_cb(pa_sink *s, 
pa_device_port *p) {
     struct userdata *u = s->userdata;
     pa_alsa_ucm_port_data *data;
 
-    data = PA_DEVICE_PORT_DATA(p);
-
     pa_assert(u);
     pa_assert(p);
+    pa_assert(u->mixer_handle);
     pa_assert(u->ucm_context);
 
-    u->mixer_path = data->path;
+    data = PA_DEVICE_PORT_DATA(p);
+    pa_assert_se(u->mixer_path = data->path);
     mixer_volume_init(u);
 
-    if (u->mixer_path) {
-        pa_alsa_path_select(u->mixer_path, NULL, u->mixer_handle, s->muted);
-
-        if (s->set_mute)
-            s->set_mute(s);
-        if (s->flags & PA_SINK_DEFERRED_VOLUME) {
-            if (s->write_volume)
-                s->write_volume(s);
-        } else {
-            if (s->set_volume)
-                s->set_volume(s);
-        }
-    }
+    if (s->flags & PA_SINK_DEFERRED_VOLUME)
+        pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), 
SINK_MESSAGE_SYNC_MIXER, p, 0, NULL);
+    else
+        sync_mixer(u, p);
 
     return pa_alsa_ucm_set_port(u->ucm_context, p, true);
 }
@@ -2091,6 +2082,7 @@ static void set_sink_name(pa_sink_new_data *data, 
pa_modargs *ma, const char *de
 }
 
 static void find_mixer(struct userdata *u, pa_alsa_mapping *mapping, const 
char *element, bool ignore_dB) {
+
     if (!mapping && !element)
         return;
 
@@ -2099,11 +2091,6 @@ static void find_mixer(struct userdata *u, 
pa_alsa_mapping *mapping, const char
         return;
     }
 
-    if (u->ucm_context) {
-        /* We just want to open the device */
-        return;
-    }
-
     if (element) {
 
         if (!(u->mixer_path = pa_alsa_path_synthesize(element, 
PA_ALSA_DIRECTION_OUTPUT)))
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index 657ed5aeda11..d186101720b8 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -1137,7 +1137,7 @@ static void sync_mixer(struct userdata *u, pa_device_port 
*port) {
 
     /* port may be NULL, because if we use a synthesized mixer path, then the
      * source has no ports. */
-    if (port) {
+    if (port && !u->ucm_context) {
         pa_alsa_port_data *data;
 
         data = PA_DEVICE_PORT_DATA(port);
@@ -1523,24 +1523,17 @@ static int source_set_port_ucm_cb(pa_source *s, 
pa_device_port *p) {
 
     pa_assert(u);
     pa_assert(p);
+    pa_assert(u->mixer_handle);
     pa_assert(u->ucm_context);
 
-    u->mixer_path = data->path;
+    data = PA_DEVICE_PORT_DATA(p);
+    pa_assert_se(u->mixer_path = data->path);
     mixer_volume_init(u);
 
-    if (u->mixer_path) {
-        pa_alsa_path_select(u->mixer_path, NULL, u->mixer_handle, s->muted);
-
-        if (s->set_mute)
-            s->set_mute(s);
-        if (s->flags & PA_SOURCE_DEFERRED_VOLUME) {
-            if (s->write_volume)
-                s->write_volume(s);
-        } else {
-            if (s->set_volume)
-                s->set_volume(s);
-        }
-    }
+    if (s->flags & PA_SOURCE_DEFERRED_VOLUME)
+        pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), 
SOURCE_MESSAGE_SYNC_MIXER, p, 0, NULL);
+    else
+        sync_mixer(u, p);
 
     return pa_alsa_ucm_set_port(u->ucm_context, p, false);
 }
@@ -1805,11 +1798,6 @@ static void find_mixer(struct userdata *u, 
pa_alsa_mapping *mapping, const char
         return;
     }
 
-    if (u->ucm_context) {
-        /* We just want to open the device */
-        return;
-    }
-
     if (element) {
 
         if (!(u->mixer_path = pa_alsa_path_synthesize(element, 
PA_ALSA_DIRECTION_INPUT)))
@@ -2404,6 +2392,7 @@ static void userdata_free(struct userdata *u) {
     if (u->mixer_fdl)
         pa_alsa_fdlist_free(u->mixer_fdl);
 
+    /* Only free the mixer_path if the sink owns it */
     if (u->mixer_path && !u->mixer_path_set && !u->ucm_context)
         pa_alsa_path_free(u->mixer_path);
 
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index 349a59566200..a812b52f449e 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -852,21 +852,29 @@ static void ucm_add_port_combination(
 
         pa_hashmap_put(ports, port->name, port);
         pa_log_debug("Add port %s: %s", port->name, port->description);
-    }
-
-    if (num == 1) {
-        /* To keep things simple and not worry about stacking controls, we 
only support hardware volumes on non-combination
-         * ports. */
-        data = PA_DEVICE_PORT_DATA(port);
 
-        PA_HASHMAP_FOREACH_KV(profile, volume_element, is_sink ? 
dev->playback_volumes : dev->capture_volumes, state) {
-            pa_alsa_path *path = pa_alsa_path_synthesize(volume_element,
-                    is_sink ? PA_ALSA_DIRECTION_OUTPUT : 
PA_ALSA_DIRECTION_INPUT);
-
-            if (!path)
-                pa_log_warn("Failed to set up volume control: %s", 
volume_element);
-            else
-                pa_hashmap_put(data->paths, pa_xstrdup(profile), path);
+        if (num == 1) {
+            /* To keep things simple and not worry about stacking controls, we 
only support hardware volumes on non-combination
+             * ports. */
+            data = PA_DEVICE_PORT_DATA(port);
+
+            PA_HASHMAP_FOREACH_KV(profile, volume_element, is_sink ? 
dev->playback_volumes : dev->capture_volumes, state) {
+                pa_alsa_path *path = pa_alsa_path_synthesize(volume_element,
+                                                             is_sink ? 
PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT);
+
+                if (!path)
+                    pa_log_warn("Failed to set up volume control: %s", 
volume_element);
+                else {
+                    pa_hashmap_put(data->paths, pa_xstrdup(profile), path);
+
+                    /* Add path also to already created empty path set */
+                    dev = sorted[0];
+                    if (is_sink)
+                        
pa_hashmap_put(dev->playback_mapping->output_path_set->paths, 
pa_xstrdup(volume_element), path);
+                    else
+                        
pa_hashmap_put(dev->capture_mapping->input_path_set->paths, 
pa_xstrdup(volume_element), path);
+                }
+            }
         }
     }
 
@@ -1185,16 +1193,27 @@ int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context 
*context, pa_device_port *p
 
 static void ucm_add_mapping(pa_alsa_profile *p, pa_alsa_mapping *m) {
 
+    pa_alsa_path_set *ps;
+
+    /* create empty path set for the future path additions */
+    ps = pa_xnew0(pa_alsa_path_set, 1);
+    ps->direction = m->direction;
+    ps->paths = pa_hashmap_new(pa_idxset_trivial_hash_func, 
pa_idxset_trivial_compare_func);
+
     switch (m->direction) {
         case PA_ALSA_DIRECTION_ANY:
             pa_idxset_put(p->output_mappings, m, NULL);
             pa_idxset_put(p->input_mappings, m, NULL);
+            m->output_path_set = ps;
+            m->input_path_set = ps;
             break;
         case PA_ALSA_DIRECTION_OUTPUT:
             pa_idxset_put(p->output_mappings, m, NULL);
+            m->output_path_set = ps;
             break;
         case PA_ALSA_DIRECTION_INPUT:
             pa_idxset_put(p->input_mappings, m, NULL);
+            m->input_path_set = ps;
             break;
     }
 }
diff --git a/src/modules/alsa/alsa-ucm.h b/src/modules/alsa/alsa-ucm.h
index 2e39a09a51f3..d8507a83615c 100644
--- a/src/modules/alsa/alsa-ucm.h
+++ b/src/modules/alsa/alsa-ucm.h
@@ -240,7 +240,7 @@ struct pa_alsa_ucm_port_data {
      * a combination of devices. */
     pa_dynarray *devices; /* pa_alsa_ucm_device */
 
-    /* profile -> pa_alsa_path for volume control */
+    /* profile name -> pa_alsa_path for volume control */
     pa_hashmap *paths;
     /* Current path, set when activating profile */
     pa_alsa_path *path;
-- 
2.16.4

++++++ 0012-alsa-ucm-use-the-correct-mixer-identifiers-as-first.patch ++++++
>From dc9dc70fcc1b06788d08b5e7997c9053a13cbce2 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <[email protected]>
Date: Tue, 26 Nov 2019 10:54:15 +0100
Subject: [PATCH] alsa-ucm: use the correct mixer identifiers as first

The mixer identifiers should be used for snd_mixer_selem API.
Use them as first, then try to fallback to the raw control
identifiers.

Signed-off-by: Jaroslav Kysela <[email protected]>
---
 src/modules/alsa/alsa-ucm.c | 78 ++++++++++++++++++++++++++++++++-------------
 1 file changed, 56 insertions(+), 22 deletions(-)

diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index a812b52f449e..65ec6941e8de 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -200,6 +200,50 @@ static void ucm_add_devices_to_idxset(
     }
 }
 
+/* Get the volume identifier */
+static char *ucm_get_mixer_id(
+        pa_alsa_ucm_device *device,
+        const char *mprop,
+        const char *cprop,
+        const char *cid)
+{
+#if SND_LIB_VERSION >= 0x10201
+    snd_ctl_elem_id_t *ctl;
+    int err;
+#endif
+    const char *value;
+    char *value2;
+    int index;
+
+    value = pa_proplist_gets(device->proplist, mprop);
+    if (value)
+        return pa_xstrdup(value);
+    value = pa_proplist_gets(device->proplist, cprop);
+    if (value == NULL)
+        return NULL;
+#if SND_LIB_VERSION >= 0x10201
+    snd_ctl_elem_id_alloca(&ctl);
+    err = snd_use_case_parse_ctl_elem_id(ctl, cid, value);
+    if (err < 0)
+        return NULL;
+    value = snd_ctl_elem_id_get_name(ctl);
+    index = snd_ctl_elem_id_get_index(ctl);
+#else
+#warning "Upgrade to alsa-lib 1.2.1!"
+    index = 0;
+#endif
+    if (!(value2 = pa_str_strip_suffix(value, " Playback Volume")))
+        if (!(value2 = pa_str_strip_suffix(value, " Capture Volume")))
+            if (!(value2 = pa_str_strip_suffix(value, " Volume")))
+                value2 = pa_xstrdup(value);
+    if (index > 0) {
+        char *mix = pa_sprintf_malloc("'%s',%d", value2, index);
+        pa_xfree(value2);
+        return mix;
+    }
+    return value2;
+}
+
 /* Create a property list for this ucm device */
 static int ucm_get_device_property(
         pa_alsa_ucm_device *device,
@@ -296,17 +340,12 @@ static int ucm_get_device_property(
                 pa_log_debug("UCM playback priority %s for device %s error", 
value, device_name);
         }
 
-        value = pa_proplist_gets(device->proplist, 
PA_ALSA_PROP_UCM_PLAYBACK_VOLUME);
-        if (value) {
-            /* Try to get the simple control name, and failing that, just use 
the name as is */
-            char *selem;
-
-            if (!(selem = pa_str_strip_suffix(value, " Playback Volume")))
-                if (!(selem = pa_str_strip_suffix(value, " Volume")))
-                    selem = pa_xstrdup(value);
-
-            pa_hashmap_put(device->playback_volumes, 
pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), selem);
-        }
+        value = ucm_get_mixer_id(device,
+                                 PA_ALSA_PROP_UCM_PLAYBACK_MIXER_ELEM,
+                                 PA_ALSA_PROP_UCM_PLAYBACK_VOLUME,
+                                 "PlaybackVolume");
+        if (value)
+            pa_hashmap_put(device->playback_volumes, 
pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), (void 
*)value);
     }
 
     if (device->capture_channels) { /* source device */
@@ -329,17 +368,12 @@ static int ucm_get_device_property(
                 pa_log_debug("UCM capture priority %s for device %s error", 
value, device_name);
         }
 
-        value = pa_proplist_gets(device->proplist, 
PA_ALSA_PROP_UCM_CAPTURE_VOLUME);
-        if (value) {
-            /* Try to get the simple control name, and failing that, just use 
the name as is */
-            char *selem;
-
-            if (!(selem = pa_str_strip_suffix(value, " Capture Volume")))
-                if (!(selem = pa_str_strip_suffix(value, " Volume")))
-                    selem = pa_xstrdup(value);
-
-            pa_hashmap_put(device->capture_volumes, 
pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), selem);
-        }
+        value = ucm_get_mixer_id(device,
+                                 PA_ALSA_PROP_UCM_CAPTURE_MIXER_ELEM,
+                                 PA_ALSA_PROP_UCM_CAPTURE_VOLUME,
+                                 "CaptureVolume");
+        if (value)
+          pa_hashmap_put(device->capture_volumes, 
pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), (void 
*)value);
     }
 
     if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device) || 
PA_UCM_CAPTURE_PRIORITY_UNSET(device)) {
-- 
2.16.4

++++++ 0013-alsa-ucm-add-support-for-master-volume.patch ++++++
>From 6d830bf0f08c7f92418c2d8b0e73c0415dca03c8 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <[email protected]>
Date: Wed, 27 Nov 2019 11:34:49 +0100
Subject: [PATCH] alsa-ucm: add support for master volume

Signed-off-by: Jaroslav Kysela <[email protected]>
---
 src/modules/alsa/alsa-mixer.c | 20 +++++-----
 src/modules/alsa/alsa-mixer.h |  1 +
 src/modules/alsa/alsa-ucm.c   | 86 +++++++++++++++++++++++++++++++++----------
 src/modules/alsa/alsa-ucm.h   | 19 ++++++++++
 4 files changed, 96 insertions(+), 30 deletions(-)

diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index f57aabe5d885..ed06f42d86da 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -1923,7 +1923,7 @@ static int jack_probe(pa_alsa_jack *j, pa_alsa_mapping 
*mapping, snd_mixer_t *m)
     return 0;
 }
 
-static pa_alsa_element* element_get(pa_alsa_path *p, const char *section, bool 
prefixed) {
+pa_alsa_element * pa_alsa_element_get(pa_alsa_path *p, const char *section, 
bool prefixed) {
     pa_alsa_element *e;
     char *name;
     int index;
@@ -2025,7 +2025,7 @@ static pa_alsa_option* option_get(pa_alsa_path *p, const 
char *section) {
         return p->last_option;
     }
 
-    pa_assert_se(e = element_get(p, en, false));
+    pa_assert_se(e = pa_alsa_element_get(p, en, false));
 
     PA_LLIST_FOREACH(o, e->options)
         if (pa_streq(o->alsa_name, on))
@@ -2054,7 +2054,7 @@ static int element_parse_switch(pa_config_parser_state 
*state) {
 
     p = state->userdata;
 
-    if (!(e = element_get(p, state->section, true))) {
+    if (!(e = pa_alsa_element_get(p, state->section, true))) {
         pa_log("[%s:%u] Switch makes no sense in '%s'", state->filename, 
state->lineno, state->section);
         return -1;
     }
@@ -2085,7 +2085,7 @@ static int element_parse_volume(pa_config_parser_state 
*state) {
 
     p = state->userdata;
 
-    if (!(e = element_get(p, state->section, true))) {
+    if (!(e = pa_alsa_element_get(p, state->section, true))) {
         pa_log("[%s:%u] Volume makes no sense in '%s'", state->filename, 
state->lineno, state->section);
         return -1;
     }
@@ -2121,7 +2121,7 @@ static int 
element_parse_enumeration(pa_config_parser_state *state) {
 
     p = state->userdata;
 
-    if (!(e = element_get(p, state->section, true))) {
+    if (!(e = pa_alsa_element_get(p, state->section, true))) {
         pa_log("[%s:%u] Enumeration makes no sense in '%s'", state->filename, 
state->lineno, state->section);
         return -1;
     }
@@ -2213,7 +2213,7 @@ static int element_parse_required(pa_config_parser_state 
*state) {
 
     p = state->userdata;
 
-    e = element_get(p, state->section, true);
+    e = pa_alsa_element_get(p, state->section, true);
     o = option_get(p, state->section);
     j = jack_get(p, state->section);
     if (!e && !o && !j) {
@@ -2279,7 +2279,7 @@ static int element_parse_direction(pa_config_parser_state 
*state) {
 
     p = state->userdata;
 
-    if (!(e = element_get(p, state->section, true))) {
+    if (!(e = pa_alsa_element_get(p, state->section, true))) {
         pa_log("[%s:%u] Direction makes no sense in '%s'", state->filename, 
state->lineno, state->section);
         return -1;
     }
@@ -2305,7 +2305,7 @@ static int 
element_parse_direction_try_other(pa_config_parser_state *state) {
 
     p = state->userdata;
 
-    if (!(e = element_get(p, state->section, true))) {
+    if (!(e = pa_alsa_element_get(p, state->section, true))) {
         pa_log("[%s:%u] Direction makes no sense in '%s'", state->filename, 
state->lineno, state->section);
         return -1;
     }
@@ -2328,7 +2328,7 @@ static int 
element_parse_volume_limit(pa_config_parser_state *state) {
 
     p = state->userdata;
 
-    if (!(e = element_get(p, state->section, true))) {
+    if (!(e = pa_alsa_element_get(p, state->section, true))) {
         pa_log("[%s:%u] volume-limit makes no sense in '%s'", state->filename, 
state->lineno, state->section);
         return -1;
     }
@@ -2386,7 +2386,7 @@ static int 
element_parse_override_map(pa_config_parser_state *state) {
 
     p = state->userdata;
 
-    if (!(e = element_get(p, state->section, true))) {
+    if (!(e = pa_alsa_element_get(p, state->section, true))) {
         pa_log("[%s:%u] Override map makes no sense in '%s'", state->filename, 
state->lineno, state->section);
         return -1;
     }
diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h
index 709f270fbfa5..0b634f2f2be1 100644
--- a/src/modules/alsa/alsa-mixer.h
+++ b/src/modules/alsa/alsa-mixer.h
@@ -244,6 +244,7 @@ void pa_alsa_element_dump(pa_alsa_element *e);
 
 pa_alsa_path *pa_alsa_path_new(const char *paths_dir, const char *fname, 
pa_alsa_direction_t direction);
 pa_alsa_path *pa_alsa_path_synthesize(const char *element, pa_alsa_direction_t 
direction);
+pa_alsa_element *pa_alsa_element_get(pa_alsa_path *p, const char *section, 
bool prefixed);
 int pa_alsa_path_probe(pa_alsa_path *p, pa_alsa_mapping *mapping, snd_mixer_t 
*m, bool ignore_dB);
 void pa_alsa_path_dump(pa_alsa_path *p);
 int pa_alsa_path_get_volume(pa_alsa_path *p, snd_mixer_t *m, const 
pa_channel_map *cm, pa_cvolume *v);
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index 65ec6941e8de..46d016541ddf 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -200,6 +200,14 @@ static void ucm_add_devices_to_idxset(
     }
 }
 
+static void ucm_volume_free(pa_alsa_ucm_volume *vol) {
+    pa_assert(vol);
+    pa_xfree(vol->mixer_elem);
+    pa_xfree(vol->master_elem);
+    pa_xfree(vol->master_type);
+    pa_xfree(vol);
+}
+
 /* Get the volume identifier */
 static char *ucm_get_mixer_id(
         pa_alsa_ucm_device *device,
@@ -244,6 +252,32 @@ static char *ucm_get_mixer_id(
     return value2;
 }
 
+/* Get the volume identifier */
+static pa_alsa_ucm_volume *ucm_get_mixer_volume(
+        pa_alsa_ucm_device *device,
+        const char *mprop,
+        const char *cprop,
+        const char *cid,
+        const char *masterid,
+        const char *mastertype)
+{
+    pa_alsa_ucm_volume *vol;
+    char *mixer_elem;
+
+    mixer_elem = ucm_get_mixer_id(device, mprop, cprop, cid);
+    if (mixer_elem == NULL)
+        return NULL;
+    vol = pa_xnew0(pa_alsa_ucm_volume, 1);
+    if (vol == NULL) {
+        pa_xfree(mixer_elem);
+        return NULL;
+    }
+    vol->mixer_elem = mixer_elem;
+    vol->master_elem = pa_xstrdup(pa_proplist_gets(device->proplist, 
masterid));
+    vol->master_type = pa_xstrdup(pa_proplist_gets(device->proplist, 
mastertype));
+    return vol;
+}
+
 /* Create a property list for this ucm device */
 static int ucm_get_device_property(
         pa_alsa_ucm_device *device,
@@ -258,6 +292,7 @@ static int ucm_get_device_property(
     int err;
     uint32_t ui;
     int n_confdev, n_suppdev;
+    pa_alsa_ucm_volume *vol;
 
     for (i = 0; item[i].id; i++) {
         id = pa_sprintf_malloc("=%s/%s", item[i].id, device_name);
@@ -340,12 +375,14 @@ static int ucm_get_device_property(
                 pa_log_debug("UCM playback priority %s for device %s error", 
value, device_name);
         }
 
-        value = ucm_get_mixer_id(device,
-                                 PA_ALSA_PROP_UCM_PLAYBACK_MIXER_ELEM,
-                                 PA_ALSA_PROP_UCM_PLAYBACK_VOLUME,
-                                 "PlaybackVolume");
-        if (value)
-            pa_hashmap_put(device->playback_volumes, 
pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), (void 
*)value);
+        vol = ucm_get_mixer_volume(device,
+                                   PA_ALSA_PROP_UCM_PLAYBACK_MIXER_ELEM,
+                                   PA_ALSA_PROP_UCM_PLAYBACK_VOLUME,
+                                   "PlaybackVolume",
+                                   PA_ALSA_PROP_UCM_PLAYBACK_MASTER_ELEM,
+                                   PA_ALSA_PROP_UCM_PLAYBACK_MASTER_TYPE);
+        if (vol)
+            pa_hashmap_put(device->playback_volumes, 
pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), vol);
     }
 
     if (device->capture_channels) { /* source device */
@@ -368,12 +405,14 @@ static int ucm_get_device_property(
                 pa_log_debug("UCM capture priority %s for device %s error", 
value, device_name);
         }
 
-        value = ucm_get_mixer_id(device,
-                                 PA_ALSA_PROP_UCM_CAPTURE_MIXER_ELEM,
-                                 PA_ALSA_PROP_UCM_CAPTURE_VOLUME,
-                                 "CaptureVolume");
-        if (value)
-          pa_hashmap_put(device->capture_volumes, 
pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), (void 
*)value);
+        vol = ucm_get_mixer_volume(device,
+                                   PA_ALSA_PROP_UCM_CAPTURE_MIXER_ELEM,
+                                   PA_ALSA_PROP_UCM_CAPTURE_VOLUME,
+                                   "CaptureVolume",
+                                   PA_ALSA_PROP_UCM_CAPTURE_MASTER_ELEM,
+                                   PA_ALSA_PROP_UCM_CAPTURE_MASTER_TYPE);
+        if (vol)
+          pa_hashmap_put(device->capture_volumes, 
pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), vol);
     }
 
     if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device) || 
PA_UCM_CAPTURE_PRIORITY_UNSET(device)) {
@@ -478,9 +517,9 @@ static int ucm_get_devices(pa_alsa_ucm_verb *verb, 
snd_use_case_mgr_t *uc_mgr) {
         d->available = PA_AVAILABLE_UNKNOWN;
 
         d->playback_volumes = pa_hashmap_new_full(pa_idxset_string_hash_func, 
pa_idxset_string_compare_func, pa_xfree,
-                                                 pa_xfree);
+                                                  (pa_free_cb_t) 
ucm_volume_free);
         d->capture_volumes = pa_hashmap_new_full(pa_idxset_string_hash_func, 
pa_idxset_string_compare_func, pa_xfree,
-                                                pa_xfree);
+                                                 (pa_free_cb_t) 
ucm_volume_free);
 
         PA_LLIST_PREPEND(pa_alsa_ucm_device, verb->devices, d);
     }
@@ -819,9 +858,10 @@ static void ucm_add_port_combination(
     char *name, *desc;
     const char *dev_name;
     const char *direction;
-    const char *profile, *volume_element;
+    const char *profile;
     pa_alsa_ucm_device *sorted[num], *dev;
     pa_alsa_ucm_port_data *data;
+    pa_alsa_ucm_volume *vol;
     void *state;
 
     for (i = 0; i < num; i++)
@@ -892,21 +932,27 @@ static void ucm_add_port_combination(
              * ports. */
             data = PA_DEVICE_PORT_DATA(port);
 
-            PA_HASHMAP_FOREACH_KV(profile, volume_element, is_sink ? 
dev->playback_volumes : dev->capture_volumes, state) {
-                pa_alsa_path *path = pa_alsa_path_synthesize(volume_element,
+            PA_HASHMAP_FOREACH_KV(profile, vol, is_sink ? 
dev->playback_volumes : dev->capture_volumes, state) {
+                pa_alsa_path *path = pa_alsa_path_synthesize(vol->mixer_elem,
                                                              is_sink ? 
PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT);
 
                 if (!path)
-                    pa_log_warn("Failed to set up volume control: %s", 
volume_element);
+                    pa_log_warn("Failed to set up volume control: %s", 
vol->mixer_elem);
                 else {
+                    if (vol->master_elem) {
+                        pa_alsa_element *e = pa_alsa_element_get(path, 
vol->master_elem, false);
+                        e->switch_use = PA_ALSA_SWITCH_MUTE;
+                        e->volume_use = PA_ALSA_VOLUME_MERGE;
+                    }
+
                     pa_hashmap_put(data->paths, pa_xstrdup(profile), path);
 
                     /* Add path also to already created empty path set */
                     dev = sorted[0];
                     if (is_sink)
-                        
pa_hashmap_put(dev->playback_mapping->output_path_set->paths, 
pa_xstrdup(volume_element), path);
+                        
pa_hashmap_put(dev->playback_mapping->output_path_set->paths, 
pa_xstrdup(vol->mixer_elem), path);
                     else
-                        
pa_hashmap_put(dev->capture_mapping->input_path_set->paths, 
pa_xstrdup(volume_element), path);
+                        
pa_hashmap_put(dev->capture_mapping->input_path_set->paths, 
pa_xstrdup(vol->mixer_elem), path);
                 }
             }
         }
diff --git a/src/modules/alsa/alsa-ucm.h b/src/modules/alsa/alsa-ucm.h
index d8507a83615c..a6863abc05a6 100644
--- a/src/modules/alsa/alsa-ucm.h
+++ b/src/modules/alsa/alsa-ucm.h
@@ -60,6 +60,12 @@ typedef void snd_use_case_mgr_t;
 /** For devices: Playback mixer master type */
 #define PA_ALSA_PROP_UCM_PLAYBACK_MASTER_TYPE       
"alsa.ucm.playback.master.type"
 
+/** For devices: Playback mixer master identifier */
+#define PA_ALSA_PROP_UCM_PLAYBACK_MASTER_ID         
"alsa.ucm.playback.master.id"
+
+/** For devices: Playback mixer master type */
+#define PA_ALSA_PROP_UCM_PLAYBACK_MASTER_TYPE       
"alsa.ucm.playback.master.type"
+
 /** For devices: Playback priority */
 #define PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY          
"alsa.ucm.playback.priority"
 
@@ -87,6 +93,12 @@ typedef void snd_use_case_mgr_t;
 /** For devices: Capture mixer identifier */
 #define PA_ALSA_PROP_UCM_CAPTURE_MASTER_TYPE        
"alsa.ucm.capture.master.type"
 
+/** For devices: Capture mixer identifier */
+#define PA_ALSA_PROP_UCM_CAPTURE_MASTER_ID          
"alsa.ucm.capture.master.id"
+
+/** For devices: Capture mixer identifier */
+#define PA_ALSA_PROP_UCM_CAPTURE_MASTER_TYPE        
"alsa.ucm.capture.master.type"
+
 /** For devices: Capture priority */
 #define PA_ALSA_PROP_UCM_CAPTURE_PRIORITY           "alsa.ucm.capture.priority"
 
@@ -114,6 +126,7 @@ typedef struct pa_alsa_ucm_device pa_alsa_ucm_device;
 typedef struct pa_alsa_ucm_config pa_alsa_ucm_config;
 typedef struct pa_alsa_ucm_mapping_context pa_alsa_ucm_mapping_context;
 typedef struct pa_alsa_ucm_port_data pa_alsa_ucm_port_data;
+typedef struct pa_alsa_ucm_volume pa_alsa_ucm_volume;
 
 int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index);
 pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, 
pa_channel_map *default_channel_map);
@@ -246,4 +259,10 @@ struct pa_alsa_ucm_port_data {
     pa_alsa_path *path;
 };
 
+struct pa_alsa_ucm_volume {
+    char *mixer_elem;  /* mixer element identifier */
+    char *master_elem; /* master mixer element identifier */
+    char *master_type;
+};
+
 #endif
-- 
2.16.4

++++++ 0014-alsa-ucm-split-correctly-JackHWMute-device-names.patch ++++++
>From 156bd7742490d68701688572ec06f2c608f33db6 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <[email protected]>
Date: Tue, 3 Dec 2019 14:52:08 +0100
Subject: [PATCH] alsa-ucm: split correctly JackHWMute device names

Signed-off-by: Jaroslav Kysela <[email protected]>
---
 src/modules/alsa/alsa-ucm.c | 28 +++++++++++++++++++++++++++-
 1 file changed, 27 insertions(+), 1 deletion(-)

diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index 46d016541ddf..65b786caf95e 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -200,6 +200,32 @@ static void ucm_add_devices_to_idxset(
     }
 }
 
+/* Split a string into words. Like pa_split_spaces() but handle '' and "". */
+static char *ucm_split_devnames(const char *c, const char **state) {
+    const char *current = *state ? *state : c;
+    char h;
+    size_t l;
+
+    if (!*current || *c == 0)
+        return NULL;
+
+    current += strspn(current, "\n\r \t");
+    h = *current;
+    if (h == '\'' || h =='"') {
+        c = ++current;
+        for (l = 0; *c && *c != h; l++) c++;
+        if (*c != h)
+            return NULL;
+        *state = c + 1;
+    } else {
+        l = strcspn(current, "\n\r \t");
+        *state = current+l;
+    }
+
+    return pa_xstrndup(current, l);
+}
+
+
 static void ucm_volume_free(pa_alsa_ucm_volume *vol) {
     pa_assert(vol);
     pa_xfree(vol->mixer_elem);
@@ -1607,7 +1633,7 @@ static int ucm_create_profile(
             char *hw_mute_device_name;
             const char *state = NULL;
 
-            while ((hw_mute_device_name = pa_split_spaces(jack_hw_mute, 
&state))) {
+            while ((hw_mute_device_name = ucm_split_devnames(jack_hw_mute, 
&state))) {
                 pa_alsa_ucm_verb *verb2;
                 bool device_found = false;
 
-- 
2.16.4

++++++ 0015-alsa-ucm-fix-parsing-for-JackControl.patch ++++++
>From e04f14ebf3a0898dd2f665434524cc34cb267ddd Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <[email protected]>
Date: Tue, 3 Dec 2019 15:13:48 +0100
Subject: [PATCH] alsa-ucm: fix parsing for JackControl

Signed-off-by: Jaroslav Kysela <[email protected]>
---
 src/modules/alsa/alsa-ucm.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index 65b786caf95e..221fed719284 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -1534,6 +1534,22 @@ static pa_alsa_jack* ucm_get_jack(pa_alsa_ucm_config 
*ucm, pa_alsa_ucm_device *d
 
     jack_control = pa_proplist_gets(device->proplist, 
PA_ALSA_PROP_UCM_JACK_CONTROL);
     if (jack_control) {
+#if SND_LIB_VERSION >= 0x10201
+        snd_ctl_elem_id_t *ctl;
+        int err, index;
+        snd_ctl_elem_id_alloca(&ctl);
+        err = snd_use_case_parse_ctl_elem_id(ctl, "JackControl", jack_control);
+        if (err < 0)
+            return NULL;
+        jack_control = snd_ctl_elem_id_get_name(ctl);
+        index = snd_ctl_elem_id_get_index(ctl);
+        if (index > 0) {
+            pa_log("[%s] Invalid JackControl index value: \"%s\",%d", 
device_name, jack_control, index);
+            return NULL;
+        }
+#else
+#warning "Upgrade to alsa-lib 1.2.1!"
+#endif
         if (!pa_endswith(jack_control, " Jack")) {
             pa_log("[%s] Invalid JackControl value: \"%s\"", device_name, 
jack_control);
             return NULL;
-- 
2.16.4

++++++ 0016-alsa-ucm-add-comments-to-ucm_get_mixer_id.patch ++++++
>From f5c02dfcd821ab77fc7f91da985254a7bdb658ad Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <[email protected]>
Date: Wed, 4 Dec 2019 16:29:51 +0100
Subject: [PATCH] alsa-ucm: add comments to ucm_get_mixer_id()

Signed-off-by: Jaroslav Kysela <[email protected]>
---
 src/modules/alsa/alsa-ucm.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index 221fed719284..3ee271845c19 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -241,7 +241,7 @@ static char *ucm_get_mixer_id(
         const char *cprop,
         const char *cid)
 {
-#if SND_LIB_VERSION >= 0x10201
+#if SND_LIB_VERSION >= 0x10201 /* alsa-lib-1.2.1+ check */
     snd_ctl_elem_id_t *ctl;
     int err;
 #endif
@@ -249,13 +249,17 @@ static char *ucm_get_mixer_id(
     char *value2;
     int index;
 
+    /* mixer element as first, if it's found, return it without modifications 
*/
     value = pa_proplist_gets(device->proplist, mprop);
     if (value)
         return pa_xstrdup(value);
+    /* fallback, get the control element identifier */
+    /* and try to do some heuristic to determine the mixer element name */
     value = pa_proplist_gets(device->proplist, cprop);
     if (value == NULL)
         return NULL;
-#if SND_LIB_VERSION >= 0x10201
+#if SND_LIB_VERSION >= 0x10201 /* alsa-lib-1.2.1+ check */
+    /* The new parser may return also element index. */
     snd_ctl_elem_id_alloca(&ctl);
     err = snd_use_case_parse_ctl_elem_id(ctl, cid, value);
     if (err < 0)
-- 
2.16.4

++++++ 0017-alsa-ucm-validate-access-to-PA_DEVICE_PORT_DATA.patch ++++++
>From e6779ad229d5858f90f5f10c3796c9778f05c3fa Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <[email protected]>
Date: Wed, 4 Dec 2019 19:33:01 +0100
Subject: [PATCH] alsa-ucm: validate access to PA_DEVICE_PORT_DATA()

Signed-off-by: Jaroslav Kysela <[email protected]>
---
 src/modules/alsa/alsa-sink.c        | 3 ++-
 src/modules/alsa/alsa-source.c      | 3 +--
 src/modules/alsa/module-alsa-card.c | 6 +++++-
 3 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 0a5c92529af3..19e9efc3674b 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -1672,6 +1672,7 @@ static int sink_set_port_cb(pa_sink *s, pa_device_port 
*p) {
     pa_assert(u);
     pa_assert(p);
     pa_assert(u->mixer_handle);
+    pa_assert(!u->ucm_context);
 
     data = PA_DEVICE_PORT_DATA(p);
     pa_assert_se(u->mixer_path = data->path);
@@ -2688,7 +2689,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, 
const char*driver, pa_ca
      * pa_sink_suspend() between pa_sink_new() and pa_sink_put() would
      * otherwise work, but currently pa_sink_suspend() will crash if
      * pa_sink_put() hasn't been called. */
-    if (u->sink->active_port) {
+    if (u->sink->active_port && !u->ucm_context) {
         pa_alsa_port_data *port_data;
 
         port_data = PA_DEVICE_PORT_DATA(u->sink->active_port);
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index d186101720b8..34946b74c77f 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -1519,8 +1519,6 @@ static int source_set_port_ucm_cb(pa_source *s, 
pa_device_port *p) {
     struct userdata *u = s->userdata;
     pa_alsa_ucm_port_data *data;
 
-    data = PA_DEVICE_PORT_DATA(p);
-
     pa_assert(u);
     pa_assert(p);
     pa_assert(u->mixer_handle);
@@ -1545,6 +1543,7 @@ static int source_set_port_cb(pa_source *s, 
pa_device_port *p) {
     pa_assert(u);
     pa_assert(p);
     pa_assert(u->mixer_handle);
+    pa_assert(!u->ucm_context);
 
     data = PA_DEVICE_PORT_DATA(p);
     pa_assert_se(u->mixer_path = data->path);
diff --git a/src/modules/alsa/module-alsa-card.c 
b/src/modules/alsa/module-alsa-card.c
index e2a86bc1c68d..be260e4badab 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -538,6 +538,9 @@ static int hdmi_eld_changed(snd_mixer_elem_t *melem, 
unsigned int mask) {
     if (mask == SND_CTL_EVENT_MASK_REMOVE)
         return 0;
 
+    if (u->use_ucm)
+        return 0;
+
     p = find_port_with_eld_device(u->card->ports, device);
     if (p == NULL) {
         pa_log_error("Invalid device changed in ALSA: %d", device);
@@ -900,7 +903,8 @@ int pa__init(pa_module *m) {
      * results in an infinite loop of "fill buffer, handle underrun". To work
      * around this issue, the suspend_when_unavailable flag is used to stop
      * playback when the HDMI cable is unplugged. */
-    if (pa_safe_streq(pa_proplist_gets(data.proplist, "alsa.driver_name"), 
"snd_hdmi_lpe_audio")) {
+    if (!u->use_ucm &&
+        pa_safe_streq(pa_proplist_gets(data.proplist, "alsa.driver_name"), 
"snd_hdmi_lpe_audio")) {
         pa_device_port *port;
         void *state;
 
-- 
2.16.4

++++++ 0018-alsa-Skip-resume-PCM-if-hardware-doesn-t-support-it.patch ++++++
>From 734a00c849815a45697970d593068c301a04ebbb Mon Sep 17 00:00:00 2001
From: Kai-Heng Feng <[email protected]>
Date: Tue, 10 Dec 2019 16:16:18 +0800
Subject: [PATCH] alsa: Skip resume PCM if hardware doesn't support it

Hardwares without SNDRV_PCM_INFO_RESUME capability, like USB Audio,
don't support snd_pcm_resume() when it's in suspended state.

Let's use snd_pcm_hw_params_can_resume() to check hardware's capability
before snd_pcm_resume() attempt. If it doesn't support resume, just go
to snd_pcm_drop() to leave suspended state directly.
---
 src/modules/alsa/alsa-util.c | 28 +++++++++++++++++++---------
 1 file changed, 19 insertions(+), 9 deletions(-)

diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index bd0a47e5072c..a14b061118e6 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -1066,6 +1066,7 @@ void pa_alsa_init_proplist_ctl(pa_proplist *p, const char 
*name) {
 
 int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents) {
     snd_pcm_state_t state;
+    snd_pcm_hw_params_t *hwparams;
     int err;
 
     pa_assert(pcm);
@@ -1103,16 +1104,25 @@ int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int 
revents) {
             break;
 
         case SND_PCM_STATE_SUSPENDED:
-            /* Retry resume 3 times before giving up, then fallback to 
restarting the stream. */
-            for (int i = 0; i < 3; i++) {
-                if ((err = snd_pcm_resume(pcm)) == 0)
-                    return 0;
-                if (err != -EAGAIN)
-                    break;
-                pa_msleep(25);
+            snd_pcm_hw_params_alloca(&hwparams);
+
+            if ((err = snd_pcm_hw_params_any(pcm, hwparams)) < 0) {
+               pa_log_debug("snd_pcm_hw_params_any() failed: %s", 
pa_alsa_strerror(err));
+               return -1;
             }
-            pa_log_warn("Could not recover alsa device from SUSPENDED state, 
trying to restart PCM");
-            /* Fall through */
+
+            if (snd_pcm_hw_params_can_resume(hwparams)) {
+                /* Retry resume 3 times before giving up, then fallback to 
restarting the stream. */
+                for (int i = 0; i < 3; i++) {
+                    if ((err = snd_pcm_resume(pcm)) == 0)
+                        return 0;
+                    if (err != -EAGAIN)
+                        break;
+                    pa_msleep(25);
+                }
+                pa_log_warn("Could not recover alsa device from SUSPENDED 
state, trying to restart PCM");
+           }
+           /* Fall through */
 
         default:
 
-- 
2.16.4

++++++ 0019-alsa-ucm-parse-correctly-the-device-values.patch ++++++
>From 4c64f73c97c7f77426ee838f47fc7bad6a4563b6 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <[email protected]>
Date: Fri, 6 Dec 2019 21:51:47 +0100
Subject: [PATCH] alsa-ucm: parse correctly the device values

The UCM library is used to get the fallback values from the verbs
and the defaults section. There is no reason to duplicate this code
inside application.

Signed-off-by: Jaroslav Kysela <[email protected]>
---
 src/modules/alsa/alsa-ucm.c | 55 ++++++---------------------------------------
 1 file changed, 7 insertions(+), 48 deletions(-)

diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index 3ee271845c19..ac1b71e94022 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -141,30 +141,6 @@ static struct ucm_info dev_info[] = {
     {NULL, 0}
 };
 
-/* UCM profile properties - The verb data is store so it can be used to fill
- * the new profiles properties */
-static int ucm_get_property(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t 
*uc_mgr, const char *verb_name) {
-    const char *value;
-    char *id;
-    int i;
-
-    for (i = 0; item[i].id; i++) {
-        int err;
-
-        id = pa_sprintf_malloc("=%s//%s", item[i].id, verb_name);
-        err = snd_use_case_get(uc_mgr, id, &value);
-        pa_xfree(id);
-        if (err < 0)
-            continue;
-
-        pa_log_debug("Got %s for verb %s: %s", item[i].id, verb_name, value);
-        pa_proplist_sets(verb->proplist, item[i].property, value);
-        free((void*)value);
-    }
-
-    return 0;
-};
-
 static int ucm_device_exists(pa_idxset *idxset, pa_alsa_ucm_device *dev) {
     pa_alsa_ucm_device *d;
     uint32_t idx;
@@ -325,7 +301,7 @@ static int ucm_get_device_property(
     pa_alsa_ucm_volume *vol;
 
     for (i = 0; item[i].id; i++) {
-        id = pa_sprintf_malloc("=%s/%s", item[i].id, device_name);
+        id = pa_sprintf_malloc("%s/%s", item[i].id, device_name);
         err = snd_use_case_get(uc_mgr, id, &value);
         pa_xfree(id);
         if (err < 0)
@@ -347,14 +323,8 @@ static int ucm_get_device_property(
 
         /* get pcm */
         value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_SINK);
-        if (!value) { /* take pcm from verb playback default */
-            value = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_SINK);
-            if (value) {
-                pa_log_debug("UCM playback device %s fetch pcm from verb 
default %s", device_name, value);
-                pa_proplist_sets(device->proplist, PA_ALSA_PROP_UCM_SINK, 
value);
-            } else
-                pa_log("UCM playback device %s fetch pcm failed", device_name);
-        }
+        if (!value) /* take pcm from verb playback default */
+            pa_log("UCM playback device %s fetch pcm failed", device_name);
     }
 
     value = pa_proplist_gets(device->proplist, 
PA_ALSA_PROP_UCM_CAPTURE_CHANNELS);
@@ -367,14 +337,8 @@ static int ucm_get_device_property(
 
         /* get pcm */
         value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_SOURCE);
-        if (!value) { /* take pcm from verb capture default */
-            value = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_SOURCE);
-            if (value) {
-                pa_log_debug("UCM capture device %s fetch pcm from verb 
default %s", device_name, value);
-                pa_proplist_sets(device->proplist, PA_ALSA_PROP_UCM_SOURCE, 
value);
-            } else
-                pa_log("UCM capture device %s fetch pcm failed", device_name);
-        }
+        if (!value) /* take pcm from verb capture default */
+            pa_log("UCM capture device %s fetch pcm failed", device_name);
     }
 
     if (device->playback_channels == 0 && device->capture_channels == 0) {
@@ -387,8 +351,7 @@ static int ucm_get_device_property(
     /* get rate and priority of device */
     if (device->playback_channels) { /* sink device */
         /* get rate */
-        if ((value = pa_proplist_gets(device->proplist, 
PA_ALSA_PROP_UCM_PLAYBACK_RATE)) ||
-            (value = pa_proplist_gets(verb->proplist, 
PA_ALSA_PROP_UCM_PLAYBACK_RATE))) {
+        if ((value = pa_proplist_gets(device->proplist, 
PA_ALSA_PROP_UCM_PLAYBACK_RATE))) {
             if (pa_atou(value, &ui) == 0 && pa_sample_rate_valid(ui)) {
                 pa_log_debug("UCM playback device %s rate %d", device_name, 
ui);
                 device->playback_rate = ui;
@@ -417,8 +380,7 @@ static int ucm_get_device_property(
 
     if (device->capture_channels) { /* source device */
         /* get rate */
-        if ((value = pa_proplist_gets(device->proplist, 
PA_ALSA_PROP_UCM_CAPTURE_RATE)) ||
-            (value = pa_proplist_gets(verb->proplist, 
PA_ALSA_PROP_UCM_CAPTURE_RATE))) {
+        if ((value = pa_proplist_gets(device->proplist, 
PA_ALSA_PROP_UCM_CAPTURE_RATE))) {
             if (pa_atou(value, &ui) == 0 && pa_sample_rate_valid(ui)) {
                 pa_log_debug("UCM capture device %s rate %d", device_name, ui);
                 device->capture_rate = ui;
@@ -796,9 +758,6 @@ int pa_alsa_ucm_get_verb(snd_use_case_mgr_t *uc_mgr, const 
char *verb_name, cons
     if (err < 0)
         pa_log("No UCM modifiers for verb %s", verb_name);
 
-    /* Verb properties */
-    ucm_get_property(verb, uc_mgr, verb_name);
-
     PA_LLIST_FOREACH(d, verb->devices) {
         const char *dev_name = pa_proplist_gets(d->proplist, 
PA_ALSA_PROP_UCM_NAME);
 
-- 
2.16.4

++++++ 0020-alsa-ucm-do-not-try-to-use-UCM-device-name-as-jack-n.patch ++++++
>From ef1df946274a0499e1fa631a8b6680c23c4eb723 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <[email protected]>
Date: Fri, 6 Dec 2019 15:43:04 +0100
Subject: [PATCH] alsa-ucm: do not try to use UCM device name as jack name by
 default

Remove the implicit rule. It is perfectly ok to have the jack with
the same name for another I/O in the driver. Trust only the
value obtained from UCM.

Signed-off-by: Jaroslav Kysela <[email protected]>
---
 src/modules/alsa/alsa-ucm.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index ac1b71e94022..95f1a47f8b61 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -1522,9 +1522,8 @@ static pa_alsa_jack* ucm_get_jack(pa_alsa_ucm_config 
*ucm, pa_alsa_ucm_device *d
          * end, so drop the trailing " Jack". */
         name = pa_xstrndup(jack_control, strlen(jack_control) - 5);
     } else {
-        /* The jack control hasn't been explicitly configured - try a jack name
-         * that is the same as the device name. */
-        name = pa_xstrdup(device_name);
+        /* The jack control hasn't been explicitly configured, fail. */
+        return NULL;
     }
 
     PA_LLIST_FOREACH(j, ucm->jacks)
@@ -1603,7 +1602,8 @@ static int ucm_create_profile(
         ucm_create_mapping(ucm, ps, p, dev, verb_name, name, sink, source);
 
         jack = ucm_get_jack(ucm, dev);
-        device_set_jack(dev, jack);
+        if (jack)
+            device_set_jack(dev, jack);
 
         /* JackHWMute contains a list of device names. Each listed device must
          * be associated with the jack object that we just created. */
-- 
2.16.4

++++++ 0021-alsa-util-do-not-try-to-guess-the-mixer-name-from-th.patch ++++++
>From d8200ee805ed6b508a8174031080b1d98a7c27b3 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <[email protected]>
Date: Fri, 6 Dec 2019 16:05:07 +0100
Subject: [PATCH] alsa-util: do not try to guess the mixer name from the PCM
 name

This is just invalid. It results to an error in almost all cases.
The hw:<number> scheme is sufficient to get the right card mixer.

Signed-off-by: Jaroslav Kysela <[email protected]>
---
 src/modules/alsa/alsa-ucm.c  |  2 ++
 src/modules/alsa/alsa-util.c | 26 +++++++-------------------
 2 files changed, 9 insertions(+), 19 deletions(-)

diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index 95f1a47f8b61..45eb83085b38 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -1725,6 +1725,8 @@ static void ucm_mapping_jack_probe(pa_alsa_mapping *m) {
     PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
         bool has_control;
 
+        if (!dev->jack)
+            continue;
         has_control = pa_alsa_mixer_find(mixer_handle, dev->jack->alsa_name, 
0) != NULL;
         pa_alsa_jack_set_has_control(dev->jack, has_control);
         pa_log_info("UCM jack %s has_control=%d", dev->jack->name, 
dev->jack->has_control);
diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index a14b061118e6..54fe1361148a 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -1743,7 +1743,6 @@ snd_mixer_t *pa_alsa_open_mixer(int alsa_card_index, char 
**ctl_device) {
 snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, char **ctl_device) {
     int err;
     snd_mixer_t *m;
-    const char *dev;
     snd_pcm_info_t* info;
     snd_pcm_info_alloca(&info);
 
@@ -1754,15 +1753,6 @@ snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, 
char **ctl_device) {
         return NULL;
     }
 
-    /* First, try by name */
-    if ((dev = snd_pcm_name(pcm)))
-        if (prepare_mixer(m, dev) >= 0) {
-            if (ctl_device)
-                *ctl_device = pa_xstrdup(dev);
-
-            return m;
-        }
-
     /* Then, try by card index */
     if (snd_pcm_info(pcm, info) >= 0) {
         char *md;
@@ -1771,17 +1761,15 @@ snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, 
char **ctl_device) {
         if ((card_idx = snd_pcm_info_get_card(info)) >= 0) {
 
             md = pa_sprintf_malloc("hw:%i", card_idx);
+            if (prepare_mixer(m, md) >= 0) {
 
-            if (!dev || !pa_streq(dev, md))
-                if (prepare_mixer(m, md) >= 0) {
+                if (ctl_device)
+                    *ctl_device = md;
+                else
+                    pa_xfree(md);
 
-                    if (ctl_device)
-                        *ctl_device = md;
-                    else
-                        pa_xfree(md);
-
-                    return m;
-                }
+                return m;
+            }
 
             pa_xfree(md);
         }
-- 
2.16.4

++++++ 0022-alsa-ucm-add-control-and-mixer-device-items.patch ++++++
>From ddd0fdb9970b920ef95e33cfe50a1e492be9d60b Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <[email protected]>
Date: Fri, 6 Dec 2019 20:33:45 +0100
Subject: [PATCH] alsa-ucm: add control and mixer device items

Signed-off-by: Jaroslav Kysela <[email protected]>
---
 src/modules/alsa/alsa-ucm.c |  5 +++++
 src/modules/alsa/alsa-ucm.h | 15 +++++++++++++++
 2 files changed, 20 insertions(+)

diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index 45eb83085b38..2a594b4df546 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -90,16 +90,20 @@ static void ucm_port_update_available(pa_alsa_ucm_port_data 
*port);
 static struct ucm_items item[] = {
     {"PlaybackPCM", PA_ALSA_PROP_UCM_SINK},
     {"CapturePCM", PA_ALSA_PROP_UCM_SOURCE},
+    {"PlaybackCTL", PA_ALSA_PROP_UCM_PLAYBACK_CTL_DEVICE},
     {"PlaybackVolume", PA_ALSA_PROP_UCM_PLAYBACK_VOLUME},
     {"PlaybackSwitch", PA_ALSA_PROP_UCM_PLAYBACK_SWITCH},
+    {"PlaybackMixer", PA_ALSA_PROP_UCM_PLAYBACK_MIXER_DEVICE},
     {"PlaybackMixerElem", PA_ALSA_PROP_UCM_PLAYBACK_MIXER_ELEM},
     {"PlaybackMasterElem", PA_ALSA_PROP_UCM_PLAYBACK_MASTER_ELEM},
     {"PlaybackMasterType", PA_ALSA_PROP_UCM_PLAYBACK_MASTER_TYPE},
     {"PlaybackPriority", PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY},
     {"PlaybackRate", PA_ALSA_PROP_UCM_PLAYBACK_RATE},
     {"PlaybackChannels", PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS},
+    {"CaptureCTL", PA_ALSA_PROP_UCM_CAPTURE_CTL_DEVICE},
     {"CaptureVolume", PA_ALSA_PROP_UCM_CAPTURE_VOLUME},
     {"CaptureSwitch", PA_ALSA_PROP_UCM_CAPTURE_SWITCH},
+    {"CaptureMixer", PA_ALSA_PROP_UCM_CAPTURE_MIXER_DEVICE},
     {"CaptureMixerElem", PA_ALSA_PROP_UCM_CAPTURE_MIXER_ELEM},
     {"CaptureMasterElem", PA_ALSA_PROP_UCM_CAPTURE_MASTER_ELEM},
     {"CaptureMasterType", PA_ALSA_PROP_UCM_CAPTURE_MASTER_TYPE},
@@ -107,6 +111,7 @@ static struct ucm_items item[] = {
     {"CaptureRate", PA_ALSA_PROP_UCM_CAPTURE_RATE},
     {"CaptureChannels", PA_ALSA_PROP_UCM_CAPTURE_CHANNELS},
     {"TQ", PA_ALSA_PROP_UCM_QOS},
+    {"JackCTL", PA_ALSA_PROP_UCM_JACK_DEVICE},
     {"JackControl", PA_ALSA_PROP_UCM_JACK_CONTROL},
     {"JackHWMute", PA_ALSA_PROP_UCM_JACK_HW_MUTE},
     {NULL, NULL},
diff --git a/src/modules/alsa/alsa-ucm.h b/src/modules/alsa/alsa-ucm.h
index a6863abc05a6..014bc334ad67 100644
--- a/src/modules/alsa/alsa-ucm.h
+++ b/src/modules/alsa/alsa-ucm.h
@@ -45,12 +45,18 @@ typedef void snd_use_case_mgr_t;
 /** For devices: Playback roles */
 #define PA_ALSA_PROP_UCM_PLAYBACK_ROLES             "alsa.ucm.playback.roles"
 
+/** For devices: Playback control device name  */
+#define PA_ALSA_PROP_UCM_PLAYBACK_CTL_DEVICE        "alsa.ucm.playback.ctldev"
+
 /** For devices: Playback control volume ID string. e.g PlaybackVolume */
 #define PA_ALSA_PROP_UCM_PLAYBACK_VOLUME            "alsa.ucm.playback.volume"
 
 /** For devices: Playback switch e.g PlaybackSwitch */
 #define PA_ALSA_PROP_UCM_PLAYBACK_SWITCH            "alsa.ucm.playback.switch"
 
+/** For devices: Playback mixer device name  */
+#define PA_ALSA_PROP_UCM_PLAYBACK_MIXER_DEVICE      
"alsa.ucm.playback.mixer.device"
+
 /** For devices: Playback mixer identifier */
 #define PA_ALSA_PROP_UCM_PLAYBACK_MIXER_ELEM        
"alsa.ucm.playback.mixer.element"
 
@@ -78,12 +84,18 @@ typedef void snd_use_case_mgr_t;
 /** For devices: Capture roles */
 #define PA_ALSA_PROP_UCM_CAPTURE_ROLES              "alsa.ucm.capture.roles"
 
+/** For devices: Capture control device name  */
+#define PA_ALSA_PROP_UCM_CAPTURE_CTL_DEVICE         "alsa.ucm.capture.ctldev"
+
 /** For devices: Capture controls volume ID string. e.g CaptureVolume */
 #define PA_ALSA_PROP_UCM_CAPTURE_VOLUME             "alsa.ucm.capture.volume"
 
 /** For devices: Capture switch e.g CaptureSwitch */
 #define PA_ALSA_PROP_UCM_CAPTURE_SWITCH             "alsa.ucm.capture.switch"
 
+/** For devices: Capture mixer device name  */
+#define PA_ALSA_PROP_UCM_CAPTURE_MIXER_DEVICE       
"alsa.ucm.capture.mixer.device"
+
 /** For devices: Capture mixer identifier */
 #define PA_ALSA_PROP_UCM_CAPTURE_MIXER_ELEM         
"alsa.ucm.capture.mixer.element"
 
@@ -114,6 +126,9 @@ typedef void snd_use_case_mgr_t;
 /** For devices: The modifier (if any) that this device corresponds to */
 #define PA_ALSA_PROP_UCM_MODIFIER "alsa.ucm.modifier"
 
+/* Corresponds to the "JackCTL" UCM value. */
+#define PA_ALSA_PROP_UCM_JACK_DEVICE               "alsa.ucm.jack_device"
+
 /* Corresponds to the "JackControl" UCM value. */
 #define PA_ALSA_PROP_UCM_JACK_CONTROL               "alsa.ucm.jack_control"
 
-- 
2.16.4

++++++ 0023-alsa-ucm-get-the-mixer-names-from-ucm-don-t-guess.patch ++++++
>From e438382a51f7e0d04fb9439da2f45c183e958e0f Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <[email protected]>
Date: Fri, 6 Dec 2019 21:32:21 +0100
Subject: [PATCH] alsa-ucm: get the mixer names from ucm, don't guess

Signed-off-by: Jaroslav Kysela <[email protected]>
---
 src/modules/alsa/alsa-mixer.h |  1 +
 src/modules/alsa/alsa-ucm.c   | 96 +++++++++++++++++++++++++++++++++++--------
 src/modules/alsa/alsa-util.c  | 26 ++++++++----
 3 files changed, 97 insertions(+), 26 deletions(-)

diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h
index 0b634f2f2be1..7864a2c1252f 100644
--- a/src/modules/alsa/alsa-mixer.h
+++ b/src/modules/alsa/alsa-mixer.h
@@ -364,6 +364,7 @@ void pa_alsa_profile_set_free(pa_alsa_profile_set *s);
 void pa_alsa_profile_set_dump(pa_alsa_profile_set *s);
 void pa_alsa_profile_set_drop_unsupported(pa_alsa_profile_set *s);
 
+snd_mixer_t *pa_alsa_open_mixer_by_name(const char *dev);
 snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, char **ctl_device);
 
 pa_alsa_fdlist *pa_alsa_fdlist_new(void);
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index 2a594b4df546..d02adab650fe 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -289,6 +289,31 @@ static pa_alsa_ucm_volume *ucm_get_mixer_volume(
     return vol;
 }
 
+/* Get the ALSA mixer device for the UCM device */
+static const char *get_mixer_device(pa_alsa_ucm_device *dev, bool is_sink)
+{
+    const char *dev_name;
+    
+    if (is_sink) {
+        dev_name = pa_proplist_gets(dev->proplist, 
PA_ALSA_PROP_UCM_PLAYBACK_MIXER_DEVICE);
+        if (!dev_name)
+            dev_name = pa_proplist_gets(dev->proplist, 
PA_ALSA_PROP_UCM_PLAYBACK_CTL_DEVICE);
+    } else {
+        dev_name = pa_proplist_gets(dev->proplist, 
PA_ALSA_PROP_UCM_CAPTURE_MIXER_DEVICE);
+        if (!dev_name)
+            dev_name = pa_proplist_gets(dev->proplist, 
PA_ALSA_PROP_UCM_CAPTURE_CTL_DEVICE);
+    }
+    return dev_name;
+}
+
+/* Get the ALSA mixer device for the UCM jack */
+static const char *get_jack_mixer_device(pa_alsa_ucm_device *dev, bool 
is_sink) {
+    const char *dev_name = pa_proplist_gets(dev->proplist, 
PA_ALSA_PROP_UCM_JACK_DEVICE);
+    if (!dev_name)
+        return get_mixer_device(dev, is_sink);
+    return dev_name;
+}
+
 /* Create a property list for this ucm device */
 static int ucm_get_device_property(
         pa_alsa_ucm_device *device,
@@ -795,22 +820,41 @@ static int pa_alsa_ucm_device_cmp(const void *a, const 
void *b) {
     return strcmp(pa_proplist_gets(d1->proplist, PA_ALSA_PROP_UCM_NAME), 
pa_proplist_gets(d2->proplist, PA_ALSA_PROP_UCM_NAME));
 }
 
-static void probe_volumes(pa_hashmap *hash, snd_pcm_t *pcm_handle, bool 
ignore_dB) {
+static void probe_volumes(pa_hashmap *hash, bool is_sink, snd_pcm_t 
*pcm_handle, bool ignore_dB) {
     pa_device_port *port;
     pa_alsa_path *path;
     pa_alsa_ucm_port_data *data;
-    snd_mixer_t *mixer_handle;
-    const char *profile;
+    pa_alsa_ucm_device *dev;
+    snd_mixer_t *mixer_handle = NULL;
+    const char *profile, *mdev_opened = NULL, *mdev, *mdev2;
     void *state, *state2;
-
-    if (!(mixer_handle = pa_alsa_open_mixer_for_pcm(pcm_handle, NULL))) {
-        pa_log_error("Failed to find a working mixer device.");
-        goto fail;
-    }
+    int idx;
 
     PA_HASHMAP_FOREACH(port, hash, state) {
         data = PA_DEVICE_PORT_DATA(port);
 
+        mdev = NULL;
+        PA_DYNARRAY_FOREACH(dev, data->devices, idx) {
+            mdev2 = get_mixer_device(dev, is_sink);
+            if (mdev && !pa_streq(mdev, mdev2)) {
+                pa_log_error("Two mixer device names found ('%s', '%s'), using 
s/w volume", mdev, mdev2);
+                goto fail;
+            }
+            mdev = mdev2;
+        }
+
+        if (!mdev_opened || !pa_streq(mdev_opened, mdev)) {
+            if (mixer_handle) {
+                snd_mixer_close(mixer_handle);
+                mdev_opened = NULL;
+            }
+            if (!(mixer_handle = pa_alsa_open_mixer_by_name(mdev))) {
+                pa_log_error("Failed to find a working mixer device (%s).", 
mdev);
+                goto fail;
+            }
+            mdev_opened = mdev;
+        }
+
         PA_HASHMAP_FOREACH_KV(profile, path, data->paths, state2) {
             if (pa_alsa_path_probe(path, NULL, mixer_handle, ignore_dB) < 0) {
                 pa_log_warn("Could not probe path: %s, using s/w volume", 
data->path->name);
@@ -823,7 +867,8 @@ static void probe_volumes(pa_hashmap *hash, snd_pcm_t 
*pcm_handle, bool ignore_d
         }
     }
 
-    snd_mixer_close(mixer_handle);
+    if (mixer_handle)
+        snd_mixer_close(mixer_handle);
 
     return;
 
@@ -1149,7 +1194,7 @@ void pa_alsa_ucm_add_ports(
     pa_alsa_ucm_add_ports_combination(*p, context, is_sink, card->ports, NULL, 
card->core);
 
     /* now set up volume paths if any */
-    probe_volumes(*p, pcm_handle, ignore_dB);
+    probe_volumes(*p, is_sink, pcm_handle, ignore_dB);
 
     /* then set property PA_PROP_DEVICE_INTENDED_ROLES */
     merged_roles = pa_xstrdup(pa_proplist_gets(proplist, 
PA_PROP_DEVICE_INTENDED_ROLES));
@@ -1716,28 +1761,43 @@ static void profile_finalize_probing(pa_alsa_profile 
*p) {
 }
 
 static void ucm_mapping_jack_probe(pa_alsa_mapping *m) {
-    snd_pcm_t *pcm_handle;
-    snd_mixer_t *mixer_handle;
+    snd_mixer_t *mixer_handle = NULL;
     pa_alsa_ucm_mapping_context *context = &m->ucm_context;
     pa_alsa_ucm_device *dev;
+    bool is_sink = m->direction == PA_ALSA_DIRECTION_OUTPUT;
+    const char *mdev_opened = NULL, *mdev;
     uint32_t idx;
 
-    pcm_handle = m->direction == PA_ALSA_DIRECTION_OUTPUT ? m->output_pcm : 
m->input_pcm;
-    mixer_handle = pa_alsa_open_mixer_for_pcm(pcm_handle, NULL);
-    if (!mixer_handle)
-        return;
-
     PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
         bool has_control;
 
         if (!dev->jack)
             continue;
+
+        mdev = get_jack_mixer_device(dev, is_sink);
+        if (mdev == NULL) {
+            pa_log_error("Unable to determine mixer device for jack %s", 
dev->jack->name);
+            continue;
+        }
+
+        if (!mdev_opened || !pa_streq(mdev_opened, mdev)) {
+            if (mixer_handle) {
+                snd_mixer_close(mixer_handle);
+                mdev_opened = NULL;
+            }
+            mixer_handle = pa_alsa_open_mixer_by_name(mdev);
+            if (!mixer_handle)
+                continue;
+            mdev_opened = mdev;
+        }
+
         has_control = pa_alsa_mixer_find(mixer_handle, dev->jack->alsa_name, 
0) != NULL;
         pa_alsa_jack_set_has_control(dev->jack, has_control);
         pa_log_info("UCM jack %s has_control=%d", dev->jack->name, 
dev->jack->has_control);
     }
 
-    snd_mixer_close(mixer_handle);
+    if (mixer_handle)
+        snd_mixer_close(mixer_handle);
 }
 
 static void ucm_probe_profile_set(pa_alsa_ucm_config *ucm, pa_alsa_profile_set 
*ps) {
diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index 54fe1361148a..2df2258d85af 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -1740,20 +1740,31 @@ snd_mixer_t *pa_alsa_open_mixer(int alsa_card_index, 
char **ctl_device) {
     return NULL;
 }
 
-snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, char **ctl_device) {
+snd_mixer_t *pa_alsa_open_mixer_by_name(const char *dev) {
     int err;
     snd_mixer_t *m;
-    snd_pcm_info_t* info;
-    snd_pcm_info_alloca(&info);
 
-    pa_assert(pcm);
+    pa_assert(dev);
 
     if ((err = snd_mixer_open(&m, 0)) < 0) {
         pa_log("Error opening mixer: %s", pa_alsa_strerror(err));
         return NULL;
     }
 
-    /* Then, try by card index */
+    if (prepare_mixer(m, dev) >= 0)
+        return m;
+
+    snd_mixer_close(m);
+    return NULL;
+}
+
+snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, char **ctl_device) {
+    snd_mixer_t *m;
+    snd_pcm_info_t* info;
+    snd_pcm_info_alloca(&info);
+
+    pa_assert(pcm);
+
     if (snd_pcm_info(pcm, info) >= 0) {
         char *md;
         int card_idx;
@@ -1761,8 +1772,8 @@ snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, 
char **ctl_device) {
         if ((card_idx = snd_pcm_info_get_card(info)) >= 0) {
 
             md = pa_sprintf_malloc("hw:%i", card_idx);
-            if (prepare_mixer(m, md) >= 0) {
-
+            m = pa_alsa_open_mixer_by_name(md);
+            if (m) {
                 if (ctl_device)
                     *ctl_device = md;
                 else
@@ -1775,7 +1786,6 @@ snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, 
char **ctl_device) {
         }
     }
 
-    snd_mixer_close(m);
     return NULL;
 }
 
-- 
2.16.4

++++++ 0024-alsa-ucm-use-the-proper-mixer-name-for-ucm-pcm-sink-.patch ++++++
>From dacfcbb09c9d91ca20dedfa449da37f0f7e3953f Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <[email protected]>
Date: Sat, 7 Dec 2019 11:50:13 +0100
Subject: [PATCH] alsa-ucm: use the proper mixer name for ucm pcm sink/source

Signed-off-by: Jaroslav Kysela <[email protected]>
---
 src/modules/alsa/alsa-sink.c   |  9 ++++++++-
 src/modules/alsa/alsa-source.c | 10 +++++++++-
 src/modules/alsa/alsa-ucm.c    |  9 +++++++--
 3 files changed, 24 insertions(+), 4 deletions(-)

diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 19e9efc3674b..5ea4b2597449 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -2083,11 +2083,18 @@ static void set_sink_name(pa_sink_new_data *data, 
pa_modargs *ma, const char *de
 }
 
 static void find_mixer(struct userdata *u, pa_alsa_mapping *mapping, const 
char *element, bool ignore_dB) {
+    const char *mdev;
 
     if (!mapping && !element)
         return;
 
-    if (!(u->mixer_handle = pa_alsa_open_mixer_for_pcm(u->pcm_handle, 
&u->control_device))) {
+    mdev = pa_proplist_gets(mapping->proplist, "alsa.mixer_device");
+    if (mdev) {
+        u->mixer_handle = pa_alsa_open_mixer_by_name(mdev);
+    } else {
+        u->mixer_handle = pa_alsa_open_mixer_for_pcm(u->pcm_handle, 
&u->control_device);
+    }
+    if (!mdev) {
         pa_log_info("Failed to find a working mixer device.");
         return;
     }
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index 34946b74c77f..bd78d45e2a5b 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -1789,10 +1789,18 @@ static void set_source_name(pa_source_new_data *data, 
pa_modargs *ma, const char
 }
 
 static void find_mixer(struct userdata *u, pa_alsa_mapping *mapping, const 
char *element, bool ignore_dB) {
+    const char *mdev;
+
     if (!mapping && !element)
         return;
 
-    if (!(u->mixer_handle = pa_alsa_open_mixer_for_pcm(u->pcm_handle, 
&u->control_device))) {
+    mdev = pa_proplist_gets(mapping->proplist, "alsa.mixer_device");
+    if (mdev) {
+        u->mixer_handle = pa_alsa_open_mixer_by_name(mdev);
+    } else {
+        u->mixer_handle = pa_alsa_open_mixer_for_pcm(u->pcm_handle, 
&u->control_device);
+    }
+    if (!mdev) {
         pa_log_info("Failed to find a working mixer device.");
         return;
     }
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index d02adab650fe..d1ca62d28619 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -1339,7 +1339,8 @@ static void ucm_add_mapping(pa_alsa_profile *p, 
pa_alsa_mapping *m) {
 
 static void alsa_mapping_add_ucm_device(pa_alsa_mapping *m, pa_alsa_ucm_device 
*device) {
     char *cur_desc;
-    const char *new_desc;
+    const char *new_desc, *mdev;
+    bool is_sink = m->direction == PA_ALSA_DIRECTION_OUTPUT;
 
     pa_idxset_put(m->ucm_context.ucm_devices, device, NULL);
 
@@ -1355,10 +1356,14 @@ static void alsa_mapping_add_ucm_device(pa_alsa_mapping 
*m, pa_alsa_ucm_device *
     m->description = m->description ? m->description : pa_xstrdup("");
 
     /* save mapping to ucm device */
-    if (m->direction == PA_ALSA_DIRECTION_OUTPUT)
+    if (is_sink)
         device->playback_mapping = m;
     else
         device->capture_mapping = m;
+
+    mdev = get_mixer_device(device, is_sink);
+    if (mdev)
+        pa_proplist_sets(m->proplist, "alsa.mixer_device", mdev);
 }
 
 static void alsa_mapping_add_ucm_modifier(pa_alsa_mapping *m, 
pa_alsa_ucm_modifier *modifier) {
-- 
2.16.4

++++++ 0025-alsa-mixer-handle-interface-type-CARD-PCM-for-mixer-.patch ++++++
>From f18b0c3402f5e1f7db9d0a42c6e10cfe1f212da3 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <[email protected]>
Date: Sat, 7 Dec 2019 17:54:04 +0100
Subject: [PATCH] alsa-mixer: handle interface type (CARD,PCM) for mixer
 element lookups

Signed-off-by: Jaroslav Kysela <[email protected]>
---
 src/modules/alsa/alsa-mixer.c       |  2 +-
 src/modules/alsa/alsa-ucm.c         |  2 +-
 src/modules/alsa/alsa-util.c        | 18 +++++++++++++++++-
 src/modules/alsa/alsa-util.h        |  3 ++-
 src/modules/alsa/module-alsa-card.c |  4 ++--
 5 files changed, 23 insertions(+), 6 deletions(-)

diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index ed06f42d86da..094cff817df7 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -1907,7 +1907,7 @@ static int jack_probe(pa_alsa_jack *j, pa_alsa_mapping 
*mapping, snd_mixer_t *m)
         j->append_pcm_to_name = false;
     }
 
-    has_control = pa_alsa_mixer_find(m, j->alsa_name, 0) != NULL;
+    has_control = pa_alsa_mixer_find_card(m, j->alsa_name, 0) != NULL;
     pa_alsa_jack_set_has_control(j, has_control);
 
     if (j->has_control) {
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index d1ca62d28619..a64505a0c781 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -1796,7 +1796,7 @@ static void ucm_mapping_jack_probe(pa_alsa_mapping *m) {
             mdev_opened = mdev;
         }
 
-        has_control = pa_alsa_mixer_find(mixer_handle, dev->jack->alsa_name, 
0) != NULL;
+        has_control = pa_alsa_mixer_find_card(mixer_handle, 
dev->jack->alsa_name, 0) != NULL;
         pa_alsa_jack_set_has_control(dev->jack, has_control);
         pa_log_info("UCM jack %s has_control=%d", dev->jack->name, 
dev->jack->has_control);
     }
diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index 2df2258d85af..08a144782394 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -1610,7 +1610,11 @@ bool pa_alsa_may_tsched(bool want) {
 
 #define SND_MIXER_ELEM_PULSEAUDIO (SND_MIXER_ELEM_LAST + 10)
 
-snd_mixer_elem_t *pa_alsa_mixer_find(snd_mixer_t *mixer, const char *name, 
unsigned int device) {
+static snd_mixer_elem_t *pa_alsa_mixer_find(snd_mixer_t *mixer,
+                                            snd_ctl_elem_iface_t iface,
+                                            const char *name,
+                                            unsigned int index,
+                                            unsigned int device) {
     snd_mixer_elem_t *elem;
 
     for (elem = snd_mixer_first_elem(mixer); elem; elem = 
snd_mixer_elem_next(elem)) {
@@ -1618,8 +1622,12 @@ snd_mixer_elem_t *pa_alsa_mixer_find(snd_mixer_t *mixer, 
const char *name, unsig
         if (snd_mixer_elem_get_type(elem) != SND_MIXER_ELEM_PULSEAUDIO)
             continue;
         helem = snd_mixer_elem_get_private(elem);
+        if (snd_hctl_elem_get_interface(helem) != iface)
+            continue;
         if (!pa_streq(snd_hctl_elem_get_name(helem), name))
             continue;
+        if (snd_hctl_elem_get_index(helem) != index)
+            continue;
         if (snd_hctl_elem_get_device(helem) != device)
             continue;
         return elem;
@@ -1627,6 +1635,14 @@ snd_mixer_elem_t *pa_alsa_mixer_find(snd_mixer_t *mixer, 
const char *name, unsig
     return NULL;
 }
 
+snd_mixer_elem_t *pa_alsa_mixer_find_card(snd_mixer_t *mixer, const char 
*name, unsigned int device) {
+    return pa_alsa_mixer_find(mixer, SND_CTL_ELEM_IFACE_CARD, name, 0, device);
+}
+
+snd_mixer_elem_t *pa_alsa_mixer_find_pcm(snd_mixer_t *mixer, const char *name, 
unsigned int device) {
+    return pa_alsa_mixer_find(mixer, SND_CTL_ELEM_IFACE_PCM, name, 0, device);
+}
+
 static int mixer_class_compare(const snd_mixer_elem_t *c1, const 
snd_mixer_elem_t *c2)
 {
     /* Dummy compare function */
diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h
index 4ceaa06ee480..ceca48809400 100644
--- a/src/modules/alsa/alsa-util.h
+++ b/src/modules/alsa/alsa-util.h
@@ -141,7 +141,8 @@ const char* pa_alsa_strerror(int errnum);
 
 bool pa_alsa_may_tsched(bool want);
 
-snd_mixer_elem_t *pa_alsa_mixer_find(snd_mixer_t *mixer, const char *name, 
unsigned int device);
+snd_mixer_elem_t *pa_alsa_mixer_find_card(snd_mixer_t *mixer, const char 
*name, unsigned int device);
+snd_mixer_elem_t *pa_alsa_mixer_find_pcm(snd_mixer_t *mixer, const char *name, 
unsigned int device);
 
 snd_mixer_t *pa_alsa_open_mixer(int alsa_card_index, char **ctl_device);
 
diff --git a/src/modules/alsa/module-alsa-card.c 
b/src/modules/alsa/module-alsa-card.c
index be260e4badab..ba7a19a38983 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -588,7 +588,7 @@ static void init_eld_ctls(struct userdata *u) {
         if (device < 0)
             continue;
 
-        melem = pa_alsa_mixer_find(u->mixer_handle, "ELD", device);
+        melem = pa_alsa_mixer_find_pcm(u->mixer_handle, "ELD", device);
         if (melem) {
             snd_mixer_elem_set_callback(melem, hdmi_eld_changed);
             snd_mixer_elem_set_callback_private(melem, u);
@@ -635,7 +635,7 @@ static void init_jacks(struct userdata *u) {
     u->mixer_handle = pa_alsa_open_mixer(u->alsa_card_index, NULL);
     if (u->mixer_handle && pa_alsa_fdlist_set_handle(u->mixer_fdl, 
u->mixer_handle, NULL, u->core->mainloop) >= 0) {
         PA_HASHMAP_FOREACH(jack, u->jacks, state) {
-            jack->melem = pa_alsa_mixer_find(u->mixer_handle, jack->alsa_name, 
0);
+            jack->melem = pa_alsa_mixer_find_card(u->mixer_handle, 
jack->alsa_name, 0);
             if (!jack->melem) {
                 pa_log_warn("Jack '%s' seems to have disappeared.", 
jack->alsa_name);
                 pa_alsa_jack_set_has_control(jack, false);
-- 
2.16.4

++++++ 0026-alsa-mixer-Add-the-ability-to-pass-the-intended-role.patch ++++++
>From c6a0665618975eedc98bdf23e4140935a1af38c2 Mon Sep 17 00:00:00 2001
From: Laurent Bigonville <[email protected]>
Date: Thu, 7 Mar 2019 11:35:30 +0100
Subject: [PATCH] alsa-mixer: Add the ability to pass the intended-role to the
 mapping

https://gitlab.freedesktop.org/pulseaudio/pulseaudio/issues/640
---
 src/modules/alsa/alsa-mixer.c | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index 094cff817df7..7956371b604b 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -3978,6 +3978,24 @@ static int mapping_parse_fallback(pa_config_parser_state 
*state) {
     return 0;
 }
 
+static int mapping_parse_intended_roles(pa_config_parser_state *state) {
+    pa_alsa_profile_set *ps;
+    pa_alsa_mapping *m;
+
+    pa_assert(state);
+
+    ps = state->userdata;
+
+    if (!(m = pa_alsa_mapping_get(ps, state->section))) {
+        pa_log("[%s:%u] %s invalid in section %s", state->filename, 
state->lineno, state->lvalue, state->section);
+        return -1;
+    }
+
+    pa_proplist_sets(m->proplist, PA_PROP_DEVICE_INTENDED_ROLES, 
state->rvalue);
+
+    return 0;
+}
+
 
 static int profile_parse_mappings(pa_config_parser_state *state) {
     pa_alsa_profile_set *ps;
@@ -4569,6 +4587,7 @@ pa_alsa_profile_set* pa_alsa_profile_set_new(const char 
*fname, const pa_channel
         { "element-output",         mapping_parse_element,        NULL, NULL },
         { "direction",              mapping_parse_direction,      NULL, NULL },
         { "exact-channels",         mapping_parse_exact_channels, NULL, NULL },
+        { "intended-roles",         mapping_parse_intended_roles, NULL, NULL },
 
         /* Shared by [Mapping ...] and [Profile ...] */
         { "description",            mapping_parse_description,    NULL, NULL },
-- 
2.16.4

++++++ 0027-alsa-mixer-Set-the-intended-role-of-Steelseries-Arct.patch ++++++
>From 6438e5c46dc449e4726ec9312859cc70388d2851 Mon Sep 17 00:00:00 2001
From: Laurent Bigonville <[email protected]>
Date: Thu, 7 Mar 2019 11:36:02 +0100
Subject: [PATCH] alsa-mixer: Set the intended-role of Steelseries Arctis 5/7
 headset as phone

Fixes: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/issues/640
---
 .../alsa/mixer/profile-sets/steelseries-arctis-common-usb-audio.conf     | 1 +
 src/modules/alsa/mixer/profile-sets/usb-gaming-headset.conf              | 1 +
 2 files changed, 2 insertions(+)

diff --git 
a/src/modules/alsa/mixer/profile-sets/steelseries-arctis-common-usb-audio.conf 
b/src/modules/alsa/mixer/profile-sets/steelseries-arctis-common-usb-audio.conf
index 80c33707aea4..5f11ed1e26ef 100644
--- 
a/src/modules/alsa/mixer/profile-sets/steelseries-arctis-common-usb-audio.conf
+++ 
b/src/modules/alsa/mixer/profile-sets/steelseries-arctis-common-usb-audio.conf
@@ -7,6 +7,7 @@ device-strings = hw:%f,0,0
 channel-map = left,right
 paths-input = analog-input-mic
 paths-output = steelseries-arctis-output-chat-common
+intended-roles = phone
 
 [Mapping analog-game]
 description = Game
diff --git a/src/modules/alsa/mixer/profile-sets/usb-gaming-headset.conf 
b/src/modules/alsa/mixer/profile-sets/usb-gaming-headset.conf
index 01ecf864bcf2..f48b44f03767 100644
--- a/src/modules/alsa/mixer/profile-sets/usb-gaming-headset.conf
+++ b/src/modules/alsa/mixer/profile-sets/usb-gaming-headset.conf
@@ -34,6 +34,7 @@ device-strings = hw:%f,0,0
 channel-map = mono
 paths-output = usb-gaming-headset-output-mono
 paths-input = usb-gaming-headset-input
+intended-roles = phone
 
 [Mapping analog-stereo]
 device-strings = hw:%f,1,0
-- 
2.16.4

++++++ 0028-alsa-rewrite-mixer-open-close-cache-mixer-accesses-i.patch ++++++
++++ 761 lines (skipped)

++++++ 0029-alsa-ucm-add-support-for-HDMI-ELD.patch ++++++
>From 3ceff8bb3f697ec16dc5c72e658b10ac40bc19f5 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <[email protected]>
Date: Sat, 7 Dec 2019 23:22:33 +0100
Subject: [PATCH] alsa-ucm: add support for HDMI ELD

Signed-off-by: Jaroslav Kysela <[email protected]>
---
 src/modules/alsa/alsa-ucm.c         | 72 +++++++++++++++++++++++++++++++++++--
 src/modules/alsa/alsa-ucm.h         |  7 ++++
 src/modules/alsa/module-alsa-card.c | 56 ++++++++++++++++++-----------
 3 files changed, 113 insertions(+), 22 deletions(-)

diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index b9a4b8c4b436..5695840abaf1 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -820,6 +820,36 @@ static int pa_alsa_ucm_device_cmp(const void *a, const 
void *b) {
     return strcmp(pa_proplist_gets(d1->proplist, PA_ALSA_PROP_UCM_NAME), 
pa_proplist_gets(d2->proplist, PA_ALSA_PROP_UCM_NAME));
 }
 
+static void set_eld_devices(pa_hashmap *hash)
+{
+    pa_device_port *port;
+    pa_alsa_ucm_port_data *data;
+    pa_alsa_ucm_device *dev;
+    const char *eld_mixer_device_name;
+    void *state;
+    int idx, eld_device;
+
+    PA_HASHMAP_FOREACH(port, hash, state) {
+        data = PA_DEVICE_PORT_DATA(port);
+        eld_mixer_device_name = NULL;
+        eld_device = -1;
+        PA_DYNARRAY_FOREACH(dev, data->devices, idx) {
+            if (dev->eld_device >= 0 && dev->eld_mixer_device_name) {
+                if (eld_device >= 0 && eld_device != dev->eld_device) {
+                    pa_log_error("The ELD device is already set!");
+                } else if (eld_mixer_device_name && 
pa_streq(dev->eld_mixer_device_name, eld_mixer_device_name)) {
+                    pa_log_error("The ELD mixer device is already set (%s, 
%s)!", dev->eld_mixer_device_name, dev->eld_mixer_device_name);
+                } else {
+                    eld_mixer_device_name = dev->eld_mixer_device_name;
+                    eld_device = dev->eld_device;
+                }
+            }
+        }
+        data->eld_device = eld_device;
+        data->eld_mixer_device_name = pa_xstrdup(eld_mixer_device_name);
+    }
+}
+
 static void probe_volumes(pa_hashmap *hash, bool is_sink, snd_pcm_t 
*pcm_handle, pa_hashmap *mixers, bool ignore_dB) {
     pa_device_port *port;
     pa_alsa_path *path;
@@ -1159,6 +1189,9 @@ void pa_alsa_ucm_add_ports_combination(
         ucm_add_ports_combination(p, context, is_sink, pdevices, 0, 
PA_IDXSET_INVALID, ports, cp, core);
         pa_xfree(pdevices);
     }
+
+    /* ELD devices */
+    set_eld_devices(ports);
 }
 
 void pa_alsa_ucm_add_ports(
@@ -1709,6 +1742,33 @@ static int ucm_create_profile(
     return 0;
 }
 
+static void mapping_init_eld(pa_alsa_mapping *m, snd_pcm_t *pcm)
+{
+    pa_alsa_ucm_mapping_context *context = &m->ucm_context;
+    pa_alsa_ucm_device *dev;
+    uint32_t idx;
+    char *mdev;
+    snd_pcm_info_t *info;
+    int pcm_card, pcm_device;
+
+    snd_pcm_info_alloca(&info);
+    if (snd_pcm_info(pcm, info) < 0)
+        return;
+
+    if ((pcm_card = snd_pcm_info_get_card(info)) < 0)
+        return;
+    if ((pcm_device = snd_pcm_info_get_device(info)) < 0)
+        return;
+
+    PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
+       mdev = pa_sprintf_malloc("hw:%i", pcm_card);
+       if (mdev == NULL)
+           continue;
+       dev->eld_mixer_device_name = mdev;
+       dev->eld_device = pcm_device;
+    }
+}
+
 static snd_pcm_t* mapping_open_pcm(pa_alsa_ucm_config *ucm, pa_alsa_mapping 
*m, int mode) {
     snd_pcm_t* pcm;
     pa_sample_spec try_ss = ucm->core->default_sample_spec;
@@ -1730,8 +1790,11 @@ static snd_pcm_t* mapping_open_pcm(pa_alsa_ucm_config 
*ucm, pa_alsa_mapping *m,
     pcm = pa_alsa_open_by_device_string(m->device_strings[0], NULL, &try_ss,
             &try_map, mode, &try_period_size, &try_buffer_size, 0, NULL, NULL, 
exact_channels);
 
-    if (pcm && !exact_channels)
-        m->channel_map = try_map;
+    if (pcm) {
+        if (!exact_channels)
+            m->channel_map = try_map;
+        mapping_init_eld(m, pcm);
+    }
 
     return pcm;
 }
@@ -1912,6 +1975,8 @@ static void free_verb(pa_alsa_ucm_verb *verb) {
         if (di->supported_devices)
             pa_idxset_free(di->supported_devices, NULL);
 
+        pa_xfree(di->eld_mixer_device_name);
+
         pa_xfree(di);
     }
 
@@ -2115,6 +2180,7 @@ static void ucm_port_data_init(pa_alsa_ucm_port_data 
*port, pa_alsa_ucm_config *
     port->ucm = ucm;
     port->core_port = core_port;
     port->devices = pa_dynarray_new(NULL);
+    port->eld_device = -1;
 
     for (i = 0; i < n_devices; i++) {
         pa_dynarray_append(port->devices, devices[i]);
@@ -2139,6 +2205,8 @@ static void ucm_port_data_free(pa_device_port *port) {
 
     if (ucm_port->paths)
         pa_hashmap_free(ucm_port->paths);
+
+    pa_xfree(ucm_port->eld_mixer_device_name);
 }
 
 static void ucm_port_update_available(pa_alsa_ucm_port_data *port) {
diff --git a/src/modules/alsa/alsa-ucm.h b/src/modules/alsa/alsa-ucm.h
index 49362992865b..48fd9db3f79f 100644
--- a/src/modules/alsa/alsa-ucm.h
+++ b/src/modules/alsa/alsa-ucm.h
@@ -207,6 +207,9 @@ struct pa_alsa_ucm_device {
     pa_alsa_jack *jack;
     pa_dynarray *hw_mute_jacks; /* pa_alsa_jack */
     pa_available_t available;
+
+    char *eld_mixer_device_name;
+    int eld_device;
 };
 
 void pa_alsa_ucm_device_update_available(pa_alsa_ucm_device *device);
@@ -273,6 +276,10 @@ struct pa_alsa_ucm_port_data {
     pa_hashmap *paths;
     /* Current path, set when activating profile */
     pa_alsa_path *path;
+
+    /* ELD info */
+    char *eld_mixer_device_name;
+    int eld_device; /* PCM device number */
 };
 
 struct pa_alsa_ucm_volume {
diff --git a/src/modules/alsa/module-alsa-card.c 
b/src/modules/alsa/module-alsa-card.c
index 3b2f99fc4e54..c5852b43d844 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -512,15 +512,24 @@ static int report_jack_state(snd_mixer_elem_t *melem, 
unsigned int mask) {
     return 0;
 }
 
-static pa_device_port* find_port_with_eld_device(pa_hashmap *ports, int 
device) {
+static pa_device_port* find_port_with_eld_device(struct userdata *u, int 
device) {
     void *state;
     pa_device_port *p;
 
-    PA_HASHMAP_FOREACH(p, ports, state) {
-        pa_alsa_port_data *data = PA_DEVICE_PORT_DATA(p);
-        pa_assert(data->path);
-        if (device == data->path->eld_device)
-            return p;
+    if (u->use_ucm) {
+        PA_HASHMAP_FOREACH(p, u->card->ports, state) {
+            pa_alsa_ucm_port_data *data = PA_DEVICE_PORT_DATA(p);
+            pa_assert(data->eld_mixer_device_name);
+            if (device == data->eld_device)
+                return p;
+        }
+    } else {
+        PA_HASHMAP_FOREACH(p, u->card->ports, state) {
+            pa_alsa_port_data *data = PA_DEVICE_PORT_DATA(p);
+            pa_assert(data->path);
+            if (device == data->path->eld_device)
+                return p;
+        }
     }
     return NULL;
 }
@@ -537,10 +546,7 @@ static int hdmi_eld_changed(snd_mixer_elem_t *melem, 
unsigned int mask) {
     if (mask == SND_CTL_EVENT_MASK_REMOVE)
         return 0;
 
-    if (u->use_ucm)
-        return 0;
-
-    p = find_port_with_eld_device(u->card->ports, device);
+    p = find_port_with_eld_device(u, device);
     if (p == NULL) {
         pa_log_error("Invalid device changed in ALSA: %d", device);
         return 0;
@@ -571,21 +577,30 @@ static void init_eld_ctls(struct userdata *u) {
     /* The code in this function expects ports to have a pa_alsa_port_data
      * struct as their data, but in UCM mode ports don't have any data. Hence,
      * the ELD controls can't currently be used in UCM mode. */
-    if (u->use_ucm)
-        return;
-
     PA_HASHMAP_FOREACH(port, u->card->ports, state) {
-        pa_alsa_port_data *data = PA_DEVICE_PORT_DATA(port);
         snd_mixer_t *mixer_handle;
         snd_mixer_elem_t* melem;
         int device;
 
-        pa_assert(data->path);
-        device = data->path->eld_device;
-        if (device < 0)
-            continue;
+        if (u->use_ucm) {
+            pa_alsa_ucm_port_data *data = PA_DEVICE_PORT_DATA(port);
+            device = data->eld_device;
+            if (device < 0 || !data->eld_mixer_device_name)
+                continue;
+
+            mixer_handle = pa_alsa_open_mixer_by_name(u->mixers, 
data->eld_mixer_device_name, true);
+        } else {
+            pa_alsa_port_data *data = PA_DEVICE_PORT_DATA(port);
+
+            pa_assert(data->path);
+
+            device = data->path->eld_device;
+            if (device < 0)
+                continue;
+
+            mixer_handle = pa_alsa_open_mixer(u->mixers, u->alsa_card_index, 
true);
+        }
 
-        mixer_handle = pa_alsa_open_mixer(u->mixers, u->alsa_card_index, true);
         if (!mixer_handle)
             continue;
 
@@ -595,9 +610,10 @@ static void init_eld_ctls(struct userdata *u) {
             snd_mixer_elem_set_callback(melem, hdmi_eld_changed);
             snd_mixer_elem_set_callback_private(melem, u);
             hdmi_eld_changed(melem, 0);
+            pa_log_info("ELD device found for port %s (%d).", port->name, 
device);
         }
         else
-            pa_log_debug("No ELD device found for port %s.", port->name);
+            pa_log_debug("No ELD device found for port %s (%d).", port->name, 
device);
     }
 }
 
-- 
2.16.4

++++++ 0030-alsa-mixer-do-the-quick-card-number-lookup-to-save-m.patch ++++++
>From 8837c90b7fe6b7c3cbbaeda0a29e2f5d311c3d14 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <[email protected]>
Date: Sun, 8 Dec 2019 22:48:45 +0100
Subject: [PATCH] alsa-mixer: do the quick card number lookup to save mixer
 instances

Signed-off-by: Jaroslav Kysela <[email protected]>
---
 src/modules/alsa/alsa-mixer.h |  1 +
 src/modules/alsa/alsa-util.c  | 33 +++++++++++++++++++++++++++++++++
 2 files changed, 34 insertions(+)

diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h
index 325b0c998b02..d740a78c8dce 100644
--- a/src/modules/alsa/alsa-mixer.h
+++ b/src/modules/alsa/alsa-mixer.h
@@ -102,6 +102,7 @@ struct pa_alsa_setting {
 /* An entry for one ALSA mixer */
 struct pa_alsa_mixer {
     snd_mixer_t *mixer_handle;
+    int card_index;
     pa_alsa_fdlist *fdl;
     bool used_for_probe_only:1;
 };
diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index 34930c9fa155..d86f43c10098 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -1737,11 +1737,33 @@ snd_mixer_t *pa_alsa_open_mixer_by_name(pa_hashmap 
*mixers, const char *dev, boo
     int err;
     snd_mixer_t *m;
     pa_alsa_mixer *pm;
+    char *dev2;
+    void *state;
 
     pa_assert(mixers);
     pa_assert(dev);
 
     pm = pa_hashmap_get(mixers, dev);
+
+    /* The quick card number/index lookup (hw:#)
+     * We already know the card number/index, thus use the mixer
+     * from the cache at first.
+     */
+    if (!pm && pa_strneq(dev, "hw:", 3)) {
+        const char *s = dev + 3;
+        int card_index;
+        while (*s && *s >= 0 && *s <= '9') s++;
+        if (*s == '\0' && pa_atoi(dev + 3, &card_index) >= 0) {
+            PA_HASHMAP_FOREACH_KV(dev2, pm, mixers, state) {
+                if (pm->card_index == card_index) {
+                    dev = dev2;
+                    pm = pa_hashmap_get(mixers, dev);
+                    break;
+                }
+            }
+        }
+    }
+
     if (pm) {
         if (!probe)
             pm->used_for_probe_only = false;
@@ -1756,6 +1778,17 @@ snd_mixer_t *pa_alsa_open_mixer_by_name(pa_hashmap 
*mixers, const char *dev, boo
     if (prepare_mixer(m, dev) >= 0) {
         pm = pa_xnew0(pa_alsa_mixer, 1);
         if (pm) {
+            snd_hctl_t *hctl;
+            pm->card_index = -1;
+            /* determine the ALSA card number (index) and store it to 
card_index */
+            err = snd_mixer_get_hctl(m, dev, &hctl);
+            if (err >= 0) {
+                snd_ctl_card_info_t *info;
+                snd_ctl_card_info_alloca(&info);
+                err = snd_ctl_card_info(snd_hctl_ctl(hctl), info);
+                if (err >= 0)
+                    pm->card_index = snd_ctl_card_info_get_card(info);
+            }
             pm->used_for_probe_only = probe;
             pm->mixer_handle = m;
             pa_hashmap_put(mixers, pa_xstrdup(dev), pm);
-- 
2.16.4

++++++ 0031-alsa-mixer-improve-check-for-the-empty-path-set-for-.patch ++++++
>From d7dbd0cbe3191661c02ac89d108b36c79474de3c Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <[email protected]>
Date: Sun, 8 Dec 2019 23:17:32 +0100
Subject: [PATCH] alsa-mixer: improve check for the empty path set for
 sink/source

The unused mixer instances are created without this code.

Signed-off-by: Jaroslav Kysela <[email protected]>
---
 src/modules/alsa/alsa-mixer.c  |  6 ++++++
 src/modules/alsa/alsa-mixer.h  |  1 +
 src/modules/alsa/alsa-sink.c   | 19 +++++++++++++------
 src/modules/alsa/alsa-source.c |  9 ++++++---
 4 files changed, 26 insertions(+), 9 deletions(-)

diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index a3c998b654e9..d184aec7aee7 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -741,6 +741,12 @@ void pa_alsa_path_set_free(pa_alsa_path_set *ps) {
     pa_xfree(ps);
 }
 
+int pa_alsa_path_set_is_empty(pa_alsa_path_set *ps) {
+    if (ps && !pa_hashmap_isempty(ps->paths))
+        return 0;
+    return 1;
+}
+
 static long to_alsa_dB(pa_volume_t v) {
     return lround(pa_sw_volume_to_dB(v) * 100.0);
 }
diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h
index d740a78c8dce..df739cc2e0f3 100644
--- a/src/modules/alsa/alsa-mixer.h
+++ b/src/modules/alsa/alsa-mixer.h
@@ -272,6 +272,7 @@ pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, 
pa_alsa_direction_t d
 void pa_alsa_path_set_dump(pa_alsa_path_set *s);
 void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, 
snd_mixer_elem_callback_t cb, void *userdata);
 void pa_alsa_path_set_free(pa_alsa_path_set *s);
+int pa_alsa_path_set_is_empty(pa_alsa_path_set *s);
 
 struct pa_alsa_mapping {
     pa_alsa_profile_set *profile_set;
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 363b4be2fa25..042d4dfd9c89 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -1651,7 +1651,6 @@ static int sink_set_port_ucm_cb(pa_sink *s, 
pa_device_port *p) {
 
     pa_assert(u);
     pa_assert(p);
-    pa_assert(u->mixer_handle);
     pa_assert(u->ucm_context);
 
     data = PA_DEVICE_PORT_DATA(p);
@@ -2089,6 +2088,9 @@ static void find_mixer(struct userdata *u, 
pa_alsa_mapping *mapping, const char
     if (!mapping && !element)
         return;
 
+    if (!element && mapping && 
pa_alsa_path_set_is_empty(mapping->output_path_set))
+        return;
+
     u->mixers = pa_hashmap_new_full(pa_idxset_string_hash_func, 
pa_idxset_string_compare_func,
                                     NULL, (pa_free_cb_t) pa_alsa_mixer_free);
 
@@ -2113,8 +2115,9 @@ static void find_mixer(struct userdata *u, 
pa_alsa_mapping *mapping, const char
 
         pa_log_debug("Probed mixer path %s:", u->mixer_path->name);
         pa_alsa_path_dump(u->mixer_path);
-    } else if (!(u->mixer_path_set = mapping->output_path_set))
-        goto fail;
+    } else {
+        u->mixer_path_set = mapping->output_path_set;
+    }
 
     return;
 
@@ -2559,10 +2562,14 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, 
const char*driver, pa_ca
         goto fail;
     }
 
-    if (u->ucm_context)
+    if (u->ucm_context) {
         pa_alsa_ucm_add_ports(&data.ports, data.proplist, u->ucm_context, 
true, card, u->pcm_handle, ignore_dB);
-    else if (u->mixer_path_set)
-        pa_alsa_add_ports(&data, u->mixer_path_set, card);
+        find_mixer(u, mapping, pa_modargs_get_value(ma, "control", NULL), 
ignore_dB);
+    } else {
+        find_mixer(u, mapping, pa_modargs_get_value(ma, "control", NULL), 
ignore_dB);
+        if (u->mixer_path_set)
+            pa_alsa_add_ports(&data, u->mixer_path_set, card);
+    }
 
     u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE | PA_SINK_LATENCY | 
(u->use_tsched ? PA_SINK_DYNAMIC_LATENCY : 0) |
                           (set_formats ? PA_SINK_SET_FORMATS : 0));
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index b46e845cc5a7..104de4e266dd 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -1522,7 +1522,6 @@ static int source_set_port_ucm_cb(pa_source *s, 
pa_device_port *p) {
 
     pa_assert(u);
     pa_assert(p);
-    pa_assert(u->mixer_handle);
     pa_assert(u->ucm_context);
 
     data = PA_DEVICE_PORT_DATA(p);
@@ -1795,6 +1794,9 @@ static void find_mixer(struct userdata *u, 
pa_alsa_mapping *mapping, const char
     if (!mapping && !element)
         return;
 
+    if (!element && mapping && 
pa_alsa_path_set_is_empty(mapping->input_path_set))
+        return;
+
     u->mixers = pa_hashmap_new_full(pa_idxset_string_hash_func, 
pa_idxset_string_compare_func,
                                     NULL, (pa_free_cb_t) pa_alsa_mixer_free);
 
@@ -1819,8 +1821,9 @@ static void find_mixer(struct userdata *u, 
pa_alsa_mapping *mapping, const char
 
         pa_log_debug("Probed mixer path %s:", u->mixer_path->name);
         pa_alsa_path_dump(u->mixer_path);
-    } else if (!(u->mixer_path_set = mapping->input_path_set))
-        goto fail;
+    } else {
+        u->mixer_path_set = mapping->input_path_set;
+    }
 
     return;
 
-- 
2.16.4

++++++ 0032-alsa-ucm-allow-to-set-profile-priority-from-UCM-valu.patch ++++++
>From cd4a69374cefe7c720bd6916f06ff7151da9892a Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <[email protected]>
Date: Tue, 10 Dec 2019 12:34:19 +0100
Subject: [PATCH] alsa-ucm: allow to set profile priority from UCM value

Signed-off-by: Jaroslav Kysela <[email protected]>
---
 src/modules/alsa/alsa-ucm.c | 59 +++++++++++++++++++++++++++++++++------------
 src/modules/alsa/alsa-ucm.h |  1 +
 2 files changed, 45 insertions(+), 15 deletions(-)

diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index 5695840abaf1..a57be6d22497 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -146,6 +146,26 @@ static struct ucm_info dev_info[] = {
     {NULL, 0}
 };
 
+
+static char *ucm_verb_value(
+    snd_use_case_mgr_t *uc_mgr,
+    const char *verb_name,
+    const char *id) {
+
+    const char *value;
+    char *_id = pa_sprintf_malloc("=%s//%s", id, verb_name);
+    int err = snd_use_case_get(uc_mgr, _id, &value);
+    pa_xfree(_id);
+    if (err < 0)
+         return NULL;
+    pa_log_debug("Got %s for verb %s: %s", id, verb_name, value);
+    /* Use the cast here to allow free() call without casting for callers.
+     * The snd_use_case_get() returns mallocated string.
+     * See the Note: in use-case.h for snd_use_case_get().
+     */
+    return (char *)value;
+}
+
 static int ucm_device_exists(pa_idxset *idxset, pa_alsa_ucm_device *dev) {
     pa_alsa_ucm_device *d;
     uint32_t idx;
@@ -766,6 +786,8 @@ int pa_alsa_ucm_get_verb(snd_use_case_mgr_t *uc_mgr, const 
char *verb_name, cons
     pa_alsa_ucm_device *d;
     pa_alsa_ucm_modifier *mod;
     pa_alsa_ucm_verb *verb;
+    char *value;
+    unsigned ui;
     int err = 0;
 
     *p_verb = NULL;
@@ -780,6 +802,11 @@ int pa_alsa_ucm_get_verb(snd_use_case_mgr_t *uc_mgr, const 
char *verb_name, cons
     pa_proplist_sets(verb->proplist, PA_ALSA_PROP_UCM_NAME, 
pa_strnull(verb_name));
     pa_proplist_sets(verb->proplist, PA_ALSA_PROP_UCM_DESCRIPTION, 
pa_strna(verb_desc));
 
+    value = ucm_verb_value(uc_mgr, verb_name, "Priority");
+    if (value && !pa_atou(value, &ui))
+        verb->priority = ui > 10000 ? 10000 : ui;
+    free(value);
+
     err = ucm_get_devices(verb, uc_mgr);
     if (err < 0)
         pa_log("No UCM devices for verb %s", verb_name);
@@ -1637,7 +1664,7 @@ static int ucm_create_profile(
     pa_alsa_ucm_modifier *mod;
     int i = 0;
     const char *name, *sink, *source;
-    char *verb_cmp, *c;
+    unsigned int priority;
 
     pa_assert(ps);
 
@@ -1657,24 +1684,26 @@ static int ucm_create_profile(
     p->supported = true;
     pa_hashmap_put(ps->profiles, p->name, p);
 
-    /* TODO: get profile priority from ucm info or policy management */
-    c = verb_cmp = pa_xstrdup(verb_name);
-    while (*c) {
-        if (*c == '_') *c = ' ';
-        c++;
-    }
+    /* TODO: get profile priority from policy management */
+    priority = verb->priority;
 
-    for (i = 0; verb_info[i].id; i++) {
-        if (strcasecmp(verb_info[i].id, verb_cmp) == 0) {
-            p->priority = verb_info[i].priority;
-            break;
+    if (priority == 0) {
+        char *verb_cmp, *c;
+        c = verb_cmp = pa_xstrdup(verb_name);
+        while (*c) {
+            if (*c == '_') *c = ' ';
+            c++;
+        }
+        for (i = 0; verb_info[i].id; i++) {
+            if (strcasecmp(verb_info[i].id, verb_cmp) == 0) {
+                priority = verb_info[i].priority;
+                break;
+            }
         }
+        pa_xfree(verb_cmp);
     }
 
-    pa_xfree(verb_cmp);
-
-    if (verb_info[i].id == NULL)
-        p->priority = 1000;
+    p->priority = priority;
 
     PA_LLIST_FOREACH(dev, verb->devices) {
         pa_alsa_jack *jack;
diff --git a/src/modules/alsa/alsa-ucm.h b/src/modules/alsa/alsa-ucm.h
index 48fd9db3f79f..e7a795cedeb7 100644
--- a/src/modules/alsa/alsa-ucm.h
+++ b/src/modules/alsa/alsa-ucm.h
@@ -241,6 +241,7 @@ struct pa_alsa_ucm_verb {
     PA_LLIST_FIELDS(pa_alsa_ucm_verb);
 
     pa_proplist *proplist;
+    unsigned priority;
 
     PA_LLIST_HEAD(pa_alsa_ucm_device, devices);
     PA_LLIST_HEAD(pa_alsa_ucm_modifier, modifiers);
-- 
2.16.4


Reply via email to