Some quirky devices tune their volume by linearly tuning the voltage
level (linear volume). In other words, such devices has a linear TLV
mapping of DECLARE_TLV_DB_LINEAR(scale, TLV_DB_GAIN_MUTE, 0).
Add quirk flags MIXER_PLAYBACK_LINEAR_VOL and MIXER_CAPTURE_LINEAR_VOL
to represent this case respectively for playback and capture mixers.
No functional change intended.
Signed-off-by: Rong Zhang <i@rong.moe>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Link: https://patch.msgid.link/20260303194805.266158-7-i@rong.moe
Skip the probe-time interface setup (usb_set_interface,
init_pitch, init_sample_rate); redundant with
snd_usb_endpoint_prepare() at stream-open time
+ * bit 27: ``mixer_playback_linear_vol``
+ Set linear volume mapping for devices where the playback volume
+ control value is mapped to voltage (instead of dB) level linearly.
+ In short: ``x(raw) = (raw - raw_min) / (raw_max - raw_min)``;
+ ``V(x) = k * x``; ``dB(x) = 20 * log10(x)``. Overrides bit 24
+ * bit 28: ``mixer_capture_linear_vol``
+ Similar to bit 27 but for capture streams. Overrides bit 25
This module supports multiple devices, autoprobe and hotplugging.
usb_audio_dbg(chip, "something wrong in kctl name %s\n", id->name);
}
+static void snd_usb_mixer_fu_quirk_linear_scale(struct usb_mixer_interface *mixer,
+ struct usb_mixer_elem_info *cval,
+ struct snd_kcontrol *kctl)
+{
+ static const DECLARE_TLV_DB_LINEAR(scale, TLV_DB_GAIN_MUTE, 0);
+
+ if (cval->min_mute) {
+ /*
+ * We are clearing SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
+ * resulting in min_mute being a no-op.
+ */
+ usb_audio_warn(mixer->chip, "LINEAR_VOL overrides MIN_MUTE\n");
+ }
+
+ kctl->tlv.p = scale;
+ kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+}
+
void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer,
struct usb_mixer_elem_info *cval, int unitid,
struct snd_kcontrol *kctl)
"applying capture min mute quirk\n");
cval->min_mute = 1;
}
+
+ if (mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL)
+ if (cval->control == UAC_FU_VOLUME && strstr(kctl->id.name, "Playback")) {
+ usb_audio_info(mixer->chip,
+ "applying playback linear volume quirk\n");
+ snd_usb_mixer_fu_quirk_linear_scale(mixer, cval, kctl);
+ }
+
+ if (mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL)
+ if (cval->control == UAC_FU_VOLUME && strstr(kctl->id.name, "Capture")) {
+ usb_audio_info(mixer->chip,
+ "applying capture linear volume quirk\n");
+ snd_usb_mixer_fu_quirk_linear_scale(mixer, cval, kctl);
+ }
+
/* ALSA-ify some Plantronics headset control names */
if (USB_ID_VENDOR(mixer->chip->usb_id) == 0x047f &&
(cval->control == UAC_FU_MUTE || cval->control == UAC_FU_VOLUME))
QUIRK_STRING_ENTRY(MIXER_PLAYBACK_MIN_MUTE),
QUIRK_STRING_ENTRY(MIXER_CAPTURE_MIN_MUTE),
QUIRK_STRING_ENTRY(SKIP_IFACE_SETUP),
+ QUIRK_STRING_ENTRY(MIXER_PLAYBACK_LINEAR_VOL),
+ QUIRK_STRING_ENTRY(MIXER_CAPTURE_LINEAR_VOL),
NULL
};
* Skip the probe-time interface setup (usb_set_interface,
* init_pitch, init_sample_rate); redundant with
* snd_usb_endpoint_prepare() at stream-open time
+ * QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL
+ * Set linear volume mapping for devices where the playback volume control
+ * value is mapped to voltage (instead of dB) level linearly. In short:
+ * x(raw) = (raw - raw_min) / (raw_max - raw_min); V(x) = k * x;
+ * dB(x) = 20 * log10(x). Overrides QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE
+ * QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL
+ * Similar to QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL, but for capture streams.
+ * Overrides QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE
*/
enum {
QUIRK_TYPE_MIXER_PLAYBACK_MIN_MUTE = 24,
QUIRK_TYPE_MIXER_CAPTURE_MIN_MUTE = 25,
QUIRK_TYPE_SKIP_IFACE_SETUP = 26,
+ QUIRK_TYPE_MIXER_PLAYBACK_LINEAR_VOL = 27,
+ QUIRK_TYPE_MIXER_CAPTURE_LINEAR_VOL = 28,
/* Please also edit snd_usb_audio_quirk_flag_names */
};
#define QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE QUIRK_FLAG(MIXER_PLAYBACK_MIN_MUTE)
#define QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE QUIRK_FLAG(MIXER_CAPTURE_MIN_MUTE)
#define QUIRK_FLAG_SKIP_IFACE_SETUP QUIRK_FLAG(SKIP_IFACE_SETUP)
+#define QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL QUIRK_FLAG(MIXER_PLAYBACK_LINEAR_VOL)
+#define QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL QUIRK_FLAG(MIXER_CAPTURE_LINEAR_VOL)
#endif /* __USBAUDIO_H */