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 <g@b4.vu> Link: https://lore.kernel.org/r/Zj8zCTjzPsTDENN+@m.b4.vu Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
da0713fff5
commit
d5ca9ad58e
@ -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.
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user