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>
This commit is contained in:
parent
4004f3029e
commit
e9606148a6
@ -2541,14 +2541,23 @@ enum {
|
|||||||
#define SND_BBFPRO_CTL_REG2_PAD_AN1 4
|
#define SND_BBFPRO_CTL_REG2_PAD_AN1 4
|
||||||
#define SND_BBFPRO_CTL_REG2_PAD_AN2 5
|
#define SND_BBFPRO_CTL_REG2_PAD_AN2 5
|
||||||
|
|
||||||
#define SND_BBFPRO_MIXER_IDX_MASK 0x1ff
|
#define SND_BBFPRO_MIXER_MAIN_OUT_CH_OFFSET 992
|
||||||
|
#define SND_BBFPRO_MIXER_IDX_MASK 0x3ff
|
||||||
#define SND_BBFPRO_MIXER_VAL_MASK 0x3ffff
|
#define SND_BBFPRO_MIXER_VAL_MASK 0x3ffff
|
||||||
#define SND_BBFPRO_MIXER_VAL_SHIFT 9
|
#define SND_BBFPRO_MIXER_VAL_SHIFT 9
|
||||||
#define SND_BBFPRO_MIXER_VAL_MIN 0 // -inf
|
#define SND_BBFPRO_MIXER_VAL_MIN 0 // -inf
|
||||||
#define SND_BBFPRO_MIXER_VAL_MAX 65536 // +6dB
|
#define SND_BBFPRO_MIXER_VAL_MAX 65536 // +6dB
|
||||||
|
|
||||||
|
#define SND_BBFPRO_GAIN_CHANNEL_MASK 0x03
|
||||||
|
#define SND_BBFPRO_GAIN_CHANNEL_SHIFT 7
|
||||||
|
#define SND_BBFPRO_GAIN_VAL_MASK 0x7f
|
||||||
|
#define SND_BBFPRO_GAIN_VAL_MIN 0
|
||||||
|
#define SND_BBFPRO_GAIN_VAL_MIC_MAX 65
|
||||||
|
#define SND_BBFPRO_GAIN_VAL_LINE_MAX 18 // 9db in 0.5db incraments
|
||||||
|
|
||||||
#define SND_BBFPRO_USBREQ_CTL_REG1 0x10
|
#define SND_BBFPRO_USBREQ_CTL_REG1 0x10
|
||||||
#define SND_BBFPRO_USBREQ_CTL_REG2 0x17
|
#define SND_BBFPRO_USBREQ_CTL_REG2 0x17
|
||||||
|
#define SND_BBFPRO_USBREQ_GAIN 0x1a
|
||||||
#define SND_BBFPRO_USBREQ_MIXER 0x12
|
#define SND_BBFPRO_USBREQ_MIXER 0x12
|
||||||
|
|
||||||
static int snd_bbfpro_ctl_update(struct usb_mixer_interface *mixer, u8 reg,
|
static int snd_bbfpro_ctl_update(struct usb_mixer_interface *mixer, u8 reg,
|
||||||
@ -2695,6 +2704,114 @@ static int snd_bbfpro_ctl_resume(struct usb_mixer_elem_list *list)
|
|||||||
return snd_bbfpro_ctl_update(list->mixer, reg, idx, value);
|
return snd_bbfpro_ctl_update(list->mixer, reg, idx, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int snd_bbfpro_gain_update(struct usb_mixer_interface *mixer,
|
||||||
|
u8 channel, u8 gain)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct snd_usb_audio *chip = mixer->chip;
|
||||||
|
|
||||||
|
if (channel < 2) {
|
||||||
|
// XLR preamp: 3-bit fine, 5-bit coarse; special case >60
|
||||||
|
if (gain < 60)
|
||||||
|
gain = ((gain % 3) << 5) | (gain / 3);
|
||||||
|
else
|
||||||
|
gain = ((gain % 6) << 5) | (60 / 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
err = snd_usb_lock_shutdown(chip);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = snd_usb_ctl_msg(chip->dev,
|
||||||
|
usb_sndctrlpipe(chip->dev, 0),
|
||||||
|
SND_BBFPRO_USBREQ_GAIN,
|
||||||
|
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||||
|
gain, channel, NULL, 0);
|
||||||
|
|
||||||
|
snd_usb_unlock_shutdown(chip);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int snd_bbfpro_gain_get(struct snd_kcontrol *kcontrol,
|
||||||
|
struct snd_ctl_elem_value *ucontrol)
|
||||||
|
{
|
||||||
|
int value = kcontrol->private_value & SND_BBFPRO_GAIN_VAL_MASK;
|
||||||
|
|
||||||
|
ucontrol->value.integer.value[0] = value;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int snd_bbfpro_gain_info(struct snd_kcontrol *kcontrol,
|
||||||
|
struct snd_ctl_elem_info *uinfo)
|
||||||
|
{
|
||||||
|
int pv, channel;
|
||||||
|
|
||||||
|
pv = kcontrol->private_value;
|
||||||
|
channel = (pv >> SND_BBFPRO_GAIN_CHANNEL_SHIFT) &
|
||||||
|
SND_BBFPRO_GAIN_CHANNEL_MASK;
|
||||||
|
|
||||||
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||||
|
uinfo->count = 1;
|
||||||
|
uinfo->value.integer.min = SND_BBFPRO_GAIN_VAL_MIN;
|
||||||
|
|
||||||
|
if (channel < 2)
|
||||||
|
uinfo->value.integer.max = SND_BBFPRO_GAIN_VAL_MIC_MAX;
|
||||||
|
else
|
||||||
|
uinfo->value.integer.max = SND_BBFPRO_GAIN_VAL_LINE_MAX;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int snd_bbfpro_gain_put(struct snd_kcontrol *kcontrol,
|
||||||
|
struct snd_ctl_elem_value *ucontrol)
|
||||||
|
{
|
||||||
|
int pv, channel, old_value, value, err;
|
||||||
|
|
||||||
|
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
|
||||||
|
struct usb_mixer_interface *mixer = list->mixer;
|
||||||
|
|
||||||
|
pv = kcontrol->private_value;
|
||||||
|
channel = (pv >> SND_BBFPRO_GAIN_CHANNEL_SHIFT) &
|
||||||
|
SND_BBFPRO_GAIN_CHANNEL_MASK;
|
||||||
|
old_value = pv & SND_BBFPRO_GAIN_VAL_MASK;
|
||||||
|
value = ucontrol->value.integer.value[0];
|
||||||
|
|
||||||
|
if (value < SND_BBFPRO_GAIN_VAL_MIN)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (channel < 2) {
|
||||||
|
if (value > SND_BBFPRO_GAIN_VAL_MIC_MAX)
|
||||||
|
return -EINVAL;
|
||||||
|
} else {
|
||||||
|
if (value > SND_BBFPRO_GAIN_VAL_LINE_MAX)
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value == old_value)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err = snd_bbfpro_gain_update(mixer, channel, value);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
kcontrol->private_value =
|
||||||
|
(channel << SND_BBFPRO_GAIN_CHANNEL_SHIFT) | value;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int snd_bbfpro_gain_resume(struct usb_mixer_elem_list *list)
|
||||||
|
{
|
||||||
|
int pv, channel, value;
|
||||||
|
struct snd_kcontrol *kctl = list->kctl;
|
||||||
|
|
||||||
|
pv = kctl->private_value;
|
||||||
|
channel = (pv >> SND_BBFPRO_GAIN_CHANNEL_SHIFT) &
|
||||||
|
SND_BBFPRO_GAIN_CHANNEL_MASK;
|
||||||
|
value = pv & SND_BBFPRO_GAIN_VAL_MASK;
|
||||||
|
|
||||||
|
return snd_bbfpro_gain_update(list->mixer, channel, value);
|
||||||
|
}
|
||||||
|
|
||||||
static int snd_bbfpro_vol_update(struct usb_mixer_interface *mixer, u16 index,
|
static int snd_bbfpro_vol_update(struct usb_mixer_interface *mixer, u16 index,
|
||||||
u32 value)
|
u32 value)
|
||||||
{
|
{
|
||||||
@ -2790,6 +2907,15 @@ static const struct snd_kcontrol_new snd_bbfpro_ctl_control = {
|
|||||||
.put = snd_bbfpro_ctl_put
|
.put = snd_bbfpro_ctl_put
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct snd_kcontrol_new snd_bbfpro_gain_control = {
|
||||||
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||||
|
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
||||||
|
.index = 0,
|
||||||
|
.info = snd_bbfpro_gain_info,
|
||||||
|
.get = snd_bbfpro_gain_get,
|
||||||
|
.put = snd_bbfpro_gain_put
|
||||||
|
};
|
||||||
|
|
||||||
static const struct snd_kcontrol_new snd_bbfpro_vol_control = {
|
static const struct snd_kcontrol_new snd_bbfpro_vol_control = {
|
||||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
||||||
@ -2813,6 +2939,18 @@ static int snd_bbfpro_ctl_add(struct usb_mixer_interface *mixer, u8 reg,
|
|||||||
&knew, NULL);
|
&knew, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int snd_bbfpro_gain_add(struct usb_mixer_interface *mixer, u8 channel,
|
||||||
|
char *name)
|
||||||
|
{
|
||||||
|
struct snd_kcontrol_new knew = snd_bbfpro_gain_control;
|
||||||
|
|
||||||
|
knew.name = name;
|
||||||
|
knew.private_value = channel << SND_BBFPRO_GAIN_CHANNEL_SHIFT;
|
||||||
|
|
||||||
|
return add_single_ctl_with_resume(mixer, 0, snd_bbfpro_gain_resume,
|
||||||
|
&knew, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
static int snd_bbfpro_vol_add(struct usb_mixer_interface *mixer, u16 index,
|
static int snd_bbfpro_vol_add(struct usb_mixer_interface *mixer, u16 index,
|
||||||
char *name)
|
char *name)
|
||||||
{
|
{
|
||||||
@ -2860,6 +2998,29 @@ static int snd_bbfpro_controls_create(struct usb_mixer_interface *mixer)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Main out volume
|
||||||
|
for (i = 0 ; i < 12 ; ++i) {
|
||||||
|
snprintf(name, sizeof(name), "Main-Out %s", output[i]);
|
||||||
|
// Main outs are offset to 992
|
||||||
|
err = snd_bbfpro_vol_add(mixer,
|
||||||
|
i + SND_BBFPRO_MIXER_MAIN_OUT_CH_OFFSET,
|
||||||
|
name);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input gain
|
||||||
|
for (i = 0 ; i < 4 ; ++i) {
|
||||||
|
if (i < 2)
|
||||||
|
snprintf(name, sizeof(name), "Mic-%s Gain", input[i]);
|
||||||
|
else
|
||||||
|
snprintf(name, sizeof(name), "Line-%s Gain", input[i]);
|
||||||
|
|
||||||
|
err = snd_bbfpro_gain_add(mixer, i, name);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
// Control Reg 1
|
// Control Reg 1
|
||||||
err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG1,
|
err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG1,
|
||||||
SND_BBFPRO_CTL_REG1_CLK_OPTICAL,
|
SND_BBFPRO_CTL_REG1_CLK_OPTICAL,
|
||||||
|
Loading…
Reference in New Issue
Block a user