From: sashiko-bot@kernel.org
To: "Ismaïl Bahloul" <i.bahloul01@gmail.com>, linux-sound@vger.kernel.org
Cc: linux-usb@vger.kernel.org
Subject: Re: [PATCH v8 3/8] ALSA: usb: babyfacepro: add mic preamp, phantom/pad and input trim
Date: Thu, 05 Nov 2026 15:32:00 +0000 [thread overview]
Message-ID: <sashiko-outbox-199312@kernel.org> (raw)
In-Reply-To: <20261105151553.223722-4-i.bahloul01@gmail.com>
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] ALSA: usb: babyfacepro: bf_trim_apply() and bf_phase_apply() clobber xpoints
- [Medium] ALSA: usb: babyfacepro: hardcoded Mic 1 names in babyface_create_preamp()
- [Low] ALSA: usb: babyfacepro: unchecked snd_ctl_new1() in babyface_create_preamp()
- [Low] ALSA: usb: babyfacepro: bf_phase_info() and bf_bool_info() duplicate code
--
commit 82ef805ee77606164dc3814bf736734ca60721d5
Author: Ismaïl Bahloul <i.bahloul01@gmail.com>
ALSA: usb: babyfacepro: add mic preamp, phantom/pad and input trim
This commit adds ALSA mixer controls for the input side of the RME Babyface
Pro, including phantom power, pad, instrument reference level, mic/instrument
preamp gains, phase invert, stereo split, and input trim.
> diff --git a/sound/usb/babyfacepro/babyfacepro-ctl.c b/sound/usb/babyfacepro/babyfacepro-ctl.c
> index 942d4732cbe07..fe6266ea118e6 100644
> --- a/sound/usb/babyfacepro/babyfacepro-ctl.c
> +++ b/sound/usb/babyfacepro/babyfacepro-ctl.c
> @@ -654,3 +654,627 @@ int babyface_create_masters(struct snd_usb_babyface *chip)
[ ... ]
> +int bf_phase_apply(struct snd_usb_babyface *chip, int mic, bool invert)
> +{
> + const struct bf_source *s = &bf_sources[mic];
> + int out, ret;
> + u16 flag;
> +
> + for (out = 0; out < 6; out++) {
> + u16 plain = chip->xpoint[out][mic][0];
> + u16 value = invert ? (u16)~plain : plain;
> +
> + /* The AN1/2 block is written without the rotating bits, as
> + * bf_xpoint_write() does.
> + */
> + flag = 0;
> + if (out) {
> + flag = bf_flag_cycle[chip->flag_cnt];
> + chip->flag_cnt = (chip->flag_cnt + 1) & 3;
> + }
> + ret = bf_vendor_write(chip, BF_REQ_CROSSPOINT, value,
> + (BF_REG_XPOINT_BASE_L +
> + BF_REG_XPOINT_STRIDE * out + s->idx_l) |
> + flag);
> + if (ret < 0)
> + return ret;
> + }
> + return 0;
> +}
[ ... ]
> +int bf_split_apply(struct snd_usb_babyface *chip, int pb, bool split)
> +{
> + const struct bf_source *s = &bf_sources[8 + pb];
> + u16 l = split ? 0x2000 : chip->xpoint[0][8 + pb][0];
> + u16 r = split ? 0x0000 : chip->xpoint[0][8 + pb][1];
> + int ret;
> +
> + /* The AN1/2 block. The same values also went to a "standard map"
> + * half at 0x0068, which is AS1/2's submix, not part of AN1/2: it
> + * overwrote AS1/2's own crosspoint for this playback pair.
> + */
> + ret = bf_vendor_write(chip, BF_REQ_CROSSPOINT, l,
> + BF_REG_XPOINT_BASE_L + s->idx_l);
> + if (ret < 0)
> + return ret;
> + return bf_vendor_write(chip, BF_REQ_CROSSPOINT, r,
> + BF_REG_XPOINT_BASE_R + s->idx_r);
> +}
[ ... ]
> +int bf_trim_apply(struct snd_usb_babyface *chip, int mic, int trim_db2)
> +{
> + int base = mic & ~1;
> + int sib = base + 1;
> + const struct bf_source *sb = &bf_sources[base];
> + const struct bf_source *ss = &bf_sources[sib];
> + u16 trim_raw = bf_master_16bit(trim_db2);
> + int ret;
> +
> + ret = bf_vendor_write(chip, BF_REQ_CROSSPOINT, trim_raw,
> + BF_REG_LOWMAP_BASE_L + sb->idx_l);
> + if (ret < 0)
> + return ret;
> + ret = bf_vendor_write(chip, BF_REQ_CROSSPOINT, trim_raw,
> + BF_REG_LOWMAP_BASE_R + sb->idx_r);
> + if (ret < 0)
> + return ret;
> + ret = bf_vendor_write(chip, BF_REQ_CROSSPOINT, trim_raw,
> + BF_REG_LOWMAP_BASE_L + ss->idx_l);
> + if (ret < 0)
> + return ret;
> + return bf_vendor_write(chip, BF_REQ_CROSSPOINT, trim_raw,
> + BF_REG_LOWMAP_BASE_R + ss->idx_r);
> +}
[ ... ]
> +static int bf_trim_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
> +{
> + struct snd_usb_babyface *chip = snd_kcontrol_chip(kctl);
> + int mic = kctl->private_value;
> + int sib = mic ^ 1;
> + int db = ucontrol->value.integer.value[0];
> + int ret = 0;
> +
> + if (db < -65 || db > 6)
> + return -EINVAL;
> +
> + scoped_guard(mutex, &chip->mutex) {
> + if (db == chip->trim[mic])
> + return 0;
> + ret = bf_trim_apply(chip, mic, db * 2);
> + if (ret < 0)
> + return ret;
[Severity: High]
As reported by Sashiko on v7 [1], does bf_trim_apply() clobber the AN1/2
crosspoint state and unmute live analog inputs into the main outputs?
In sound/usb/babyfacepro/babyfacepro.h, BF_REG_LOWMAP_BASE_L (0x0000) and
BF_REG_LOWMAP_BASE_R (0x001a) are the exact same register addresses as
BF_REG_XPOINT_BASE_L and BF_REG_XPOINT_BASE_R for output pair 0 (the AN1/2
main output). In babyface_write_default_mixer(), analog inputs AN1-4 have
chip->xpoint[0][0..3][0/1] initialized to 0 so live microphones are not
routed directly into the speakers.
When bf_trim_put() calls bf_trim_apply(), it converts trim_db2 via
bf_master_16bit() (clamped to [1, 0x4000] where 0 dB is 0x2000, compared to
BF_FADER_0DB = 0x16a0 on the crosspoint curve) and writes trim_raw directly
to all four L and R crosspoint registers of the input pair (base and sib) on
AN1/2, ignoring chip->xpoint[0][base/sib][0/1]. Touching Trim Volume at all
(even at -65 dB, or returning it to 0 dB which writes 0x2000 instead of
restoring chip->xpoint[0][base/sib][0/1]) unconditionally routes both inputs
of the pair to both L and R of AN1/2, overriding any muted state, fader
level, or panning.
Because chip->xpoint[0][base/sib][0/1] remains 0 in the cache while the
hardware holds 0x2000, a subsequent write of 0 to the crosspoint control hits
the early return in bf_xpoint_put():
sound/usb/babyfacepro/babyfacepro-ctl.c:bf_xpoint_put() {
guard(mutex)(&chip->mutex);
if (l == chip->xpoint[out][src][0] && r == chip->xpoint[out][src][1])
return 0;
ret = bf_xpoint_write(chip, out, src, l, r);
}
and cannot re-mute the hardware.
Additionally, because bf_phase_apply(), bf_split_apply(), bf_trim_apply(),
and bf_xpoint_put() write to the shared crosspoint registers without
combining their state:
- bf_trim_apply() (both in bf_trim_put() and in bf_state_apply_flags() where
the trim loop runs after the phase loop) overwrites the negated out = 0 L
register written by bf_phase_apply().
- bf_phase_apply() only negates BF_REG_XPOINT_BASE_L (leaving R un-inverted,
and writing ~0 = 0xffff when plain == 0).
- Any subsequent bf_xpoint_put() writes plain l/r values, silently wiping out
active phase, stereo split, or trim settings in hardware while their cached
control states remain active.
[ ... ]
> +static int bf_bool_info(struct snd_kcontrol *kctl,
> + struct snd_ctl_elem_info *uinfo)
> +{
> + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
> + uinfo->count = 1;
> + uinfo->value.integer.min = 0;
> + uinfo->value.integer.max = 1;
> + return 0;
> +}
[Severity: Low]
This isn't a bug, but bf_bool_info() is identical to bf_phase_info() added
earlier in sound/usb/babyfacepro/babyfacepro-ctl.c (and to the existing
bf_mute_info()). Could these controls use the standard ALSA helper
snd_ctl_boolean_mono_info() from include/sound/control.h instead of
duplicating the callback?
[ ... ]
> +int babyface_create_preamp(struct snd_usb_babyface *chip)
> +{
> + struct snd_kcontrol *kctl;
> + int i, err;
> +
> + /* The control name is the type and .index is the channel (0 =
> + * Mic 1/AN1, 1 = Mic 2/AN2), the convention the companion mixer
> + * and existing asound.state files use.
> + */
> + for (i = 0; i < 2; i++) {
> + u16 bit = i == 0 ? BF_PREAMP_48V_MIC1 : BF_PREAMP_48V_MIC2;
> +
> + kctl = snd_ctl_new1(&(struct snd_kcontrol_new){
> + .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
> + .name = "Phantom Power Mic 1",
> + .index = i,
> + .info = bf_bool_info,
> + .get = bf_phantom_get,
> + .put = bf_phantom_put,
> + .private_value = bit,
> + }, chip);
> + if (!kctl)
> + return -ENOMEM;
> + err = snd_ctl_add(chip->card, kctl);
> + if (err < 0)
> + return err;
> + }
> +
> + for (i = 0; i < 2; i++) {
> + u16 bit = i == 0 ? BF_PREAMP_PAD_MIC1 : BF_PREAMP_PAD_MIC2;
> +
> + kctl = snd_ctl_new1(&(struct snd_kcontrol_new){
> + .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
> + .name = "Pad Mic 1",
> + .index = i,
> + .info = bf_bool_info,
> + .get = bf_phantom_get,
> + .put = bf_phantom_put,
> + .private_value = bit,
> + }, chip);
> + if (!kctl)
> + return -ENOMEM;
> + err = snd_ctl_add(chip->card, kctl);
> + if (err < 0)
> + return err;
> + }
> +
> + kctl = snd_ctl_new1(&(struct snd_kcontrol_new){
> + .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
> + .name = "Instrument Ref Level",
> + .info = bf_reflevel_info,
> + .get = bf_reflevel_get,
> + .put = bf_reflevel_put,
> + }, chip);
> + err = snd_ctl_add(chip->card, kctl);
> + if (err < 0)
> + return err;
[Severity: Low]
If snd_ctl_new1() returns NULL for the "Instrument Ref Level" control in
babyface_create_preamp(), passing NULL to snd_ctl_add() will return -EINVAL
instead of -ENOMEM. Should this check if (!kctl) return -ENOMEM; before
calling snd_ctl_add() like the other control creation calls in this function?
> +
> + for (i = 0; i < 4; i++) {
> + kctl = snd_ctl_new1(&(struct snd_kcontrol_new){
> + .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
> + .name = "Mic 1 Capture Volume",
> + .index = i,
> + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
> + SNDRV_CTL_ELEM_ACCESS_TLV_READ,
> + .info = bf_gain_info,
> + .get = bf_gain_get,
> + .put = bf_gain_put,
> + .tlv.p = bf_gain_tlv,
> + .private_value = i,
> + }, chip);
[Severity: Medium]
As reported by Sashiko on v7 [1], why do all three loops in
babyface_create_preamp() hardcode "Mic 1" in .name ("Phantom Power Mic 1",
"Pad Mic 1", and "Mic 1 Capture Volume") across all loop iterations?
When i > 0, this registers Mic 2 phantom power and pad as "Phantom Power
Mic 1",1 and "Pad Mic 1",1, and registers Mic 2, Instrument 3, and Instrument
4 capture gains as "Mic 1 Capture Volume" with indices 1, 2, and 3. Should
these either use a channel-neutral name when indexing by i, or format the
per-channel name as babyface_create_trim() does?
[1] https://lore.kernel.org/r/20261027113140.134257-4-i.bahloul01@gmail.com
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261105151553.223722-1-i.bahloul01@gmail.com?part=3
next prev parent reply other threads:[~2026-11-05 15:32 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-05 15:15 [PATCH v8 0/8] ALSA: usb: add RME Babyface Pro driver (proprietary mode) Ismaïl Bahloul
2026-11-05 15:15 ` [PATCH v8 1/8] ALSA: usb: add RME Babyface Pro driver core (probe, PCM stream) Ismaïl Bahloul
2026-11-05 16:24 ` sashiko-bot
2026-11-05 15:15 ` [PATCH v8 2/8] ALSA: usb: babyfacepro: add output masters and crosspoint routing Ismaïl Bahloul
2026-11-05 15:31 ` sashiko-bot
2026-11-05 15:15 ` [PATCH v8 3/8] ALSA: usb: babyfacepro: add mic preamp, phantom/pad and input trim Ismaïl Bahloul
2026-11-05 15:32 ` sashiko-bot [this message]
2026-11-05 15:15 ` [PATCH v8 4/8] ALSA: usb: babyfacepro: add routing flags and varispeed pitch Ismaïl Bahloul
2026-11-05 15:32 ` sashiko-bot
2026-11-05 15:15 ` [PATCH v8 5/8] ALSA: usb: babyfacepro: add S3 suspend/resume Ismaïl Bahloul
2026-11-05 16:45 ` sashiko-bot
2026-11-05 15:15 ` [PATCH v8 6/8] ALSA: usb: babyfacepro: add the front-panel poll and controls Ismaïl Bahloul
2026-11-05 17:03 ` sashiko-bot
2026-11-05 15:15 ` [PATCH v8 7/8] ALSA: usb: babyfacepro: add the hardware DSP EQ Ismaïl Bahloul
2026-11-05 17:19 ` sashiko-bot
2026-11-05 15:15 ` [PATCH v8 8/8] Documentation: sound: add the Babyface Pro proprietary-mode design doc Ismaïl Bahloul
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-199312@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=i.bahloul01@gmail.com \
--cc=linux-sound@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox