Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ALSA: usb-audio: Add input gain and master output mixer elements for RME Babyface Pro

Add missing input gain and master output mixer controls for
RME Babyface Pro.

This patch implements:

1. Input gain controls for 2 mic and 2 line inputs
2. Master output volume controls for all 12 output channels

These additions allow for more complete control of the Babyface Pro under
Linux.

Signed-off-by: Stefan Stistrup <sstistrup@gmail.com>
Link: https://patch.msgid.link/20240809204922.20112-1-sstistrup@gmail.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>

authored by

Stefan Stistrup and committed by
Takashi Iwai
e9606148 4004f302

+162 -1
+162 -1
sound/usb/mixer_quirks.c
··· 2541 2541 #define SND_BBFPRO_CTL_REG2_PAD_AN1 4 2542 2542 #define SND_BBFPRO_CTL_REG2_PAD_AN2 5 2543 2543 2544 - #define SND_BBFPRO_MIXER_IDX_MASK 0x1ff 2544 + #define SND_BBFPRO_MIXER_MAIN_OUT_CH_OFFSET 992 2545 + #define SND_BBFPRO_MIXER_IDX_MASK 0x3ff 2545 2546 #define SND_BBFPRO_MIXER_VAL_MASK 0x3ffff 2546 2547 #define SND_BBFPRO_MIXER_VAL_SHIFT 9 2547 2548 #define SND_BBFPRO_MIXER_VAL_MIN 0 // -inf 2548 2549 #define SND_BBFPRO_MIXER_VAL_MAX 65536 // +6dB 2549 2550 2551 + #define SND_BBFPRO_GAIN_CHANNEL_MASK 0x03 2552 + #define SND_BBFPRO_GAIN_CHANNEL_SHIFT 7 2553 + #define SND_BBFPRO_GAIN_VAL_MASK 0x7f 2554 + #define SND_BBFPRO_GAIN_VAL_MIN 0 2555 + #define SND_BBFPRO_GAIN_VAL_MIC_MAX 65 2556 + #define SND_BBFPRO_GAIN_VAL_LINE_MAX 18 // 9db in 0.5db incraments 2557 + 2550 2558 #define SND_BBFPRO_USBREQ_CTL_REG1 0x10 2551 2559 #define SND_BBFPRO_USBREQ_CTL_REG2 0x17 2560 + #define SND_BBFPRO_USBREQ_GAIN 0x1a 2552 2561 #define SND_BBFPRO_USBREQ_MIXER 0x12 2553 2562 2554 2563 static int snd_bbfpro_ctl_update(struct usb_mixer_interface *mixer, u8 reg, ··· 2704 2695 return snd_bbfpro_ctl_update(list->mixer, reg, idx, value); 2705 2696 } 2706 2697 2698 + static int snd_bbfpro_gain_update(struct usb_mixer_interface *mixer, 2699 + u8 channel, u8 gain) 2700 + { 2701 + int err; 2702 + struct snd_usb_audio *chip = mixer->chip; 2703 + 2704 + if (channel < 2) { 2705 + // XLR preamp: 3-bit fine, 5-bit coarse; special case >60 2706 + if (gain < 60) 2707 + gain = ((gain % 3) << 5) | (gain / 3); 2708 + else 2709 + gain = ((gain % 6) << 5) | (60 / 3); 2710 + } 2711 + 2712 + err = snd_usb_lock_shutdown(chip); 2713 + if (err < 0) 2714 + return err; 2715 + 2716 + err = snd_usb_ctl_msg(chip->dev, 2717 + usb_sndctrlpipe(chip->dev, 0), 2718 + SND_BBFPRO_USBREQ_GAIN, 2719 + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 2720 + gain, channel, NULL, 0); 2721 + 2722 + snd_usb_unlock_shutdown(chip); 2723 + return err; 2724 + } 2725 + 2726 + static int snd_bbfpro_gain_get(struct snd_kcontrol *kcontrol, 2727 + struct snd_ctl_elem_value *ucontrol) 2728 + { 2729 + int value = kcontrol->private_value & SND_BBFPRO_GAIN_VAL_MASK; 2730 + 2731 + ucontrol->value.integer.value[0] = value; 2732 + return 0; 2733 + } 2734 + 2735 + static int snd_bbfpro_gain_info(struct snd_kcontrol *kcontrol, 2736 + struct snd_ctl_elem_info *uinfo) 2737 + { 2738 + int pv, channel; 2739 + 2740 + pv = kcontrol->private_value; 2741 + channel = (pv >> SND_BBFPRO_GAIN_CHANNEL_SHIFT) & 2742 + SND_BBFPRO_GAIN_CHANNEL_MASK; 2743 + 2744 + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 2745 + uinfo->count = 1; 2746 + uinfo->value.integer.min = SND_BBFPRO_GAIN_VAL_MIN; 2747 + 2748 + if (channel < 2) 2749 + uinfo->value.integer.max = SND_BBFPRO_GAIN_VAL_MIC_MAX; 2750 + else 2751 + uinfo->value.integer.max = SND_BBFPRO_GAIN_VAL_LINE_MAX; 2752 + 2753 + return 0; 2754 + } 2755 + 2756 + static int snd_bbfpro_gain_put(struct snd_kcontrol *kcontrol, 2757 + struct snd_ctl_elem_value *ucontrol) 2758 + { 2759 + int pv, channel, old_value, value, err; 2760 + 2761 + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); 2762 + struct usb_mixer_interface *mixer = list->mixer; 2763 + 2764 + pv = kcontrol->private_value; 2765 + channel = (pv >> SND_BBFPRO_GAIN_CHANNEL_SHIFT) & 2766 + SND_BBFPRO_GAIN_CHANNEL_MASK; 2767 + old_value = pv & SND_BBFPRO_GAIN_VAL_MASK; 2768 + value = ucontrol->value.integer.value[0]; 2769 + 2770 + if (value < SND_BBFPRO_GAIN_VAL_MIN) 2771 + return -EINVAL; 2772 + 2773 + if (channel < 2) { 2774 + if (value > SND_BBFPRO_GAIN_VAL_MIC_MAX) 2775 + return -EINVAL; 2776 + } else { 2777 + if (value > SND_BBFPRO_GAIN_VAL_LINE_MAX) 2778 + return -EINVAL; 2779 + } 2780 + 2781 + if (value == old_value) 2782 + return 0; 2783 + 2784 + err = snd_bbfpro_gain_update(mixer, channel, value); 2785 + if (err < 0) 2786 + return err; 2787 + 2788 + kcontrol->private_value = 2789 + (channel << SND_BBFPRO_GAIN_CHANNEL_SHIFT) | value; 2790 + return 1; 2791 + } 2792 + 2793 + static int snd_bbfpro_gain_resume(struct usb_mixer_elem_list *list) 2794 + { 2795 + int pv, channel, value; 2796 + struct snd_kcontrol *kctl = list->kctl; 2797 + 2798 + pv = kctl->private_value; 2799 + channel = (pv >> SND_BBFPRO_GAIN_CHANNEL_SHIFT) & 2800 + SND_BBFPRO_GAIN_CHANNEL_MASK; 2801 + value = pv & SND_BBFPRO_GAIN_VAL_MASK; 2802 + 2803 + return snd_bbfpro_gain_update(list->mixer, channel, value); 2804 + } 2805 + 2707 2806 static int snd_bbfpro_vol_update(struct usb_mixer_interface *mixer, u16 index, 2708 2807 u32 value) 2709 2808 { ··· 2907 2790 .put = snd_bbfpro_ctl_put 2908 2791 }; 2909 2792 2793 + static const struct snd_kcontrol_new snd_bbfpro_gain_control = { 2794 + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2795 + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 2796 + .index = 0, 2797 + .info = snd_bbfpro_gain_info, 2798 + .get = snd_bbfpro_gain_get, 2799 + .put = snd_bbfpro_gain_put 2800 + }; 2801 + 2910 2802 static const struct snd_kcontrol_new snd_bbfpro_vol_control = { 2911 2803 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2912 2804 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, ··· 2936 2810 << SND_BBFPRO_CTL_IDX_SHIFT); 2937 2811 2938 2812 return add_single_ctl_with_resume(mixer, 0, snd_bbfpro_ctl_resume, 2813 + &knew, NULL); 2814 + } 2815 + 2816 + static int snd_bbfpro_gain_add(struct usb_mixer_interface *mixer, u8 channel, 2817 + char *name) 2818 + { 2819 + struct snd_kcontrol_new knew = snd_bbfpro_gain_control; 2820 + 2821 + knew.name = name; 2822 + knew.private_value = channel << SND_BBFPRO_GAIN_CHANNEL_SHIFT; 2823 + 2824 + return add_single_ctl_with_resume(mixer, 0, snd_bbfpro_gain_resume, 2939 2825 &knew, NULL); 2940 2826 } 2941 2827 ··· 2996 2858 if (err < 0) 2997 2859 return err; 2998 2860 } 2861 + } 2862 + 2863 + // Main out volume 2864 + for (i = 0 ; i < 12 ; ++i) { 2865 + snprintf(name, sizeof(name), "Main-Out %s", output[i]); 2866 + // Main outs are offset to 992 2867 + err = snd_bbfpro_vol_add(mixer, 2868 + i + SND_BBFPRO_MIXER_MAIN_OUT_CH_OFFSET, 2869 + name); 2870 + if (err < 0) 2871 + return err; 2872 + } 2873 + 2874 + // Input gain 2875 + for (i = 0 ; i < 4 ; ++i) { 2876 + if (i < 2) 2877 + snprintf(name, sizeof(name), "Mic-%s Gain", input[i]); 2878 + else 2879 + snprintf(name, sizeof(name), "Line-%s Gain", input[i]); 2880 + 2881 + err = snd_bbfpro_gain_add(mixer, i, name); 2882 + if (err < 0) 2883 + return err; 2999 2884 } 3000 2885 3001 2886 // Control Reg 1