/* maximum Bluetooth volume value */
#define SCARLETT2_MAX_BLUETOOTH_VOLUME 30
+/* maximum front-panel sleep time in seconds (24 hours) */
+#define SCARLETT2_MAX_FP_SLEEP_TIME 86400
+
/* mixer range from -80dB to +12dB in 0.5dB steps */
#define SCARLETT2_MIXER_MIN_DB -80
#define SCARLETT2_MIXER_BIAS (-SCARLETT2_MIXER_MIN_DB * 2)
SCARLETT2_CONFIG_BLUETOOTH_VOLUME,
SCARLETT2_CONFIG_SPDIF_MODE,
SCARLETT2_CONFIG_SP_HP_MUTE,
+ SCARLETT2_CONFIG_FP_BRIGHTNESS,
+ SCARLETT2_CONFIG_FP_SLEEP_TIME,
SCARLETT2_CONFIG_COUNT
};
}
};
+/* Solo Gen 4, firmware version 2417 and above */
+static const struct scarlett2_config_set scarlett2_config_set_gen4_solo_2417 = {
+ .notifications = scarlett4_solo_notifications,
+ .param_buf_addr = 0xd8,
+ .items = {
+ [SCARLETT2_CONFIG_MSD_SWITCH] = {
+ .offset = 0x47, .size = 8, .activate = 4 },
+
+ [SCARLETT2_CONFIG_DIRECT_MONITOR] = {
+ .offset = 0x108, .size = 8, .activate = 12, .pbuf = 1 },
+
+ [SCARLETT2_CONFIG_PHANTOM_SWITCH] = {
+ .offset = 0x46, .size = 8, .activate = 9, .pbuf = 1,
+ .mute = 1 },
+
+ [SCARLETT2_CONFIG_LEVEL_SWITCH] = {
+ .offset = 0x3d, .size = 8, .activate = 10, .pbuf = 1,
+ .mute = 1 },
+
+ [SCARLETT2_CONFIG_AIR_SWITCH] = {
+ .offset = 0x3e, .size = 8, .activate = 11, .pbuf = 1 },
+
+ [SCARLETT2_CONFIG_PCM_INPUT_SWITCH] = {
+ .offset = 0x206, .size = 8, .activate = 25, .pbuf = 1 },
+
+ [SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN] = {
+ .offset = 0x232, .size = 16, .activate = 26 },
+
+ [SCARLETT2_CONFIG_FP_BRIGHTNESS] = {
+ .offset = 0x243, .size = 8, .activate = 27, .pbuf = 1 },
+
+ [SCARLETT2_CONFIG_FP_SLEEP_TIME] = {
+ .offset = 0x248, .size = 32, .activate = 29 }
+ }
+};
+
/* 2i2 Gen 4 */
static const struct scarlett2_config_set scarlett2_config_set_gen4_2i2 = {
.notifications = scarlett4_2i2_notifications,
/* 2i2 Gen 4, firmware version 2417 and above
*
- * Firmware 2417 shifted DIRECT_MONITOR_GAIN by 4 bytes; all other
- * offsets are unchanged from scarlett2_config_set_gen4_2i2.
+ * Firmware 2417 shifted DIRECT_MONITOR_GAIN by 4 bytes and added
+ * front-panel brightness and sleep controls; all other offsets are
+ * unchanged from scarlett2_config_set_gen4_2i2.
*/
static const struct scarlett2_config_set scarlett2_config_set_gen4_2i2_2417 = {
.notifications = scarlett4_2i2_notifications,
.offset = 0x14e, .size = 8, .activate = 18, .pbuf = 1 },
[SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN] = {
- .offset = 0x2a4, .size = 16, .activate = 36 }
+ .offset = 0x2a4, .size = 16, .activate = 36 },
+
+ [SCARLETT2_CONFIG_FP_BRIGHTNESS] = {
+ .offset = 0x2c7, .size = 8, .activate = 37, .pbuf = 1 },
+
+ [SCARLETT2_CONFIG_FP_SLEEP_TIME] = {
+ .offset = 0x2cc, .size = 32, .activate = 39 }
}
};
}
};
+/* 4i4 Gen 4, firmware version 2417 and above */
+static const struct scarlett2_config_set scarlett2_config_set_gen4_4i4_2417 = {
+ .notifications = scarlett4_4i4_notifications,
+ .param_buf_addr = 0x130,
+ .input_gain_tlv = db_scale_gen4_gain,
+ .autogain_status_texts = scarlett2_autogain_status_gen4,
+ .items = {
+ [SCARLETT2_CONFIG_MSD_SWITCH] = {
+ .offset = 0x5c, .size = 8, .activate = 4 },
+
+ [SCARLETT2_CONFIG_AUTOGAIN_SWITCH] = {
+ .offset = 0x13e, .size = 8, .activate = 10, .pbuf = 1 },
+
+ [SCARLETT2_CONFIG_AUTOGAIN_STATUS] = {
+ .offset = 0x140, .size = 8 },
+
+ [SCARLETT2_CONFIG_AG_MEAN_TARGET] = {
+ .offset = 0x13a, .size = 8, .activate = 23, .pbuf = 1 },
+
+ [SCARLETT2_CONFIG_AG_PEAK_TARGET] = {
+ .offset = 0x13b, .size = 8, .activate = 24, .pbuf = 1 },
+
+ [SCARLETT2_CONFIG_PHANTOM_SWITCH] = {
+ .offset = 0x5a, .size = 8, .activate = 11, .pbuf = 1,
+ .mute = 1 },
+
+ [SCARLETT2_CONFIG_INPUT_GAIN] = {
+ .offset = 0x5e, .size = 8, .activate = 12, .pbuf = 1 },
+
+ [SCARLETT2_CONFIG_LEVEL_SWITCH] = {
+ .offset = 0x4e, .size = 8, .activate = 13, .pbuf = 1,
+ .mute = 1 },
+
+ [SCARLETT2_CONFIG_SAFE_SWITCH] = {
+ .offset = 0x150, .size = 8, .activate = 14, .pbuf = 1 },
+
+ [SCARLETT2_CONFIG_AIR_SWITCH] = {
+ .offset = 0x50, .size = 8, .activate = 15, .pbuf = 1 },
+
+ [SCARLETT2_CONFIG_INPUT_SELECT_SWITCH] = {
+ .offset = 0x153, .size = 8, .activate = 16, .pbuf = 1 },
+
+ [SCARLETT2_CONFIG_INPUT_LINK_SWITCH] = {
+ .offset = 0x156, .size = 8, .activate = 17, .pbuf = 1 },
+
+ [SCARLETT2_CONFIG_MASTER_VOLUME] = {
+ .offset = 0x32, .size = 16 },
+
+ [SCARLETT2_CONFIG_HEADPHONE_VOLUME] = {
+ .offset = 0x3a, .size = 16 },
+
+ [SCARLETT2_CONFIG_POWER_EXT] = {
+ .offset = 0x168, .size = 8 },
+
+ [SCARLETT2_CONFIG_POWER_LOW] = {
+ .offset = 0x16d, .size = 8 },
+
+ [SCARLETT2_CONFIG_FP_BRIGHTNESS] = {
+ .offset = 0x3a9, .size = 8, .activate = 36, .pbuf = 1 },
+
+ [SCARLETT2_CONFIG_FP_SLEEP_TIME] = {
+ .offset = 0x3ac, .size = 32, .activate = 38 }
+ }
+};
+
/* Clarett USB and Clarett+ devices: 2Pre, 4Pre, 8Pre */
static const struct scarlett2_config_set scarlett2_config_set_clarett = {
.notifications = scarlett2_notifications,
struct snd_kcontrol *talkback_ctl;
struct snd_kcontrol *power_status_ctl;
struct snd_kcontrol *bluetooth_volume_ctl;
+ u8 fp_brightness;
+ u32 fp_sleep_time;
u8 mux[SCARLETT2_MUX_MAX];
u8 mix[SCARLETT2_MIX_MAX];
u8 monitor_mix[SCARLETT2_MONITOR_MIX_MAX];
static const struct scarlett2_device_info solo_gen4_info = {
.config_sets = (const struct scarlett2_config_set_entry[]) {
{ 2115, &scarlett2_config_set_gen4_solo },
+ { 2417, &scarlett2_config_set_gen4_solo_2417 },
{ }
},
.has_devmap = 1,
static const struct scarlett2_device_info s4i4_gen4_info = {
.config_sets = (const struct scarlett2_config_set_entry[]) {
{ 2089, &scarlett2_config_set_gen4_4i4 },
+ { 2417, &scarlett2_config_set_gen4_4i4_2417 },
{ }
},
.has_devmap = 1,
&private->bluetooth_volume_ctl);
}
+/*** Front Panel Brightness/Sleep Controls ***/
+
+static int scarlett2_update_fp(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ int err;
+
+ if (scarlett2_has_config_item(private, SCARLETT2_CONFIG_FP_BRIGHTNESS)) {
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_FP_BRIGHTNESS,
+ 1, &private->fp_brightness);
+ if (err < 0)
+ return err;
+ }
+
+ if (scarlett2_has_config_item(private, SCARLETT2_CONFIG_FP_SLEEP_TIME)) {
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_FP_SLEEP_TIME,
+ 1, &private->fp_sleep_time);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static const char * const scarlett2_fp_brightness_texts[] = {
+ "High", "Medium", "Low"
+};
+
+static int scarlett2_fp_brightness_ctl_info(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
+{
+ return snd_ctl_enum_info(uinfo, 1,
+ ARRAY_SIZE(scarlett2_fp_brightness_texts),
+ scarlett2_fp_brightness_texts);
+}
+
+static int scarlett2_fp_brightness_ctl_get(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct scarlett2_data *private = elem->head.mixer->private_data;
+
+ ucontrol->value.enumerated.item[0] = private->fp_brightness;
+ return 0;
+}
+
+static int scarlett2_fp_brightness_ctl_put(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+ int oval, val, err;
+
+ guard(mutex)(&private->data_mutex);
+
+ if (private->hwdep_in_use)
+ return -EBUSY;
+
+ oval = private->fp_brightness;
+ val = min(ucontrol->value.enumerated.item[0],
+ ARRAY_SIZE(scarlett2_fp_brightness_texts) - 1);
+
+ if (oval == val)
+ return 0;
+
+ private->fp_brightness = val;
+
+ err = scarlett2_usb_set_config(
+ mixer, SCARLETT2_CONFIG_FP_BRIGHTNESS, 0, val);
+
+ return err < 0 ? err : 1;
+}
+
+static const struct snd_kcontrol_new scarlett2_fp_brightness_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .name = "",
+ .info = scarlett2_fp_brightness_ctl_info,
+ .get = scarlett2_fp_brightness_ctl_get,
+ .put = scarlett2_fp_brightness_ctl_put,
+};
+
+static int scarlett2_fp_sleep_time_ctl_info(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = SCARLETT2_MAX_FP_SLEEP_TIME;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+
+static int scarlett2_fp_sleep_time_ctl_get(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct scarlett2_data *private = elem->head.mixer->private_data;
+
+ ucontrol->value.integer.value[0] = private->fp_sleep_time;
+ return 0;
+}
+
+static int scarlett2_fp_sleep_time_ctl_put(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+ u32 oval, val;
+ int err;
+
+ guard(mutex)(&private->data_mutex);
+
+ if (private->hwdep_in_use)
+ return -EBUSY;
+
+ oval = private->fp_sleep_time;
+ val = clamp(ucontrol->value.integer.value[0],
+ 0L, (long)SCARLETT2_MAX_FP_SLEEP_TIME);
+
+ if (oval == val)
+ return 0;
+
+ private->fp_sleep_time = val;
+
+ err = scarlett2_usb_set_config(
+ mixer, SCARLETT2_CONFIG_FP_SLEEP_TIME, 0, val);
+
+ return err < 0 ? err : 1;
+}
+
+static const struct snd_kcontrol_new scarlett2_fp_sleep_time_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .name = "",
+ .info = scarlett2_fp_sleep_time_ctl_info,
+ .get = scarlett2_fp_sleep_time_ctl_get,
+ .put = scarlett2_fp_sleep_time_ctl_put,
+};
+
+static int scarlett2_add_fp_ctls(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ int err;
+
+ if (scarlett2_has_config_item(private, SCARLETT2_CONFIG_FP_BRIGHTNESS)) {
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_fp_brightness_ctl, 0, 1,
+ "Front Panel Brightness", NULL);
+ if (err < 0)
+ return err;
+ }
+
+ if (scarlett2_has_config_item(private, SCARLETT2_CONFIG_FP_SLEEP_TIME)) {
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_fp_sleep_time_ctl, 0, 1,
+ "Front Panel Sleep Time", NULL);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
/*** S/PDIF Mode Controls ***/
static int scarlett2_update_spdif_mode(struct usb_mixer_interface *mixer)
if (err < 0)
return err;
+ err = scarlett2_update_fp(mixer);
+ if (err < 0)
+ return err;
+
err = scarlett2_update_spdif_mode(mixer);
if (err < 0)
return err;
if (err < 0)
return err;
+ /* Create the front-panel brightness/sleep controls */
+ err = scarlett2_add_fp_ctls(mixer);
+ if (err < 0)
+ return err;
+
/* Create the S/PDIF mode control */
err = scarlett2_add_spdif_mode_ctl(mixer);
if (err < 0)