summaryrefslogtreecommitdiff
path: root/sound/usb
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2018-05-02 12:52:55 +0300
committerTakashi Iwai <tiwai@suse.de>2018-05-02 17:02:34 +0300
commit4120fbedbb6c999686d03a9c74acc4f650ece8a2 (patch)
tree9071fd54131328570871d82b6512a2c3026984de /sound/usb
parent8a463225b11047455b374729d18c8a371fe6e591 (diff)
downloadlinux-4120fbedbb6c999686d03a9c74acc4f650ece8a2.tar.xz
ALSA: usb-audio: Add "Keep Interface" control
This patch adds "Keep Interface" control for each USB-audio device. The control element is with SND_CTL_IFACE_CARD, so that it won't appear on any sane mixer applications. For a device that is confirmed to work well with "keep-interface" mode, user can flip the control via amixer, e.g. % amixer -c1 cset iface=CARD,name='Keep Interface' on and save/restore the state via alsactl. The reason to provide this via control API is that the behavior must be pretty depending on the device (and the firmware in it), so it's not ideal to apply via module option. For a device that certainly works, we may set it statically via a quirk table entry. But a device like Dell WD15 dock behaves so differently depending on the firmware, and we can't set it statically. So leave this as a dynamic switch each user can adjust freely. Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/usb')
-rw-r--r--sound/usb/mixer.c45
1 files changed, 45 insertions, 0 deletions
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 76fabc4b72b5..bb203b3684fc 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -2801,6 +2801,48 @@ static int snd_usb_mixer_status_create(struct usb_mixer_interface *mixer)
return 0;
}
+static int keep_iface_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = mixer->chip->keep_iface;
+ return 0;
+}
+
+static int keep_iface_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+ bool keep_iface = !!ucontrol->value.integer.value[0];
+
+ if (mixer->chip->keep_iface == keep_iface)
+ return 0;
+ mixer->chip->keep_iface = keep_iface;
+ return 1;
+}
+
+static const struct snd_kcontrol_new keep_iface_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .name = "Keep Interface",
+ .info = snd_ctl_boolean_mono_info,
+ .get = keep_iface_ctl_get,
+ .put = keep_iface_ctl_put,
+};
+
+static int create_keep_iface_ctl(struct usb_mixer_interface *mixer)
+{
+ struct snd_kcontrol *kctl = snd_ctl_new1(&keep_iface_ctl, mixer);
+
+ /* need only one control per card */
+ if (snd_ctl_find_id(mixer->chip->card, &kctl->id)) {
+ snd_ctl_free_one(kctl);
+ return 0;
+ }
+
+ return snd_ctl_add(mixer->chip->card, kctl);
+}
+
int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
int ignore_error)
{
@@ -2842,6 +2884,9 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
if ((err = snd_usb_mixer_controls(mixer)) < 0 ||
(err = snd_usb_mixer_status_create(mixer)) < 0)
goto _error;
+ err = create_keep_iface_ctl(mixer);
+ if (err < 0)
+ goto _error;
snd_usb_mixer_apply_create_quirk(mixer);