// SPDX-License-Identifier: GPL-2.0-or-later /* * Scarlett Driver for ALSA * * Copyright (c) 2013 by Tobias Hoffmann * Copyright (c) 2013 by Robin Gareus <robin at gareus.org> * Copyright (c) 2002 by Takashi Iwai <tiwai at suse.de> * Copyright (c) 2014 by Chris J Arges <chris.j.arges at canonical.com> * * Many codes borrowed from audio.c by * Alan Cox (alan at lxorguk.ukuu.org.uk) * Thomas Sailer (sailer at ife.ee.ethz.ch) * * Code cleanup: * David Henningsson <david.henningsson at canonical.com> */ /* * Rewritten and extended to support more models, e.g. Scarlett 18i8. * * Auto-detection via UAC2 is not feasible to properly discover the vast * majority of features. It's related to both Linux/ALSA's UAC2 as well as * Focusrite's implementation of it. Eventually quirks may be sufficient but * right now it's a major headache to work around these things. * * NB. Neither the OSX nor the win driver provided by Focusrite performs * discovery, they seem to operate the same as this driver. */ /* Mixer Interface for the Focusrite Scarlett 18i6 audio interface. * * The protocol was reverse engineered by looking at communication between * Scarlett MixControl (v 1.2.128.0) and the Focusrite(R) Scarlett 18i6 * (firmware v305) using wireshark and usbmon in January 2013. * Extended in July 2013. * * this mixer gives complete access to all features of the device: * - change Impedance of inputs (Line-in, Mic / Instrument, Hi-Z) * - select clock source * - dynamic input to mixer-matrix assignment * - 18 x 6 mixer-matrix gain stages * - bus routing & volume control * - automatic re-initialization on connect if device was power-cycled * * USB URB commands overview (bRequest = 0x01 = UAC2_CS_CUR) * wIndex * 0x01 Analog Input line/instrument impedance switch, wValue=0x0901 + * channel, data=Line/Inst (2bytes) * pad (-10dB) switch, wValue=0x0b01 + channel, data=Off/On (2bytes) * ?? wValue=0x0803/04, ?? (2bytes) * 0x0a Master Volume, wValue=0x0200+bus[0:all + only 1..4?] data(2bytes) * Bus Mute/Unmute wValue=0x0100+bus[0:all + only 1..4?], data(2bytes) * 0x28 Clock source, wValue=0x0100, data={1:int,2:spdif,3:adat} (1byte) * 0x29 Set Sample-rate, wValue=0x0100, data=sample-rate(4bytes) * 0x32 Mixer mux, wValue=0x0600 + mixer-channel, data=input-to-connect(2bytes) * 0x33 Output mux, wValue=bus, data=input-to-connect(2bytes) * 0x34 Capture mux, wValue=0...18, data=input-to-connect(2bytes) * 0x3c Matrix Mixer gains, wValue=mixer-node data=gain(2bytes) * ?? [sometimes](4bytes, e.g 0x000003be 0x000003bf ...03ff) * * USB reads: (i.e. actually issued by original software) * 0x01 wValue=0x0901+channel (1byte!!), wValue=0x0b01+channed (1byte!!) * 0x29 wValue=0x0100 sample-rate(4bytes) * wValue=0x0200 ?? 1byte (only once) * 0x2a wValue=0x0100 ?? 4bytes, sample-rate2 ?? * * USB reads with bRequest = 0x03 = UAC2_CS_MEM * 0x3c wValue=0x0002 1byte: sync status (locked=1) * wValue=0x0000 18*2byte: peak meter (inputs) * wValue=0x0001 8(?)*2byte: peak meter (mix) * wValue=0x0003 6*2byte: peak meter (pcm/daw) * * USB write with bRequest = 0x03 * 0x3c Save settings to hardware: wValue=0x005a, data=0xa5 * * * <ditaa> * /--------------\ 18chn 6chn /--------------\ * | Hardware in +--+-------\ /------+--+ ALSA PCM out | * \--------------/ | | | | \--------------/ * | | | | * | v v | * | +---------------+ | * | \ Matrix Mux / | * | +-----+-----+ | * | | | * | | 18chn | * | v | * | +-----------+ | * | | Mixer | | * | | Matrix | | * | | | | * | | 18x6 Gain | | * | | stages | | * | +-----+-----+ | * | | | * | | | * | 18chn | 6chn | 6chn * v v v * ========================= * +---------------+ +--—------------+ * \ Output Mux / \ Capture Mux / * +-----+-----+ +-----+-----+ * | | * | 6chn | * v | * +-------------+ | * | Master Gain | | * +------+------+ | * | | * | 6chn | 18chn * | (3 stereo pairs) | * /--------------\ | | /--------------\ * | Hardware out |<--/ \-->| ALSA PCM in | * \--------------/ \--------------/ * </ditaa> * */ #include <linux/slab.h> #include <linux/usb.h> #include <linux/usb/audio-v2.h> #include <sound/core.h> #include <sound/control.h> #include <sound/tlv.h> #include "usbaudio.h" #include "mixer.h" #include "helper.h" #include "power.h" #include "mixer_scarlett.h" /* some gui mixers can't handle negative ctl values */ #define SND_SCARLETT_LEVEL_BIAS … #define SND_SCARLETT_MATRIX_IN_MAX … #define SND_SCARLETT_CONTROLS_MAX … #define SND_SCARLETT_OFFSETS_MAX … enum { … }; enum { … }; struct scarlett_mixer_elem_enum_info { … }; struct scarlett_mixer_control { … }; struct scarlett_device_info { … }; /********************** Enum Strings *************************/ static const struct scarlett_mixer_elem_enum_info opt_pad = …; static const struct scarlett_mixer_elem_enum_info opt_gain = …; static const struct scarlett_mixer_elem_enum_info opt_impedance = …; static const struct scarlett_mixer_elem_enum_info opt_clock = …; static const struct scarlett_mixer_elem_enum_info opt_sync = …; static int scarlett_ctl_switch_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) { … } static int scarlett_ctl_switch_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { … } static int scarlett_ctl_switch_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { … } static int scarlett_ctl_resume(struct usb_mixer_elem_list *list) { … } static int scarlett_ctl_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) { … } static int scarlett_ctl_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { … } static int scarlett_ctl_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { … } static void scarlett_generate_name(int i, char *dst, int offsets[]) { … } static int scarlett_ctl_enum_dynamic_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) { … } static int scarlett_ctl_enum_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) { … } static int scarlett_ctl_enum_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { … } static int scarlett_ctl_enum_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { … } static int scarlett_ctl_enum_resume(struct usb_mixer_elem_list *list) { … } static int scarlett_ctl_meter_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { … } static const struct snd_kcontrol_new usb_scarlett_ctl_switch = …; static const DECLARE_TLV_DB_SCALE(db_scale_scarlett_gain, -12800, 100, 0); static const struct snd_kcontrol_new usb_scarlett_ctl = …; static const struct snd_kcontrol_new usb_scarlett_ctl_master = …; static const struct snd_kcontrol_new usb_scarlett_ctl_enum = …; static const struct snd_kcontrol_new usb_scarlett_ctl_dynamic_enum = …; static const struct snd_kcontrol_new usb_scarlett_ctl_sync = …; static int add_new_ctl(struct usb_mixer_interface *mixer, const struct snd_kcontrol_new *ncontrol, usb_mixer_elem_resume_func_t resume, int index, int offset, int num, int val_type, int channels, const char *name, const struct scarlett_mixer_elem_enum_info *opt, struct usb_mixer_elem_info **elem_ret ) { … } static int add_output_ctls(struct usb_mixer_interface *mixer, int index, const char *name, const struct scarlett_device_info *info) { … } /********************** device-specific config *************************/ /* untested... */ static const struct scarlett_device_info s6i6_info = …; /* untested... */ static const struct scarlett_device_info s8i6_info = …; static const struct scarlett_device_info s18i6_info = …; static const struct scarlett_device_info s18i8_info = …; static const struct scarlett_device_info s18i20_info = …; static int scarlett_controls_create_generic(struct usb_mixer_interface *mixer, const struct scarlett_device_info *info) { … } /* * Create and initialize a mixer for the Focusrite(R) Scarlett */ int snd_scarlett_controls_create(struct usb_mixer_interface *mixer) { … }