From patchwork Sat May 11 08:57:45 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Geoffrey D. Bennett" X-Patchwork-Id: 13662300 Received: from m.b4.vu (m.b4.vu [203.16.231.148]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 41277524B8 for ; Sat, 11 May 2024 08:57:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=203.16.231.148 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715417874; cv=none; b=Ab3FFkZ6ekjNX05s6LQWyGjTP6mybuyrCPypKleKzuT0uMZcFLj2zRUUrxUKbUfn3PNlXhcItbw9HcVXWSzBev4FaEHS1RHtAIfsn42vJoqFGcnItIMtcmy5lSlygMZbtdPlqMeJqgdxIj0+oDOs9l0eV7r3yJjFgcnBQYY9/Bs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715417874; c=relaxed/simple; bh=K3Ec2XRwwiP9b+Lkk1EgIN434oPpg0DkWVim/uA+ihk=; h=Date:From:To:Cc:Subject:Message-ID:MIME-Version:Content-Type: Content-Disposition; b=qCRzWR2JF4AlaWThRP/9xh+q3zz1XgoQ8CccPO94WZT5jpCpv32pEHtwNiPPSWnm/yq3XuW72XhZlA8NQ02aCXdNSBBRCJfPCx3Fobh9PaBo5Inwby/Fqc7sv6rN+Jzbt4Bma54sn7V2J3i7ENCtza+Z6OQY3qTp3L9EbLttLFw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=b4.vu; spf=pass smtp.mailfrom=b4.vu; dkim=pass (2048-bit key) header.d=b4.vu header.i=@b4.vu header.b=FArYYXtP; arc=none smtp.client-ip=203.16.231.148 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=b4.vu Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=b4.vu Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=b4.vu header.i=@b4.vu header.b="FArYYXtP" Received: by m.b4.vu (Postfix, from userid 1000) id 03298604B9CB; Sat, 11 May 2024 18:27:45 +0930 (ACST) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu 03298604B9CB DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1715417866; bh=tup50UjBihNqJADS5bYjuq/cSF/Vyak+krKTq9Pbux0=; h=Date:From:To:Cc:Subject:From; b=FArYYXtPbamJkr3zLBxw8xu1icHZZQ3iMac63pERJH1buPRWjKVIJmPeVzgxzn7Ax NzOTDgR/J7R+YXtscBLAy7LtcRuQUeTgWDsQqeyCCkOUl2fSydma00nwTVxNlHuBro GQU0TXJo/oRieVZfb6jgOPWb7n5XJ0MGB+kEPikAJ4juygN6hFk7NTmjjrrvqNcB0g 5RS9sfYmKMaczqNVMoM4XqgpToQ909qHBBCP0UOCYTDmdlVzkBhZZWW6jv/nZdK1By gc2E44ojOjp8RuOJ8xilwpsvGzG6VN7Zf3YmRWoH4IAfuFeAoZopQ8su4wdTAChF8U E/2/kFL2kawqQ== Date: Sat, 11 May 2024 18:27:45 +0930 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , linux-sound@vger.kernel.org Subject: [PATCH v2] ALSA: scarlett2: Add S/PDIF source selection controls Message-ID: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline 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 --- Notes: v2: Replaced u8 -1 with u8 0xff 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 b251d25f2a85..2dd03b46964e 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. */