From 95dd61cc85f403b852cdfceda2fb590ea5e1b168 Mon Sep 17 00:00:00 2001 From: "Geoffrey D. Bennett" Date: Wed, 8 May 2024 22:21:34 +0930 Subject: [PATCH] ALSA: scarlett2: Add S/PDIF source selection controls Add S/PDIF Source/Digital I/O Mode selection controls for the Scarlett 3rd Gen 18i8/18i20 and Clarett 4Pre/8Pre interfaces. These models have both coax S/PDIF and optical inputs, and the optical inputs are switchable between being used as S/PDIF and ADAT inputs. The Scarlett 3rd Gen 18i20 also has a "Dual ADAT" mode for 8-channel audio at 88.2/96kHz. Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 179 ++++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index b251d25f2a858a..2dd03b46964ed0 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -541,6 +541,7 @@ enum { SCARLETT2_CONFIG_PCM_INPUT_SWITCH, SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN, SCARLETT2_CONFIG_BLUETOOTH_VOLUME, + SCARLETT2_CONFIG_SPDIF_MODE, SCARLETT2_CONFIG_COUNT }; @@ -754,6 +755,9 @@ static const struct scarlett2_config_set scarlett2_config_set_gen3c = { [SCARLETT2_CONFIG_TALKBACK_MAP] = { .offset = 0xb0, .size = 16, .activate = 10 }, + + [SCARLETT2_CONFIG_SPDIF_MODE] = { + .offset = 0x94, .size = 8, .activate = 6 }, } }; @@ -977,6 +981,9 @@ static const struct scarlett2_config_set scarlett2_config_set_clarett = { [SCARLETT2_CONFIG_STANDALONE_SWITCH] = { .offset = 0x8d, .size = 8, .activate = 6 }, + + [SCARLETT2_CONFIG_SPDIF_MODE] = { + .offset = 0x9e, .size = 8, .activate = 4 }, } }; @@ -1147,6 +1154,11 @@ struct scarlett2_device_info { /* has a Bluetooth module with volume control */ u8 has_bluetooth; + /* S/PDIF Source/Digital I/O mode control */ + const char * const spdif_mode_control_name; + const u8 *spdif_mode_values; + const char * const *spdif_mode_texts; + /* remap analogue outputs; 18i8 Gen 3 has "line 3/4" connected * internally to the analogue 7/8 outputs */ @@ -1255,6 +1267,7 @@ struct scarlett2_data { u8 standalone_switch; u8 power_status; u8 bluetooth_volume; + u8 spdif_mode; u8 meter_level_map[SCARLETT2_MAX_METERS]; struct snd_kcontrol *sync_ctl; struct snd_kcontrol *master_vol_ctl; @@ -1582,6 +1595,14 @@ static const struct scarlett2_device_info s8i6_gen3_info = { } }; +static const u8 scarlett2_spdif_s18i8_gen3_values[] = { 0, 2, 0xff }; + +static const char * const scarlett2_spdif_s18i8_gen3_texts[] = { + "RCA", + "Optical", + NULL +}; + static const struct scarlett2_device_info s18i8_gen3_info = { .config_set = &scarlett2_config_set_gen3c, .has_speaker_switching = 1, @@ -1591,6 +1612,10 @@ static const struct scarlett2_device_info s18i8_gen3_info = { .phantom_count = 2, .inputs_per_phantom = 2, + .spdif_mode_control_name = "S/PDIF Mode Capture Enum", + .spdif_mode_values = scarlett2_spdif_s18i8_gen3_values, + .spdif_mode_texts = scarlett2_spdif_s18i8_gen3_texts, + .line_out_remap_enable = 1, .line_out_remap = { 0, 1, 6, 7, 2, 3, 4, 5 }, .line_out_unmap = { 0, 1, 4, 5, 6, 7, 2, 3 }, @@ -1661,6 +1686,15 @@ static const struct scarlett2_device_info s18i8_gen3_info = { } }; +static const u8 scarlett2_spdif_s18i20_gen3_values[] = { 0, 6, 1, 0xff }; + +static const char * const scarlett2_spdif_s18i20_gen3_texts[] = { + "S/PDIF RCA", + "S/PDIF Optical", + "Dual ADAT", + NULL +}; + static const struct scarlett2_device_info s18i20_gen3_info = { .config_set = &scarlett2_config_set_gen3c, .has_speaker_switching = 1, @@ -1671,6 +1705,10 @@ static const struct scarlett2_device_info s18i20_gen3_info = { .phantom_count = 2, .inputs_per_phantom = 4, + .spdif_mode_control_name = "Digital I/O Mode Capture Enum", + .spdif_mode_values = scarlett2_spdif_s18i20_gen3_values, + .spdif_mode_texts = scarlett2_spdif_s18i20_gen3_texts, + .line_out_descrs = { "Monitor 1 L", "Monitor 1 R", @@ -2019,11 +2057,24 @@ static const struct scarlett2_device_info clarett_2pre_info = { } }; +static const u8 scarlett2_spdif_clarett_values[] = { 0, 1, 2, 0xff }; + +static const char * const scarlett2_spdif_clarett_texts[] = { + "None", + "Optical", + "RCA", + NULL +}; + static const struct scarlett2_device_info clarett_4pre_info = { .config_set = &scarlett2_config_set_clarett, .level_input_count = 2, .air_input_count = 4, + .spdif_mode_control_name = "S/PDIF Source Capture Enum", + .spdif_mode_values = scarlett2_spdif_clarett_values, + .spdif_mode_texts = scarlett2_spdif_clarett_texts, + .line_out_descrs = { "Monitor L", "Monitor R", @@ -2076,6 +2127,10 @@ static const struct scarlett2_device_info clarett_8pre_info = { .level_input_count = 2, .air_input_count = 8, + .spdif_mode_control_name = "S/PDIF Source Capture Enum", + .spdif_mode_values = scarlett2_spdif_clarett_values, + .spdif_mode_texts = scarlett2_spdif_clarett_texts, + .line_out_descrs = { "Monitor L", "Monitor R", @@ -7885,6 +7940,121 @@ static int scarlett2_add_bluetooth_volume_ctl( &private->bluetooth_volume_ctl); } +/*** S/PDIF Mode Controls ***/ + +static int scarlett2_update_spdif_mode(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + int err, i; + u8 mode; + const u8 *mode_values = private->info->spdif_mode_values; + + if (!private->info->spdif_mode_control_name) + return 0; + + err = scarlett2_usb_get_config(mixer, SCARLETT2_CONFIG_SPDIF_MODE, + 1, &mode); + if (err < 0) + return err; + + private->spdif_mode = 0; + + for (i = 0; *mode_values != 0xff; i++, mode_values++) + if (*mode_values == mode) { + private->spdif_mode = i; + break; + } + + return 0; +} + +static int scarlett2_spdif_mode_ctl_info(struct snd_kcontrol *kctl, + struct snd_ctl_elem_info *uinfo) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct scarlett2_data *private = elem->head.mixer->private_data; + const char * const *mode_texts = private->info->spdif_mode_texts; + int count = 0; + + while (*mode_texts++) + count++; + + return snd_ctl_enum_info(uinfo, 1, count, + private->info->spdif_mode_texts); +} + +static int scarlett2_spdif_mode_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->spdif_mode; + return 0; +} + +static int scarlett2_spdif_mode_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 = 0; + int i; + + mutex_lock(&private->data_mutex); + + oval = private->spdif_mode; + val = ucontrol->value.enumerated.item[0]; + + if (val < 0) { + err = -EINVAL; + goto unlock; + } + + for (i = 0; i <= val; i++) + if (private->info->spdif_mode_values[i] == 0xff) { + err = -EINVAL; + goto unlock; + } + + if (oval == val) + goto unlock; + + private->spdif_mode = val; + + err = scarlett2_usb_set_config( + mixer, SCARLETT2_CONFIG_SPDIF_MODE, 0, + private->info->spdif_mode_values[val]); + if (!err) + err = 1; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static const struct snd_kcontrol_new scarlett2_spdif_mode_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = scarlett2_spdif_mode_ctl_info, + .get = scarlett2_spdif_mode_ctl_get, + .put = scarlett2_spdif_mode_ctl_put, +}; + +static int scarlett2_add_spdif_mode_ctl(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + + if (!private->info->spdif_mode_control_name) + return 0; + + return scarlett2_add_new_ctl(mixer, &scarlett2_spdif_mode_ctl, + 0, 1, + private->info->spdif_mode_control_name, + NULL); +} + /*** Notification Handlers ***/ /* Notify on sync change */ @@ -8797,6 +8967,10 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer) if (err < 0) return err; + err = scarlett2_update_spdif_mode(mixer); + if (err < 0) + return err; + err = scarlett2_update_mix(mixer); if (err < 0) return err; @@ -8929,6 +9103,11 @@ static int snd_scarlett2_controls_create( if (err < 0) return err; + /* Create the S/PDIF mode control */ + err = scarlett2_add_spdif_mode_ctl(mixer); + if (err < 0) + return err; + /* Set the access mode of controls disabled during * autogain/phantom power switching. */