From patchwork Tue Mar 12 18:33:14 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: 13590466 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 4BB35139572 for ; Tue, 12 Mar 2024 18:33:17 +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=1710268401; cv=none; b=BJDEvNg2Wl6YYp+07/LGkKj31B3XCpvlesIWcWz/3C4klrUKau7XkyfogSq6Dovc0Y4kN8ZdbnwSRnicYRHQRGLJypjKENak8n7yi5f/Eioj004xIwAgMS/7GfuT+mcLPg+v1A6s+lsFGUmggSwCf+rQh8sZ7iPb10HG+uIy7xI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1710268401; c=relaxed/simple; bh=IT1PgdHr1IELX9wOWs02PxIMQNQGJCCc93cvm36bz8g=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=V5LBanH0DrbXYZtMqsjsUpGmUiE8MGfMABgcMrhX4kixwciceY9jnP6mQLVH+7oejf3+EqBeKLhQpugzrLDX6rBxrShE6SEUSatMLs0hTmQ5PBXip2/jHYg9YP/g9ucB8qHEpY0EdZxl0jqV3bPBJcR+TrK66HxYReH3QKf28gA= 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=MYX92LAY; 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="MYX92LAY" Received: by m.b4.vu (Postfix, from userid 1000) id DCD4B604B628; Wed, 13 Mar 2024 05:03:14 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu DCD4B604B628 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1710268394; bh=ix4GX1hCkqh+ElRhB4JLjqPgL0UoH3CY+rYGXt3vGwY=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=MYX92LAYJtBtbV2WjOXpyZeNC8EGKHNw8P/8bZrmoXUEgEJ9t7koJw/qpCuLRnD6Z 3PfJXEkvxqzmAjLaj0dXzClL73idQ9wa8wPbQYdgnL+CJpqEM49N3ppe7rdI4pd2ni sP1nv7IJpxuyIJk4RoK7SCBjvj8JfKxMmYpzIm6RtEHoKgPSQhwBl8UJMetZvexplo UdEi4AJWJaN4LzoeUTckIcK3Xyr8S88uyCxkz8piBuF5pMoDCs4LB7XU49bc3TJqnt wPkGkoa0praP+mchloK4yNC1GpG1axEUT2Y+3uuNkQlOI0GfUOeBJCjtTYEKl7APc2 biXQt/SfuJsCg== Date: Wed, 13 Mar 2024 05:03:14 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , linux-sound@vger.kernel.org Subject: [PATCH 01/14] ALSA: scarlett2: Move initialisation code lower in the source Message-ID: <0922071cb8be99a2394705de27b917d1e4e46f3f.1710264833.git.g@b4.vu> References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: So that more forward declarations won't be required when we add handling of the ACK notification, move the initialisation functions to after the notification functions. Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 815 ++++++++++++++++++------------------ 1 file changed, 409 insertions(+), 406 deletions(-) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index bd114be537d7..8390b646c0ae 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -6383,412 +6383,7 @@ static int scarlett2_add_power_status_ctl(struct usb_mixer_interface *mixer) &private->power_status_ctl); } -/*** Cleanup/Suspend Callbacks ***/ - -static void scarlett2_private_free(struct usb_mixer_interface *mixer) -{ - struct scarlett2_data *private = mixer->private_data; - - cancel_delayed_work_sync(&private->work); - kfree(private); - mixer->private_data = NULL; -} - -static void scarlett2_private_suspend(struct usb_mixer_interface *mixer) -{ - struct scarlett2_data *private = mixer->private_data; - - if (cancel_delayed_work_sync(&private->work)) - scarlett2_config_save(private->mixer); -} - -/*** Initialisation ***/ - -static void scarlett2_count_io(struct scarlett2_data *private) -{ - const struct scarlett2_device_info *info = private->info; - const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; - int port_type, srcs = 0, dsts = 0; - - /* Count the number of mux sources and destinations */ - for (port_type = 0; - port_type < SCARLETT2_PORT_TYPE_COUNT; - port_type++) { - srcs += port_count[port_type][SCARLETT2_PORT_IN]; - dsts += port_count[port_type][SCARLETT2_PORT_OUT]; - } - - private->num_mux_srcs = srcs; - private->num_mux_dsts = dsts; - - /* Mixer inputs are mux outputs and vice versa. - * Scarlett Gen 4 DSP I/O uses SCARLETT2_PORT_TYPE_MIX but - * doesn't have mixer controls. - */ - private->num_mix_in = - port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_OUT] - - info->dsp_count; - - private->num_mix_out = - port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN] - - info->dsp_count; - - /* Number of analogue line outputs */ - private->num_line_out = - port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT]; - - /* Number of monitor mix controls */ - private->num_monitor_mix_ctls = - info->direct_monitor * 2 * private->num_mix_in; -} - -/* Look through the interface descriptors for the Focusrite Control - * interface (bInterfaceClass = 255 Vendor Specific Class) and set - * bInterfaceNumber, bEndpointAddress, wMaxPacketSize, and bInterval - * in private - */ -static int scarlett2_find_fc_interface(struct usb_device *dev, - struct scarlett2_data *private) -{ - struct usb_host_config *config = dev->actconfig; - int i; - - for (i = 0; i < config->desc.bNumInterfaces; i++) { - struct usb_interface *intf = config->interface[i]; - struct usb_interface_descriptor *desc = - &intf->altsetting[0].desc; - struct usb_endpoint_descriptor *epd; - - if (desc->bInterfaceClass != 255) - continue; - - epd = get_endpoint(intf->altsetting, 0); - private->bInterfaceNumber = desc->bInterfaceNumber; - private->bEndpointAddress = epd->bEndpointAddress & - USB_ENDPOINT_NUMBER_MASK; - private->wMaxPacketSize = le16_to_cpu(epd->wMaxPacketSize); - private->bInterval = epd->bInterval; - return 0; - } - - return -EINVAL; -} - -/* Initialise private data */ -static int scarlett2_init_private(struct usb_mixer_interface *mixer, - const struct scarlett2_device_entry *entry) -{ - struct scarlett2_data *private = - kzalloc(sizeof(struct scarlett2_data), GFP_KERNEL); - - if (!private) - return -ENOMEM; - - mutex_init(&private->usb_mutex); - mutex_init(&private->data_mutex); - INIT_DELAYED_WORK(&private->work, scarlett2_config_save_work); - - mixer->private_data = private; - mixer->private_free = scarlett2_private_free; - mixer->private_suspend = scarlett2_private_suspend; - - private->info = entry->info; - private->config_set = entry->info->config_set; - private->series_name = entry->series_name; - scarlett2_count_io(private); - private->scarlett2_seq = 0; - private->mixer = mixer; - - return scarlett2_find_fc_interface(mixer->chip->dev, private); -} - -/* Cargo cult proprietary initialisation sequence */ -static int scarlett2_usb_init(struct usb_mixer_interface *mixer) -{ - struct usb_device *dev = mixer->chip->dev; - struct scarlett2_data *private = mixer->private_data; - u8 step0_buf[24]; - u8 step2_buf[84]; - int err; - - if (usb_pipe_type_check(dev, usb_sndctrlpipe(dev, 0))) - return -EINVAL; - - /* step 0 */ - err = scarlett2_usb_rx(dev, private->bInterfaceNumber, - SCARLETT2_USB_CMD_INIT, - step0_buf, sizeof(step0_buf)); - if (err < 0) - return err; - - /* step 1 */ - private->scarlett2_seq = 1; - err = scarlett2_usb(mixer, SCARLETT2_USB_INIT_1, NULL, 0, NULL, 0); - if (err < 0) - return err; - - /* step 2 */ - private->scarlett2_seq = 1; - err = scarlett2_usb(mixer, SCARLETT2_USB_INIT_2, - NULL, 0, - step2_buf, sizeof(step2_buf)); - if (err < 0) - return err; - - /* extract 4-byte firmware version from step2_buf[8] */ - private->firmware_version = le32_to_cpu(*(__le32 *)(step2_buf + 8)); - usb_audio_info(mixer->chip, - "Firmware version %d\n", - private->firmware_version); - - return 0; -} - -/* Get the flash segment numbers for the App_Settings and App_Upgrade - * segments and put them in the private data - */ -static int scarlett2_get_flash_segment_nums(struct usb_mixer_interface *mixer) -{ - struct scarlett2_data *private = mixer->private_data; - int err, count, i; - - struct { - __le32 size; - __le32 count; - u8 unknown[8]; - } __packed flash_info; - - struct { - __le32 size; - __le32 flags; - char name[16]; - } __packed segment_info; - - err = scarlett2_usb(mixer, SCARLETT2_USB_INFO_FLASH, - NULL, 0, - &flash_info, sizeof(flash_info)); - if (err < 0) - return err; - - count = le32_to_cpu(flash_info.count); - - /* sanity check count */ - if (count < SCARLETT2_SEGMENT_NUM_MIN || - count > SCARLETT2_SEGMENT_NUM_MAX + 1) { - usb_audio_err(mixer->chip, - "invalid flash segment count: %d\n", count); - return -EINVAL; - } - - for (i = 0; i < count; i++) { - __le32 segment_num_req = cpu_to_le32(i); - int flash_segment_id; - - err = scarlett2_usb(mixer, SCARLETT2_USB_INFO_SEGMENT, - &segment_num_req, sizeof(segment_num_req), - &segment_info, sizeof(segment_info)); - if (err < 0) { - usb_audio_err(mixer->chip, - "failed to get flash segment info %d: %d\n", - i, err); - return err; - } - - if (!strncmp(segment_info.name, - SCARLETT2_SEGMENT_SETTINGS_NAME, 16)) - flash_segment_id = SCARLETT2_SEGMENT_ID_SETTINGS; - else if (!strncmp(segment_info.name, - SCARLETT2_SEGMENT_FIRMWARE_NAME, 16)) - flash_segment_id = SCARLETT2_SEGMENT_ID_FIRMWARE; - else - continue; - - private->flash_segment_nums[flash_segment_id] = i; - private->flash_segment_blocks[flash_segment_id] = - le32_to_cpu(segment_info.size) / - SCARLETT2_FLASH_BLOCK_SIZE; - } - - /* segment 0 is App_Gold and we never want to touch that, so - * use 0 as the "not-found" value - */ - if (!private->flash_segment_nums[SCARLETT2_SEGMENT_ID_SETTINGS]) { - usb_audio_err(mixer->chip, - "failed to find flash segment %s\n", - SCARLETT2_SEGMENT_SETTINGS_NAME); - return -EINVAL; - } - if (!private->flash_segment_nums[SCARLETT2_SEGMENT_ID_FIRMWARE]) { - usb_audio_err(mixer->chip, - "failed to find flash segment %s\n", - SCARLETT2_SEGMENT_FIRMWARE_NAME); - return -EINVAL; - } - - return 0; -} - -/* Read configuration from the interface on start */ -static int scarlett2_read_configs(struct usb_mixer_interface *mixer) -{ - struct scarlett2_data *private = mixer->private_data; - const struct scarlett2_device_info *info = private->info; - int err, i; - - if (scarlett2_has_config_item(private, SCARLETT2_CONFIG_MSD_SWITCH)) { - err = scarlett2_usb_get_config( - mixer, SCARLETT2_CONFIG_MSD_SWITCH, - 1, &private->msd_switch); - if (err < 0) - return err; - } - - if (private->firmware_version < info->min_firmware_version) { - usb_audio_err(mixer->chip, - "Focusrite %s firmware version %d is too old; " - "need %d", - private->series_name, - private->firmware_version, - info->min_firmware_version); - return 0; - } - - /* no other controls are created if MSD mode is on */ - if (private->msd_switch) - return 0; - - err = scarlett2_update_input_level(mixer); - if (err < 0) - return err; - - err = scarlett2_update_input_pad(mixer); - if (err < 0) - return err; - - err = scarlett2_update_input_air(mixer); - if (err < 0) - return err; - - err = scarlett2_update_input_phantom(mixer); - if (err < 0) - return err; - - err = scarlett2_update_direct_monitor(mixer); - if (err < 0) - return err; - - /* the rest of the configuration is for devices with a mixer */ - if (!scarlett2_has_mixer(private)) - return 0; - - err = scarlett2_update_monitor_mix(mixer); - if (err < 0) - return err; - - err = scarlett2_update_monitor_other(mixer); - if (err < 0) - return err; - - if (scarlett2_has_config_item(private, - SCARLETT2_CONFIG_STANDALONE_SWITCH)) { - err = scarlett2_usb_get_config( - mixer, SCARLETT2_CONFIG_STANDALONE_SWITCH, - 1, &private->standalone_switch); - if (err < 0) - return err; - } - - if (scarlett2_has_config_item(private, - SCARLETT2_CONFIG_POWER_EXT)) { - err = scarlett2_update_power_status(mixer); - if (err < 0) - return err; - } - - err = scarlett2_update_sync(mixer); - if (err < 0) - return err; - - if (scarlett2_has_config_item(private, - SCARLETT2_CONFIG_LINE_OUT_VOLUME)) { - s16 sw_vol[SCARLETT2_ANALOGUE_MAX]; - - /* read SW line out volume */ - err = scarlett2_usb_get_config( - mixer, SCARLETT2_CONFIG_LINE_OUT_VOLUME, - private->num_line_out, &sw_vol); - if (err < 0) - return err; - - for (i = 0; i < private->num_line_out; i++) - private->vol[i] = clamp( - sw_vol[i] + SCARLETT2_VOLUME_BIAS, - 0, SCARLETT2_VOLUME_BIAS); - - /* read SW mute */ - err = scarlett2_usb_get_config( - mixer, SCARLETT2_CONFIG_MUTE_SWITCH, - private->num_line_out, &private->mute_switch); - if (err < 0) - return err; - - for (i = 0; i < private->num_line_out; i++) - private->mute_switch[i] = - !!private->mute_switch[i]; - - /* read SW/HW switches */ - if (scarlett2_has_config_item(private, - SCARLETT2_CONFIG_SW_HW_SWITCH)) { - err = scarlett2_usb_get_config( - mixer, SCARLETT2_CONFIG_SW_HW_SWITCH, - private->num_line_out, - &private->vol_sw_hw_switch); - if (err < 0) - return err; - - for (i = 0; i < private->num_line_out; i++) - private->vol_sw_hw_switch[i] = - !!private->vol_sw_hw_switch[i]; - } - } - - err = scarlett2_update_volumes(mixer); - if (err < 0) - return err; - - err = scarlett2_update_dim_mute(mixer); - if (err < 0) - return err; - - err = scarlett2_update_input_select(mixer); - if (err < 0) - return err; - - err = scarlett2_update_input_gain(mixer); - if (err < 0) - return err; - - err = scarlett2_update_autogain(mixer); - if (err < 0) - return err; - - err = scarlett2_update_input_safe(mixer); - if (err < 0) - return err; - - if (scarlett2_has_config_item(private, - SCARLETT2_CONFIG_PCM_INPUT_SWITCH)) { - err = scarlett2_update_pcm_input_switch(mixer); - if (err < 0) - return err; - } - - err = scarlett2_update_mix(mixer); - if (err < 0) - return err; - - return scarlett2_usb_get_mux(mixer); -} +/*** Notification Handlers ***/ /* Notify on sync change */ static void scarlett2_notify_sync(struct usb_mixer_interface *mixer) @@ -7146,6 +6741,126 @@ static void scarlett2_notify(struct urb *urb) } } +/*** Cleanup/Suspend Callbacks ***/ + +static void scarlett2_private_free(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + + cancel_delayed_work_sync(&private->work); + kfree(private); + mixer->private_data = NULL; +} + +static void scarlett2_private_suspend(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + + if (cancel_delayed_work_sync(&private->work)) + scarlett2_config_save(private->mixer); +} + +/*** Initialisation ***/ + +static void scarlett2_count_io(struct scarlett2_data *private) +{ + const struct scarlett2_device_info *info = private->info; + const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; + int port_type, srcs = 0, dsts = 0; + + /* Count the number of mux sources and destinations */ + for (port_type = 0; + port_type < SCARLETT2_PORT_TYPE_COUNT; + port_type++) { + srcs += port_count[port_type][SCARLETT2_PORT_IN]; + dsts += port_count[port_type][SCARLETT2_PORT_OUT]; + } + + private->num_mux_srcs = srcs; + private->num_mux_dsts = dsts; + + /* Mixer inputs are mux outputs and vice versa. + * Scarlett Gen 4 DSP I/O uses SCARLETT2_PORT_TYPE_MIX but + * doesn't have mixer controls. + */ + private->num_mix_in = + port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_OUT] - + info->dsp_count; + + private->num_mix_out = + port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN] - + info->dsp_count; + + /* Number of analogue line outputs */ + private->num_line_out = + port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT]; + + /* Number of monitor mix controls */ + private->num_monitor_mix_ctls = + info->direct_monitor * 2 * private->num_mix_in; +} + +/* Look through the interface descriptors for the Focusrite Control + * interface (bInterfaceClass = 255 Vendor Specific Class) and set + * bInterfaceNumber, bEndpointAddress, wMaxPacketSize, and bInterval + * in private + */ +static int scarlett2_find_fc_interface(struct usb_device *dev, + struct scarlett2_data *private) +{ + struct usb_host_config *config = dev->actconfig; + int i; + + for (i = 0; i < config->desc.bNumInterfaces; i++) { + struct usb_interface *intf = config->interface[i]; + struct usb_interface_descriptor *desc = + &intf->altsetting[0].desc; + struct usb_endpoint_descriptor *epd; + + if (desc->bInterfaceClass != 255) + continue; + + epd = get_endpoint(intf->altsetting, 0); + private->bInterfaceNumber = desc->bInterfaceNumber; + private->bEndpointAddress = epd->bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + private->wMaxPacketSize = le16_to_cpu(epd->wMaxPacketSize); + private->bInterval = epd->bInterval; + return 0; + } + + return -EINVAL; +} + +/* Initialise private data */ +static int scarlett2_init_private(struct usb_mixer_interface *mixer, + const struct scarlett2_device_entry *entry) +{ + struct scarlett2_data *private = + kzalloc(sizeof(struct scarlett2_data), GFP_KERNEL); + + if (!private) + return -ENOMEM; + + mutex_init(&private->usb_mutex); + mutex_init(&private->data_mutex); + INIT_DELAYED_WORK(&private->work, scarlett2_config_save_work); + + mixer->private_data = private; + mixer->private_free = scarlett2_private_free; + mixer->private_suspend = scarlett2_private_suspend; + + private->info = entry->info; + private->config_set = entry->info->config_set; + private->series_name = entry->series_name; + scarlett2_count_io(private); + private->scarlett2_seq = 0; + private->mixer = mixer; + + return scarlett2_find_fc_interface(mixer->chip->dev, private); +} + +/* Submit a URB to receive notifications from the device */ static int scarlett2_init_notify(struct usb_mixer_interface *mixer) { struct usb_device *dev = mixer->chip->dev; @@ -7177,6 +6892,294 @@ static int scarlett2_init_notify(struct usb_mixer_interface *mixer) return usb_submit_urb(mixer->urb, GFP_KERNEL); } +/* Cargo cult proprietary initialisation sequence */ +static int scarlett2_usb_init(struct usb_mixer_interface *mixer) +{ + struct usb_device *dev = mixer->chip->dev; + struct scarlett2_data *private = mixer->private_data; + u8 step0_buf[24]; + u8 step2_buf[84]; + int err; + + if (usb_pipe_type_check(dev, usb_sndctrlpipe(dev, 0))) + return -EINVAL; + + /* step 0 */ + err = scarlett2_usb_rx(dev, private->bInterfaceNumber, + SCARLETT2_USB_CMD_INIT, + step0_buf, sizeof(step0_buf)); + if (err < 0) + return err; + + /* step 1 */ + private->scarlett2_seq = 1; + err = scarlett2_usb(mixer, SCARLETT2_USB_INIT_1, NULL, 0, NULL, 0); + if (err < 0) + return err; + + /* step 2 */ + private->scarlett2_seq = 1; + err = scarlett2_usb(mixer, SCARLETT2_USB_INIT_2, + NULL, 0, + step2_buf, sizeof(step2_buf)); + if (err < 0) + return err; + + /* extract 4-byte firmware version from step2_buf[8] */ + private->firmware_version = le32_to_cpu(*(__le32 *)(step2_buf + 8)); + usb_audio_info(mixer->chip, + "Firmware version %d\n", + private->firmware_version); + + return 0; +} + +/* Get the flash segment numbers for the App_Settings and App_Upgrade + * segments and put them in the private data + */ +static int scarlett2_get_flash_segment_nums(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + int err, count, i; + + struct { + __le32 size; + __le32 count; + u8 unknown[8]; + } __packed flash_info; + + struct { + __le32 size; + __le32 flags; + char name[16]; + } __packed segment_info; + + err = scarlett2_usb(mixer, SCARLETT2_USB_INFO_FLASH, + NULL, 0, + &flash_info, sizeof(flash_info)); + if (err < 0) + return err; + + count = le32_to_cpu(flash_info.count); + + /* sanity check count */ + if (count < SCARLETT2_SEGMENT_NUM_MIN || + count > SCARLETT2_SEGMENT_NUM_MAX + 1) { + usb_audio_err(mixer->chip, + "invalid flash segment count: %d\n", count); + return -EINVAL; + } + + for (i = 0; i < count; i++) { + __le32 segment_num_req = cpu_to_le32(i); + int flash_segment_id; + + err = scarlett2_usb(mixer, SCARLETT2_USB_INFO_SEGMENT, + &segment_num_req, sizeof(segment_num_req), + &segment_info, sizeof(segment_info)); + if (err < 0) { + usb_audio_err(mixer->chip, + "failed to get flash segment info %d: %d\n", + i, err); + return err; + } + + if (!strncmp(segment_info.name, + SCARLETT2_SEGMENT_SETTINGS_NAME, 16)) + flash_segment_id = SCARLETT2_SEGMENT_ID_SETTINGS; + else if (!strncmp(segment_info.name, + SCARLETT2_SEGMENT_FIRMWARE_NAME, 16)) + flash_segment_id = SCARLETT2_SEGMENT_ID_FIRMWARE; + else + continue; + + private->flash_segment_nums[flash_segment_id] = i; + private->flash_segment_blocks[flash_segment_id] = + le32_to_cpu(segment_info.size) / + SCARLETT2_FLASH_BLOCK_SIZE; + } + + /* segment 0 is App_Gold and we never want to touch that, so + * use 0 as the "not-found" value + */ + if (!private->flash_segment_nums[SCARLETT2_SEGMENT_ID_SETTINGS]) { + usb_audio_err(mixer->chip, + "failed to find flash segment %s\n", + SCARLETT2_SEGMENT_SETTINGS_NAME); + return -EINVAL; + } + if (!private->flash_segment_nums[SCARLETT2_SEGMENT_ID_FIRMWARE]) { + usb_audio_err(mixer->chip, + "failed to find flash segment %s\n", + SCARLETT2_SEGMENT_FIRMWARE_NAME); + return -EINVAL; + } + + return 0; +} + +/* Read configuration from the interface on start */ +static int scarlett2_read_configs(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + int err, i; + + if (scarlett2_has_config_item(private, SCARLETT2_CONFIG_MSD_SWITCH)) { + err = scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_MSD_SWITCH, + 1, &private->msd_switch); + if (err < 0) + return err; + } + + if (private->firmware_version < info->min_firmware_version) { + usb_audio_err(mixer->chip, + "Focusrite %s firmware version %d is too old; " + "need %d", + private->series_name, + private->firmware_version, + info->min_firmware_version); + return 0; + } + + /* no other controls are created if MSD mode is on */ + if (private->msd_switch) + return 0; + + err = scarlett2_update_input_level(mixer); + if (err < 0) + return err; + + err = scarlett2_update_input_pad(mixer); + if (err < 0) + return err; + + err = scarlett2_update_input_air(mixer); + if (err < 0) + return err; + + err = scarlett2_update_input_phantom(mixer); + if (err < 0) + return err; + + err = scarlett2_update_direct_monitor(mixer); + if (err < 0) + return err; + + /* the rest of the configuration is for devices with a mixer */ + if (!scarlett2_has_mixer(private)) + return 0; + + err = scarlett2_update_monitor_mix(mixer); + if (err < 0) + return err; + + err = scarlett2_update_monitor_other(mixer); + if (err < 0) + return err; + + if (scarlett2_has_config_item(private, + SCARLETT2_CONFIG_STANDALONE_SWITCH)) { + err = scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_STANDALONE_SWITCH, + 1, &private->standalone_switch); + if (err < 0) + return err; + } + + if (scarlett2_has_config_item(private, + SCARLETT2_CONFIG_POWER_EXT)) { + err = scarlett2_update_power_status(mixer); + if (err < 0) + return err; + } + + err = scarlett2_update_sync(mixer); + if (err < 0) + return err; + + if (scarlett2_has_config_item(private, + SCARLETT2_CONFIG_LINE_OUT_VOLUME)) { + s16 sw_vol[SCARLETT2_ANALOGUE_MAX]; + + /* read SW line out volume */ + err = scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_LINE_OUT_VOLUME, + private->num_line_out, &sw_vol); + if (err < 0) + return err; + + for (i = 0; i < private->num_line_out; i++) + private->vol[i] = clamp( + sw_vol[i] + SCARLETT2_VOLUME_BIAS, + 0, SCARLETT2_VOLUME_BIAS); + + /* read SW mute */ + err = scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_MUTE_SWITCH, + private->num_line_out, &private->mute_switch); + if (err < 0) + return err; + + for (i = 0; i < private->num_line_out; i++) + private->mute_switch[i] = + !!private->mute_switch[i]; + + /* read SW/HW switches */ + if (scarlett2_has_config_item(private, + SCARLETT2_CONFIG_SW_HW_SWITCH)) { + err = scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_SW_HW_SWITCH, + private->num_line_out, + &private->vol_sw_hw_switch); + if (err < 0) + return err; + + for (i = 0; i < private->num_line_out; i++) + private->vol_sw_hw_switch[i] = + !!private->vol_sw_hw_switch[i]; + } + } + + err = scarlett2_update_volumes(mixer); + if (err < 0) + return err; + + err = scarlett2_update_dim_mute(mixer); + if (err < 0) + return err; + + err = scarlett2_update_input_select(mixer); + if (err < 0) + return err; + + err = scarlett2_update_input_gain(mixer); + if (err < 0) + return err; + + err = scarlett2_update_autogain(mixer); + if (err < 0) + return err; + + err = scarlett2_update_input_safe(mixer); + if (err < 0) + return err; + + if (scarlett2_has_config_item(private, + SCARLETT2_CONFIG_PCM_INPUT_SWITCH)) { + err = scarlett2_update_pcm_input_switch(mixer); + if (err < 0) + return err; + } + + err = scarlett2_update_mix(mixer); + if (err < 0) + return err; + + return scarlett2_usb_get_mux(mixer); +} + static const struct scarlett2_device_entry *get_scarlett2_device_entry( struct usb_mixer_interface *mixer) { From patchwork Tue Mar 12 18:34:14 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: 13590467 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 0867E13D2EE for ; Tue, 12 Mar 2024 18:34:15 +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=1710268458; cv=none; b=oQ0w/HE5taAqDpVWvXqz3CkMT6OdQ4Z+dAzRSSMazughmLVD/JEgn81Cv69EkMSS02D2yAz7oeA0ew+1NWIEAQ/dGhDCt+aUTowRMAPvDKqcDnkiY+11UvCIMgnYaI2TZ4C1/V9vMxf846onCNB8Kj0oOpEf+1T35b5iXKspIO0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1710268458; c=relaxed/simple; bh=Sw5wfBrGNDzxFVaOiKtxcunHWOhb7J838+BA+Rpqylo=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=m/s+yhnsDlnopOBbmZ+tsJKo78K4kv83H7Y3vHqrmlCoRDyb/cMyXzH1vncURi2FlajTnEXXDciOK78oLh2+3cOydDXOscjtRNijuqKyOEwGor6V6fl69XOUTCzNEBQ/scse56StVwnKmqfJEXzY6ZicSAAPdzpYn+xiro9yWyc= 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=SKXVDevC; 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="SKXVDevC" Received: by m.b4.vu (Postfix, from userid 1000) id 09C69604B628; Wed, 13 Mar 2024 05:04:14 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu 09C69604B628 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1710268454; bh=/oxY8cz0VkKojDihZRMLMue48sz5bVgWcUoRwT5P1gw=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=SKXVDevCYiWy2Uh00r3Fy8su3VmWmRE/AXTWRVdVLkPThqW+4Q5Eo5Qfatqfu/WhO DgYOw73me8BRdoeCCQ9F8w5g6S4WlcZpnayEP2zcR2yBAFOCi0drBcvCpXf7cbzin1 r3rD9dQtnf+GVvgwRyDzCdHLiyBSrhLWcyR/eOsETt1IsAxfjGRQwIYPkXb6jknnQo ZIYAUrvhbL9WL5IJ/BeWfUmnSoYtynsoREjtA6PQn5X2j+g4gKIos5gl2CF+UiEF/+ 9Me94dIDhYrcZadZjvpQTmiI5mqEQgziy39BUyNh/GnEdf6tgRGgnZrsTI1pGKt6dO QB0QSS6Ciy1sQ== Date: Wed, 13 Mar 2024 05:04:14 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , linux-sound@vger.kernel.org Subject: [PATCH 02/14] ALSA: scarlett2: Implement handling of the ACK notification Message-ID: <452d1263c40fa8eba1cfb24e2055e40a84cbc437.1710264833.git.g@b4.vu> References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: After scarlett2_usb() sends a command, it seems that we should wait for an ACK before attempting to read the response. Not doing that didn't seem necessary previously but seems to be causing occasional issues with 4th Gen devices. Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 70 ++++++++++++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 9 deletions(-) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 8390b646c0ae..02c488c80b7e 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -321,6 +321,7 @@ struct scarlett2_notification { void (*func)(struct usb_mixer_interface *mixer); }; +static void scarlett2_notify_ack(struct usb_mixer_interface *mixer); static void scarlett2_notify_sync(struct usb_mixer_interface *mixer); static void scarlett2_notify_dim_mute(struct usb_mixer_interface *mixer); static void scarlett2_notify_monitor(struct usb_mixer_interface *mixer); @@ -343,7 +344,7 @@ static void scarlett2_notify_pcm_input_switch( /* Arrays of notification callback functions */ static const struct scarlett2_notification scarlett2_notifications[] = { - { 0x00000001, NULL }, /* ack, gets ignored */ + { 0x00000001, scarlett2_notify_ack }, { 0x00000008, scarlett2_notify_sync }, { 0x00200000, scarlett2_notify_dim_mute }, { 0x00400000, scarlett2_notify_monitor }, @@ -353,14 +354,14 @@ static const struct scarlett2_notification scarlett2_notifications[] = { }; static const struct scarlett2_notification scarlett3a_notifications[] = { - { 0x00000001, NULL }, /* ack, gets ignored */ + { 0x00000001, scarlett2_notify_ack }, { 0x00800000, scarlett2_notify_input_other }, { 0x01000000, scarlett2_notify_direct_monitor }, { 0, NULL } }; static const struct scarlett2_notification scarlett4_solo_notifications[] = { - { 0x00000001, NULL }, /* ack, gets ignored */ + { 0x00000001, scarlett2_notify_ack }, { 0x00000008, scarlett2_notify_sync }, { 0x00400000, scarlett2_notify_input_air }, { 0x00800000, scarlett2_notify_direct_monitor }, @@ -371,7 +372,7 @@ static const struct scarlett2_notification scarlett4_solo_notifications[] = { }; static const struct scarlett2_notification scarlett4_2i2_notifications[] = { - { 0x00000001, NULL }, /* ack, gets ignored */ + { 0x00000001, scarlett2_notify_ack }, { 0x00000008, scarlett2_notify_sync }, { 0x00200000, scarlett2_notify_input_safe }, { 0x00400000, scarlett2_notify_autogain }, @@ -387,7 +388,7 @@ static const struct scarlett2_notification scarlett4_2i2_notifications[] = { }; static const struct scarlett2_notification scarlett4_4i4_notifications[] = { - { 0x00000001, NULL }, /* ack, gets ignored */ + { 0x00000001, scarlett2_notify_ack }, { 0x00000008, scarlett2_notify_sync }, { 0x00200000, scarlett2_notify_input_safe }, { 0x00400000, scarlett2_notify_autogain }, @@ -942,7 +943,9 @@ struct scarlett2_device_info { struct scarlett2_data { struct usb_mixer_interface *mixer; struct mutex usb_mutex; /* prevent sending concurrent USB requests */ + struct completion cmd_done; struct mutex data_mutex; /* lock access to this data */ + u8 running; u8 hwdep_in_use; u8 selected_flash_segment_id; u8 flash_write_state; @@ -1960,6 +1963,17 @@ static int scarlett2_usb( goto unlock; } + if (!wait_for_completion_timeout(&private->cmd_done, + msecs_to_jiffies(1000))) { + usb_audio_err( + mixer->chip, + "%s USB request timed out, cmd %x\n", + private->series_name, cmd); + + err = -ETIMEDOUT; + goto unlock; + } + /* send a second message to get the response */ err = scarlett2_usb_rx(dev, private->bInterfaceNumber, @@ -6702,6 +6716,18 @@ static void scarlett2_notify_pcm_input_switch(struct usb_mixer_interface *mixer) scarlett2_notify_mux(mixer); } +/* Handle acknowledgement that a command was received; let + * scarlett2_usb() know that it can proceed + */ +static void scarlett2_notify_ack(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + + /* if running == 0, ignore ACKs */ + if (private->running) + complete(&private->cmd_done); +} + /* Interrupt callback */ static void scarlett2_notify(struct urb *urb) { @@ -6718,6 +6744,12 @@ static void scarlett2_notify(struct urb *urb) data = le32_to_cpu(*(__le32 *)urb->transfer_buffer); + /* Ignore notifications except ACK during initialisation. + * ACK is 0x00000001 on every device. + */ + if (private->running < 2) + data &= 1; + while (data && notifications->mask) { if (data & notifications->mask) { data &= ~notifications->mask; @@ -6738,6 +6770,8 @@ static void scarlett2_notify(struct urb *urb) ustatus != -ESHUTDOWN) { urb->dev = mixer->chip->dev; usb_submit_urb(urb, GFP_ATOMIC); + } else { + complete(&private->cmd_done); } } @@ -6889,6 +6923,8 @@ static int scarlett2_init_notify(struct usb_mixer_interface *mixer) transfer_buffer, private->wMaxPacketSize, scarlett2_notify, mixer, private->bInterval); + init_completion(&private->cmd_done); + return usb_submit_urb(mixer->urb, GFP_KERNEL); } @@ -6911,6 +6947,24 @@ static int scarlett2_usb_init(struct usb_mixer_interface *mixer) if (err < 0) return err; + /* Set up the interrupt polling for notifications. + * When running is: + * 0: all notifications are ignored + * 1: only ACKs are handled + * 2: all notifications are handled + */ + err = scarlett2_init_notify(mixer); + if (err < 0) + return err; + + /* sleep for a moment in case of an outstanding ACK */ + msleep(20); + + /* start handling ACKs, but no other notifications until the + * ALSA controls have been created + */ + private->running = 1; + /* step 1 */ private->scarlett2_seq = 1; err = scarlett2_usb(mixer, SCARLETT2_USB_INIT_1, NULL, 0, NULL, 0); @@ -7308,10 +7362,8 @@ static int snd_scarlett2_controls_create( scarlett2_phantom_update_access(mixer); } - /* Set up the interrupt polling */ - err = scarlett2_init_notify(mixer); - if (err < 0) - return err; + /* Start handling all notifications */ + private->running = 2; return 0; } From patchwork Tue Mar 12 18:34:42 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: 13590468 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 6C5C81DDC9 for ; Tue, 12 Mar 2024 18:34:44 +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=1710268486; cv=none; b=Ct/QOnSqFJSybNnnZkSO3NT0qMAi85lKg57sbnnCRstREaf+8xKCRg1tucakTn7l1VyaqPyHa7s/1HpFNetHNs2uLpP/1jpVMDPmbSJMMy3fvR5W3oyv6Dgx4vhik/SM790LtYsXe05DwpRFGAaTcpPJlsWJxgD7SDV2yNXcWl0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1710268486; c=relaxed/simple; bh=CCvyYoEU8wZyGiZAtNhCxEfZZvQ2LuSQpSdCLGy3Z8A=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=GceOQGFYHZxee9KSPDbR8kxpPb3XzCE/ggUlu0T20jk2OIki3HkslTr1WKGwmiMiTdMEJCJbRuJxKiuUVFONTx8McPv1rscFlsEUmXxai/zzcGR0xj//8xafr1YhiVG7hF/r7yC4TnFw81OeQM4Pt7H+Vg05xYUu1KeJFDJFvug= 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=iW0zg+lF; 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="iW0zg+lF" Received: by m.b4.vu (Postfix, from userid 1000) id 71714604B628; Wed, 13 Mar 2024 05:04:42 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu 71714604B628 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1710268482; bh=KnVh0m8I1ekoRCiw14HYKydIS3Shi+7aQ7HzZ+BBaFo=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=iW0zg+lF4U2M1pSS0EBQqf02YR/DBAFywFZVirTtOxnlOHxG5H7a2+z0OK6onli3g V4Ejdwv+wX7KU791NUXri0Nj1PP2qHG+aBLMnGfBf3bnFg7PM1IcCejm2FTw8TcWsh WkQVpzn1wTn1ksdIKDaZVzZCratxuv+6/q/H6TMxPD0G8SXw8ocm0LjdOGTIg7Q2ew 8WvQ1JjVeZDD/HW7h4iYdPN+XyhRE9QdojSLrck963vBLM3qdlcdnZAD5BQDp4abWX HQHg7Zhkk9ifZogPhu+X7h4g/vYllO8tiX+WAUwdotn/TiRrMh/kSImroFHoSKaPuV 6b/WwxjY66z2Q== Date: Wed, 13 Mar 2024 05:04:42 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , linux-sound@vger.kernel.org Subject: [PATCH 03/14] ALSA: scarlett2: Add support for reading from flash Message-ID: <800d20a801e8c59c2905c82ecae5676cd4f31429.1710264833.git.g@b4.vu> References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Add hwdep read op so flash segments can be read. Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 88 +++++++++++++++++++++++++++++++++++-- 1 file changed, 84 insertions(+), 4 deletions(-) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 02c488c80b7e..981ec48a811a 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -1859,6 +1859,7 @@ static int scarlett2_get_port_start_num( #define SCARLETT2_USB_ERASE_SEGMENT 0x00004002 #define SCARLETT2_USB_GET_ERASE 0x00004003 #define SCARLETT2_USB_WRITE_SEGMENT 0x00004004 +#define SCARLETT2_USB_READ_SEGMENT 0x00004005 #define SCARLETT2_USB_GET_SYNC 0x00006004 #define SCARLETT2_USB_GET_DATA 0x00800000 #define SCARLETT2_USB_SET_DATA 0x00800001 @@ -1869,7 +1870,7 @@ static int scarlett2_get_port_start_num( #define SCARLETT2_USB_METER_LEVELS_GET_MAGIC 1 #define SCARLETT2_FLASH_BLOCK_SIZE 4096 -#define SCARLETT2_FLASH_WRITE_MAX 1024 +#define SCARLETT2_FLASH_RW_MAX 1024 #define SCARLETT2_SEGMENT_NUM_MIN 1 #define SCARLETT2_SEGMENT_NUM_MAX 4 @@ -7452,7 +7453,7 @@ static int scarlett2_reboot(struct usb_mixer_interface *mixer) return scarlett2_usb(mixer, SCARLETT2_USB_REBOOT, NULL, 0, NULL, 0); } -/* Select a flash segment for erasing (and possibly writing to) */ +/* Select a flash segment for reading/erasing/writing */ static int scarlett2_ioctl_select_flash_segment( struct usb_mixer_interface *mixer, unsigned long arg) @@ -7633,6 +7634,84 @@ static int scarlett2_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, } } +static long scarlett2_hwdep_read(struct snd_hwdep *hw, + char __user *buf, + long count, loff_t *offset) +{ + struct usb_mixer_interface *mixer = hw->private_data; + struct scarlett2_data *private = mixer->private_data; + int segment_id, segment_num, err; + int flash_size; + + /* SCARLETT2_USB_READ_SEGMENT request data */ + struct { + __le32 segment_num; + __le32 offset; + __le32 len; + } __packed req; + + u8 *resp; + + /* Flash segment must first be selected */ + if (private->flash_write_state != SCARLETT2_FLASH_WRITE_STATE_SELECTED) + return -EINVAL; + + /* Get the selected flash segment number */ + segment_id = private->selected_flash_segment_id; + if (segment_id < 0 || segment_id >= SCARLETT2_SEGMENT_ID_COUNT) + return -EINVAL; + + segment_num = private->flash_segment_nums[segment_id]; + if (segment_num < 0 || + segment_num > SCARLETT2_SEGMENT_NUM_MAX) + return -EFAULT; + + /* Validate the offset and count */ + if (count < 0 || *offset < 0) + return -EINVAL; + + /* Reached EOF? */ + flash_size = private->flash_segment_blocks[segment_id] * + SCARLETT2_FLASH_BLOCK_SIZE; + if (!count || *offset >= flash_size) + return 0; + + /* Limit the numbers of bytes read to SCARLETT2_FLASH_RW_MAX */ + if (count > SCARLETT2_FLASH_RW_MAX) + count = SCARLETT2_FLASH_RW_MAX; + + /* Limit read to EOF */ + if (*offset + count >= flash_size) + count = flash_size - *offset; + + /* Create and send the request */ + req.segment_num = cpu_to_le32(segment_num); + req.offset = cpu_to_le32(*offset); + req.len = cpu_to_le32(count); + + resp = kzalloc(count, GFP_KERNEL); + if (!resp) + return -ENOMEM; + + err = scarlett2_usb(mixer, SCARLETT2_USB_READ_SEGMENT, + &req, sizeof(req), resp, count); + if (err < 0) + goto error; + + /* Copy the response to userspace */ + if (copy_to_user(buf, resp, count)) { + err = -EFAULT; + goto error; + } + + *offset += count; + err = count; + +error: + kfree(resp); + return err; +} + static long scarlett2_hwdep_write(struct snd_hwdep *hw, const char __user *buf, long count, loff_t *offset) @@ -7651,7 +7730,7 @@ static long scarlett2_hwdep_write(struct snd_hwdep *hw, } __packed *req; /* Calculate the maximum permitted in data[] */ - const size_t max_data_size = SCARLETT2_FLASH_WRITE_MAX - + const size_t max_data_size = SCARLETT2_FLASH_RW_MAX - offsetof(typeof(*req), data); /* If erasing, wait for it to complete */ @@ -7688,7 +7767,7 @@ static long scarlett2_hwdep_write(struct snd_hwdep *hw, if (!count) return 0; - /* Limit the *req size to SCARLETT2_FLASH_WRITE_MAX */ + /* Limit the *req size to SCARLETT2_FLASH_RW_MAX */ if (count > max_data_size) count = max_data_size; @@ -7749,6 +7828,7 @@ static int scarlett2_hwdep_init(struct usb_mixer_interface *mixer) hw->exclusive = 1; hw->ops.open = scarlett2_hwdep_open; hw->ops.ioctl = scarlett2_hwdep_ioctl; + hw->ops.read = scarlett2_hwdep_read; hw->ops.write = scarlett2_hwdep_write; hw->ops.release = scarlett2_hwdep_release; From patchwork Tue Mar 12 18:34:59 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: 13590469 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 0537D13D2EE for ; Tue, 12 Mar 2024 18:35:00 +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=1710268503; cv=none; b=H7zc125QPyq1yH3bn2/CyKP1QXnhdQB7EwrNCzlIL6qMDC1sxnIqv+66AWJrwFoxPPUEWzZuPiocLfC5lOw5QrZLPimcO4zZxToP1/GLCmonaMCf/+6c4VVepEi2nqlOwrOgjno+7y2B27MNfeGLO/dK9lih8zhNZWlb14wuA3Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1710268503; c=relaxed/simple; bh=cv3TPtoV3bfZ9nPkpVe+DduRxZQvdpZAPvw+7ylJoXk=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=oHfqws1fDCjwt2gncm7Q2MQ2V1rAf1G8jQSSgQTcZ3ke3PW/PJOLiKymlehBoIAnxlw7+4aQI4y1VIr7HL2oBFgom4HyNOteb1bA2z4e3p/xHVPNMazQl3yGuaoVHrd56PZ5XTUfLpRAHctr9nCcUrdxrFJU9z8FjQcwN3Phwh8= 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=DSau9G8H; 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="DSau9G8H" Received: by m.b4.vu (Postfix, from userid 1000) id 6C2A3604B628; Wed, 13 Mar 2024 05:04:59 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu 6C2A3604B628 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1710268499; bh=IljRXDStgn3sJTAC25X6vS2KJ5D2gSKRo/lwEU9b69U=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=DSau9G8HejxtRs+IeAxw+nAOs4XccQX8ETivG5Qyt+nEf55JhAlwSQ2b+iLShggRA co1QuGTlOx3EENdVIuHWByv5EzBtfLwpsaWUrpFjCG2CncQJQSrQWyL69cSLRRuvNk 6xTjtwtqUQJ/Jcfk8p6/PiD6bCEt3fvbPNz+jlNeVzbGLZ7aZxVYApecDzR7Ckvh50 WfZ0C4HCUNRsdSf5n/ZrnSbFwSHBfjnkjmf/Escxt5rJxnT2XoDYKxw80vUWT7oDwm HGUUcdwBkc1eA9Esqphq3fRsQl7bqc542tHQnszu2ZYfxGQnbGuGfJcvERRqLsXy4U PqYF2RfjyCQGw== Date: Wed, 13 Mar 2024 05:04:59 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , linux-sound@vger.kernel.org Subject: [PATCH 04/14] ALSA: scarlett2: Rename gen4_write_addr to param_buf_addr Message-ID: References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: The location pointed to by gen4_write_addr and gen4_write_addr + 1 is officially known as the parameter buffer. Update the code to match. Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 981ec48a811a..6031bfd98137 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -438,9 +438,9 @@ enum { /* Location, size, and activation command number for the configuration * parameters. Size is in bits and may be 0, 1, 8, or 16. * - * A size of 0 indicates that the parameter is a byte-sized Scarlett - * Gen 4 configuration which is written through the gen4_write_addr - * location (but still read through the given offset location). + * A size of 0 indicates that the parameter is a byte-sized + * configuration which is set through the parameter buffer (but still + * read through the given offset location). * * Some Gen 4 configuration parameters are written with 0x02 for a * desired value of 0x01, and 0x03 for 0x00. These are indicated with @@ -457,7 +457,7 @@ struct scarlett2_config { struct scarlett2_config_set { const struct scarlett2_notification *notifications; - u16 gen4_write_addr; + u16 param_buf_addr; const struct scarlett2_config items[SCARLETT2_CONFIG_COUNT]; }; @@ -625,7 +625,7 @@ static const struct scarlett2_config_set scarlett2_config_set_gen3c = { /* Solo Gen 4 */ static const struct scarlett2_config_set scarlett2_config_set_gen4_solo = { .notifications = scarlett4_solo_notifications, - .gen4_write_addr = 0xd8, + .param_buf_addr = 0xd8, .items = { [SCARLETT2_CONFIG_MSD_SWITCH] = { .offset = 0x47, .size = 8, .activate = 4 }, @@ -653,7 +653,7 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_solo = { /* 2i2 Gen 4 */ static const struct scarlett2_config_set scarlett2_config_set_gen4_2i2 = { .notifications = scarlett4_2i2_notifications, - .gen4_write_addr = 0xfc, + .param_buf_addr = 0xfc, .items = { [SCARLETT2_CONFIG_MSD_SWITCH] = { .offset = 0x49, .size = 8, .activate = 4 }, // 0x41 ?? @@ -696,7 +696,7 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_2i2 = { /* 4i4 Gen 4 */ static const struct scarlett2_config_set scarlett2_config_set_gen4_4i4 = { .notifications = scarlett4_4i4_notifications, - .gen4_write_addr = 0x130, + .param_buf_addr = 0x130, .items = { [SCARLETT2_CONFIG_MSD_SWITCH] = { .offset = 0x5c, .size = 8, .activate = 4 }, @@ -2080,7 +2080,7 @@ static int scarlett2_usb_get_config( if (!config_item->offset) return -EFAULT; - /* Gen 4 style parameters are always 1 byte */ + /* Writes to the parameter buffer are always 1 byte */ size = config_item->size ? config_item->size : 8; /* For byte-sized parameters, retrieve directly into buf */ @@ -2167,23 +2167,23 @@ static int scarlett2_usb_set_config( if (!config_item->offset) return -EFAULT; - /* Gen 4 style writes are selected with size = 0; + /* Writes via the parameter buffer are selected with size = 0; * these are only byte-sized values written through a shared * location, different to the read address */ if (!config_item->size) { - if (!config_set->gen4_write_addr) + if (!config_set->param_buf_addr) return -EFAULT; - /* Place index in gen4_write_addr + 1 */ + /* Place index in param_buf_addr + 1 */ err = scarlett2_usb_set_data( - mixer, config_set->gen4_write_addr + 1, 1, index); + mixer, config_set->param_buf_addr + 1, 1, index); if (err < 0) return err; - /* Place value in gen4_write_addr */ + /* Place value in param_buf_addr */ err = scarlett2_usb_set_data( - mixer, config_set->gen4_write_addr, 1, value); + mixer, config_set->param_buf_addr, 1, value); if (err < 0) return err; @@ -2192,9 +2192,8 @@ static int scarlett2_usb_set_config( mixer, config_item->activate); } - /* Not-Gen 4 style needs NVRAM save, supports - * bit-modification, and writing is done to the same place - * that the value can be read from + /* Direct writes (not via the parameter buffer) need NVRAM + * save and support bit-modification */ /* Cancel any pending NVRAM save */ @@ -2238,8 +2237,8 @@ static int scarlett2_usb_set_config( if (err < 0) return err; - /* Gen 2 style writes to Gen 4 devices don't need saving */ - if (config_set->gen4_write_addr) + /* Writes via the parameter buffer don't need a separate save step */ + if (config_set->param_buf_addr) return 0; /* Schedule the change to be written to NVRAM */ From patchwork Tue Mar 12 18:35:15 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: 13590470 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 1ADAC13D2EE for ; Tue, 12 Mar 2024 18:35:16 +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=1710268519; cv=none; b=X6QCjbEGemY9rvaDfYRwH+liHxpnIuzL0vY2G3g+FJdoaDhMuXRmBTRj5rZf3CBT2k2sDQQKDJ7YAyg3o36uTkArccP+7BvTWKY5Vf7q01tm14yiIA2CPsaykRvlB/Cb+RChAnqoZ5y65WfX/m2Sda4AaYqgNEpcDSskMF318rA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1710268519; c=relaxed/simple; bh=4hSDiSS+3BrcrHWDKY3NeKBU18V7/UR1b9cFUOUIfFk=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=B2S2WScPx6sqpr7QBj0Fmyt610HJX2bYVUh/0cXsRiODO17kDWFunVtLGH3zS1m3oKIK70BP89qRjZ+qLyxJwv9rCuEEInvdHxyRtNo/T+q208EiyvtAlfrDUBixdjjYNxEJYZdkfp4VAir//Wjj0tVxRJZtfK8S6HD6WtMyK98= 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=sFJxhDdj; 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="sFJxhDdj" Received: by m.b4.vu (Postfix, from userid 1000) id 35782604B628; Wed, 13 Mar 2024 05:05:15 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu 35782604B628 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1710268515; bh=L85CNCqofFJVXSnT2JBhNyQUk+0tlGmVaoQWfotddLc=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=sFJxhDdjijVVNd1TtnzwsAbXSVtiwrq4AMr9kSUGMKnMSW8OAqrK5dSnwGGizQpXC vXmx8kFGpmvCXcb+2FqwzzTi8v9afvmmFbKVmjJkYFi53293VKKa2pcXgjz1i4PLBr uEwmpVFrjK8xi7NMZoY51B37BsOml40Zi4CqRMeBf2u6SPXVwxU/GDezNXAZZmJ/OH dPFcr9kxue4FjLZuOvEGY0pCXenq5+9SDNQ4n1odJM2bqQC3/QitnBvgH9mq2uYORK Ge9nxRRVcTmdSGjrKwjVNHnvFuuMFUZ8WV3p4bnu9lJwn+XPJX60cvDSPZHybf1Oac XW3ss3POVr5/Q== Date: Wed, 13 Mar 2024 05:05:15 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , linux-sound@vger.kernel.org Subject: [PATCH 05/14] ALSA: scarlett2: Add pbuf field to struct scarlett2_config Message-ID: <50a7d85bb04f9a7f13f667c70a706826c8d3ef93.1710264833.git.g@b4.vu> References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: scarlett2_usb_set_config() was using size = 0 as a signal to use the parameter buffer. Replace that with an explicit indication (pbuf = 1), as the upcoming Vocaster support has a config item written via the parameter buffer with size = 1 rather than the implicit size of 8. Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 85 ++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 39 deletions(-) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 6031bfd98137..8d08f71bd4d6 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -436,11 +436,12 @@ enum { }; /* Location, size, and activation command number for the configuration - * parameters. Size is in bits and may be 0, 1, 8, or 16. + * parameters. Size is in bits and may be 1, 8, or 16. * - * A size of 0 indicates that the parameter is a byte-sized - * configuration which is set through the parameter buffer (but still - * read through the given offset location). + * Vocaster and 4th Gen devices have a parameter buffer to set certain + * configuration parameters. When pbuf is set, rather than writing to + * the given offset, the channel and value are written to the + * parameter buffer and the activate command is sent to the device. * * Some Gen 4 configuration parameters are written with 0x02 for a * desired value of 0x01, and 0x03 for 0x00. These are indicated with @@ -452,6 +453,7 @@ struct scarlett2_config { u16 offset; u8 size; u8 activate; + u8 pbuf; u8 mute; }; @@ -631,19 +633,21 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_solo = { .offset = 0x47, .size = 8, .activate = 4 }, [SCARLETT2_CONFIG_DIRECT_MONITOR] = { - .offset = 0x108, .activate = 12 }, + .offset = 0x108, .size = 8, .activate = 12, .pbuf = 1 }, [SCARLETT2_CONFIG_PHANTOM_SWITCH] = { - .offset = 0x46, .activate = 9, .mute = 1 }, + .offset = 0x46, .size = 8, .activate = 9, .pbuf = 1, + .mute = 1 }, [SCARLETT2_CONFIG_LEVEL_SWITCH] = { - .offset = 0x3d, .activate = 10, .mute = 1 }, + .offset = 0x3d, .size = 8, .activate = 10, .pbuf = 1, + .mute = 1 }, [SCARLETT2_CONFIG_AIR_SWITCH] = { - .offset = 0x3e, .activate = 11 }, + .offset = 0x3e, .size = 8, .activate = 11, .pbuf = 1 }, [SCARLETT2_CONFIG_PCM_INPUT_SWITCH] = { - .offset = 0x206, .activate = 25 }, + .offset = 0x206, .size = 8, .activate = 25, .pbuf = 1 }, [SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN] = { .offset = 0x232, .size = 16, .activate = 26 } @@ -656,37 +660,39 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_2i2 = { .param_buf_addr = 0xfc, .items = { [SCARLETT2_CONFIG_MSD_SWITCH] = { - .offset = 0x49, .size = 8, .activate = 4 }, // 0x41 ?? + .offset = 0x49, .size = 8, .activate = 4 }, [SCARLETT2_CONFIG_DIRECT_MONITOR] = { - .offset = 0x14a, .activate = 16 }, + .offset = 0x14a, .size = 8, .activate = 16, .pbuf = 1 }, [SCARLETT2_CONFIG_AUTOGAIN_SWITCH] = { - .offset = 0x135, .activate = 10 }, + .offset = 0x135, .size = 8, .activate = 10, .pbuf = 1 }, [SCARLETT2_CONFIG_AUTOGAIN_STATUS] = { - .offset = 0x137 }, + .offset = 0x137, .size = 8 }, [SCARLETT2_CONFIG_PHANTOM_SWITCH] = { - .offset = 0x48, .activate = 11, .mute = 1 }, + .offset = 0x48, .size = 8, .activate = 11, .pbuf = 1, + .mute = 1 }, [SCARLETT2_CONFIG_INPUT_GAIN] = { - .offset = 0x4b, .activate = 12 }, + .offset = 0x4b, .size = 8, .activate = 12, .pbuf = 1 }, [SCARLETT2_CONFIG_LEVEL_SWITCH] = { - .offset = 0x3c, .activate = 13, .mute = 1 }, + .offset = 0x3c, .size = 8, .activate = 13, .pbuf = 1, + .mute = 1 }, [SCARLETT2_CONFIG_SAFE_SWITCH] = { - .offset = 0x147, .activate = 14 }, + .offset = 0x147, .size = 8, .activate = 14, .pbuf = 1 }, [SCARLETT2_CONFIG_AIR_SWITCH] = { - .offset = 0x3e, .activate = 15 }, + .offset = 0x3e, .size = 8, .activate = 15, .pbuf = 1 }, [SCARLETT2_CONFIG_INPUT_SELECT_SWITCH] = { - .offset = 0x14b, .activate = 17 }, + .offset = 0x14b, .size = 8, .activate = 17, .pbuf = 1 }, [SCARLETT2_CONFIG_INPUT_LINK_SWITCH] = { - .offset = 0x14e, .activate = 18 }, + .offset = 0x14e, .size = 8, .activate = 18, .pbuf = 1 }, [SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN] = { .offset = 0x2a0, .size = 16, .activate = 36 } @@ -702,31 +708,33 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_4i4 = { .offset = 0x5c, .size = 8, .activate = 4 }, [SCARLETT2_CONFIG_AUTOGAIN_SWITCH] = { - .offset = 0x13e, .activate = 10 }, + .offset = 0x13e, .size = 8, .activate = 10, .pbuf = 1 }, [SCARLETT2_CONFIG_AUTOGAIN_STATUS] = { - .offset = 0x140 }, + .offset = 0x140, .size = 8 }, [SCARLETT2_CONFIG_PHANTOM_SWITCH] = { - .offset = 0x5a, .activate = 11, .mute = 1 }, + .offset = 0x5a, .size = 8, .activate = 11, .pbuf = 1, + .mute = 1 }, [SCARLETT2_CONFIG_INPUT_GAIN] = { - .offset = 0x5e, .activate = 12 }, + .offset = 0x5e, .size = 8, .activate = 12, .pbuf = 1 }, [SCARLETT2_CONFIG_LEVEL_SWITCH] = { - .offset = 0x4e, .activate = 13, .mute = 1 }, + .offset = 0x4e, .size = 8, .activate = 13, .pbuf = 1, + .mute = 1 }, [SCARLETT2_CONFIG_SAFE_SWITCH] = { - .offset = 0x150, .activate = 14 }, + .offset = 0x150, .size = 8, .activate = 14, .pbuf = 1 }, [SCARLETT2_CONFIG_AIR_SWITCH] = { - .offset = 0x50, .activate = 15 }, + .offset = 0x50, .size = 8, .activate = 15, .pbuf = 1 }, [SCARLETT2_CONFIG_INPUT_SELECT_SWITCH] = { - .offset = 0x153, .activate = 16 }, + .offset = 0x153, .size = 8, .activate = 16, .pbuf = 1 }, [SCARLETT2_CONFIG_INPUT_LINK_SWITCH] = { - .offset = 0x156, .activate = 17 }, + .offset = 0x156, .size = 8, .activate = 17, .pbuf = 1 }, [SCARLETT2_CONFIG_MASTER_VOLUME] = { .offset = 0x32, .size = 16 }, @@ -735,10 +743,10 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_4i4 = { .offset = 0x3a, .size = 16 }, [SCARLETT2_CONFIG_POWER_EXT] = { - .offset = 0x168 }, + .offset = 0x168, .size = 8 }, [SCARLETT2_CONFIG_POWER_LOW] = { - .offset = 0x16d } + .offset = 0x16d, .size = 8 } } }; @@ -2167,11 +2175,8 @@ static int scarlett2_usb_set_config( if (!config_item->offset) return -EFAULT; - /* Writes via the parameter buffer are selected with size = 0; - * these are only byte-sized values written through a shared - * location, different to the read address - */ - if (!config_item->size) { + /* Write via the parameter buffer? */ + if (config_item->pbuf) { if (!config_set->param_buf_addr) return -EFAULT; @@ -2187,7 +2192,7 @@ static int scarlett2_usb_set_config( if (err < 0) return err; - /* Request the interface do the write */ + /* Activate the write through the parameter buffer */ return scarlett2_usb_activate_config( mixer, config_item->activate); } @@ -2227,7 +2232,7 @@ static int scarlett2_usb_set_config( value = tmp; } - /* Send the configuration parameter data */ + /* Write the new value */ err = scarlett2_usb_set_data(mixer, offset, size, value); if (err < 0) return err; @@ -2237,7 +2242,9 @@ static int scarlett2_usb_set_config( if (err < 0) return err; - /* Writes via the parameter buffer don't need a separate save step */ + /* Interfaces with parameter buffer writes don't need a + * separate save step + */ if (config_set->param_buf_addr) return 0; From patchwork Tue Mar 12 18:35:30 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: 13590471 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 D217C16FF59 for ; Tue, 12 Mar 2024 18:35:32 +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=1710268534; cv=none; b=c9byhDIc6u90rz7vyvnUcsk6BOfRyvAuOLsdKOTSn2m3DUhPOa/vK7suGjnP5IjZLa0mOwy5sVrHXWpIjPzaZcf0bvhiR0qaj2b6It46fUe3VNb7SnjEGPmdI7vKtPhYXS5kB29Nt9lc1qBNpRKO2UOEWs7QiEKib5vTe0BCoZM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1710268534; c=relaxed/simple; bh=qamp655kzu1RbiF8tK0bonzwXVK36TvLV1BthDomM6U=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=EVgTHOzkuuIYmsMHuH6Q3GPzPU0pe1XeoHaiunsi56gViJ6mT0x6nzy/7c6BvFEJ9T8gzCwSHT+neom5RWRXlB/tX6njicQ7ySHO7iBw3ypsQ8zpQR7RJonFqMdUTP08ShGBN3Fjuy/YPv7rD6FqhMtIw07Qk4i9re6KeZndVdI= 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=F5tjVAKa; 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="F5tjVAKa" Received: by m.b4.vu (Postfix, from userid 1000) id 016BD604B628; Wed, 13 Mar 2024 05:05:31 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu 016BD604B628 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1710268531; bh=KAXIvFY7iPPCD82xX5+mrYk3b0cBShNJqDwZVsF3vjc=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=F5tjVAKanmhQ8gji/XjDI9boIAzhISbXlgbsZQwrjXWwSWmbiSl5HzPXnMdMll5mb z/XGVwFz5c221ypb/tM4wl3D2fyDFsG3Li9fDYvawzC/7nOhbEJuXmzdYyX65wzp6Z bzy6KuLMEp45dvAX3mGKnj1LLGVTyeK+rKg/0cnkGOblXtT6/2fbyiCSgPKPigNGMt KfzW7zYZMDEcrzHOgHiH3VWa127h8YwVNkMFYQ12HWPmv6YOnQRcr6blhUuzYugxjk 8ozXuVBqnYChUsJOKo+Xx9/MoJNbeDaUfOeiNVaawx+oTUITdCiTxiXcQKnN/qQcyC RjjVv9+Rxv/tg== Date: Wed, 13 Mar 2024 05:05:30 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , linux-sound@vger.kernel.org Subject: [PATCH 06/14] ALSA: scarlett2: Add support for config items with size = 32 Message-ID: References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Update scarlett2_usb_get_config() to support 32-bit values which are needed by the upcoming Vocaster support. Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 8d08f71bd4d6..f2bbf90680f8 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -436,7 +436,7 @@ enum { }; /* Location, size, and activation command number for the configuration - * parameters. Size is in bits and may be 1, 8, or 16. + * parameters. Size is in bits and may be 1, 8, 16, or 32. * * Vocaster and 4th Gen devices have a parameter buffer to set certain * configuration parameters. When pbuf is set, rather than writing to @@ -2102,6 +2102,11 @@ static int scarlett2_usb_get_config( for (i = 0; i < count; i++, buf_16++) *buf_16 = le16_to_cpu(*(__le16 *)buf_16); + } else if (size == 4) { + u32 *buf_32 = buf; + + for (i = 0; i < count; i++, buf_32++) + *buf_32 = le32_to_cpu(*(__le32 *)buf_32); } return 0; } From patchwork Tue Mar 12 18:35:47 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: 13590472 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 036B213DBBD for ; Tue, 12 Mar 2024 18:35: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=1710268550; cv=none; b=bCyCEPkyxRMc+PRSUQ1M/V8FmQUHMZ/fLO9Q0QizM7UrLc/NrPn/Jvb7cBTAigEzsh4FgrL9I9Q9Z/hHBhQWKLL8RBrVrK7enF40xAZFE04qXi6XVPWNl2t2ijGO2kQwCUXQ4I6h+OBsrDjzy3BEWxspQkThHFDRmRG7+FkJVIs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1710268550; c=relaxed/simple; bh=b0QsYWZAC02GtaXuLu1YtRXRyTnLmhhuFJLdfocaB1A=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=ptrl77YbAFyNIvxoWOi8lKGXAD7+hsSQujbkoGup5rhYWLSYHy384hMNsBgUPJoYMO0QuLJ5GO4f7rNk6QnzALZDuWXInLrdywzAQauVR2NGsXgJc4oO9K0zor4Kx30bptJbMzvZn72I58tJeqJhRiav5vuiSD5rbYsxqyxDARg= 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=rpkTL9mI; 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="rpkTL9mI" Received: by m.b4.vu (Postfix, from userid 1000) id 0E631604B628; Wed, 13 Mar 2024 05:05:47 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu 0E631604B628 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1710268547; bh=plriR9+9j1YGErDemRDvG9gbxvOPpc+kjkkJb3Qipzo=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=rpkTL9mIge9gXz1o9o1w1h+ZmIDUNnCj/2h5jc1Cwey4wbhfZ7F+x2yxrXe6Y3M8+ cituQ2BDaD+nWhhL2t1RINBvLqEnmqv6BOr4/6v3ZTNlkdGyVm7SfoJyZb7UqxS5oC innuz6gOMI6yRvM15M6hpJsqw+HOvYvlXXAxzLUHoABko5iv3mXg1+x4q6XACZa10F mcGwLrECHBaBljb0vctzwYlVqmUg40DqmBRZmoaqO/cXAZTHN0ep0A+bwNzJgllaHq uvw9GLobWLQz6Umg/NAcxKAgqZRZoVUBV3ZDoSna8fmcyyODqib14av/OVXByNl5W1 rdbF/lxB0mFIQ== Date: Wed, 13 Mar 2024 05:05:47 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , linux-sound@vger.kernel.org Subject: [PATCH 07/14] ALSA: scarlett2: Add additional input configuration parameters Message-ID: <167f04a37d0fb23f3077705df835adbc4f2b6a8e.1710264833.git.g@b4.vu> References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: The 4th Gen Scarlett interfaces added software-controllable input gain along with channel select, channel link, auto-gain, and "safe" mode. Vocaster has software-controllable input gain and auto-gain but not channel select, channel link, or safe mode. Add a device info field safe_input_count to indicate how many channels have a safe mode control, and use the presence of the input select and input link switch configuration parameters to determine if those controls should be created. Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 140 +++++++++++++++++++++--------------- 1 file changed, 82 insertions(+), 58 deletions(-) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index f2bbf90680f8..4289661b453f 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -916,6 +916,9 @@ struct scarlett2_device_info { /* the number of inputs with software-controllable gain */ u8 gain_input_count; + /* the number of inputs with safe mode */ + u8 safe_input_count; + /* the number of direct monitor options * (0 = none, 1 = mono only, 2 = mono/stereo) */ @@ -1550,6 +1553,7 @@ static const struct scarlett2_device_info s2i2_gen4_info = { .phantom_count = 1, .inputs_per_phantom = 2, .gain_input_count = 2, + .safe_input_count = 2, .direct_monitor = 2, .dsp_count = 2, @@ -1603,6 +1607,7 @@ static const struct scarlett2_device_info s4i4_gen4_info = { .phantom_count = 2, .inputs_per_phantom = 1, .gain_input_count = 2, + .safe_input_count = 2, .dsp_count = 2, .port_count = { @@ -2937,13 +2942,18 @@ static void scarlett2_autogain_update_access(struct usb_mixer_interface *mixer) int val = !scarlett2_autogain_is_running(private); int i; - scarlett2_set_ctl_access(private->input_select_ctl, val); - for (i = 0; i < info->gain_input_count / 2; i++) - scarlett2_set_ctl_access(private->input_link_ctls[i], val); - for (i = 0; i < info->gain_input_count; i++) { + if (scarlett2_has_config_item(private, + SCARLETT2_CONFIG_INPUT_SELECT_SWITCH)) + scarlett2_set_ctl_access(private->input_select_ctl, val); + if (scarlett2_has_config_item(private, + SCARLETT2_CONFIG_INPUT_LINK_SWITCH)) + for (i = 0; i < info->gain_input_count / 2; i++) + scarlett2_set_ctl_access(private->input_link_ctls[i], + val); + for (i = 0; i < info->gain_input_count; i++) scarlett2_set_ctl_access(private->input_gain_ctls[i], val); + for (i = 0; i < info->safe_input_count; i++) scarlett2_set_ctl_access(private->safe_ctls[i], val); - } for (i = 0; i < info->level_input_count; i++) scarlett2_set_ctl_access(private->level_ctls[i], val); for (i = 0; i < info->air_input_count; i++) @@ -2962,17 +2972,21 @@ static void scarlett2_autogain_notify_access(struct usb_mixer_interface *mixer) const struct scarlett2_device_info *info = private->info; int i; - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, - &private->input_select_ctl->id); - for (i = 0; i < info->gain_input_count / 2; i++) + if (scarlett2_has_config_item(private, + SCARLETT2_CONFIG_INPUT_SELECT_SWITCH)) snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, - &private->input_link_ctls[i]->id); - for (i = 0; i < info->gain_input_count; i++) { + &private->input_select_ctl->id); + if (scarlett2_has_config_item(private, + SCARLETT2_CONFIG_INPUT_LINK_SWITCH)) + for (i = 0; i < info->gain_input_count / 2; i++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, + &private->input_link_ctls[i]->id); + for (i = 0; i < info->gain_input_count; i++) snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, &private->input_gain_ctls[i]->id); + for (i = 0; i < info->safe_input_count; i++) snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, &private->safe_ctls[i]->id); - } for (i = 0; i < info->level_input_count; i++) snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, &private->level_ctls[i]->id); @@ -3183,7 +3197,9 @@ static int scarlett2_update_input_select(struct usb_mixer_interface *mixer) private->input_select_updated = 0; - if (!link_count) + if (!scarlett2_has_config_item(private, + SCARLETT2_CONFIG_INPUT_SELECT_SWITCH) || + !link_count) return 0; err = scarlett2_usb_get_config( @@ -3596,12 +3612,12 @@ static int scarlett2_update_input_safe(struct usb_mixer_interface *mixer) private->input_safe_updated = 0; - if (!info->gain_input_count) + if (!info->safe_input_count) return 0; return scarlett2_usb_get_config( mixer, SCARLETT2_CONFIG_SAFE_SWITCH, - info->gain_input_count, private->safe_switch); + info->safe_input_count, private->safe_switch); } static int scarlett2_safe_ctl_get(struct snd_kcontrol *kctl, @@ -5507,60 +5523,67 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer) return err; } - /* Add software-controllable input gain controls */ - if (info->gain_input_count) { + /* Add input select/link controls */ + if (scarlett2_has_config_item(private, + SCARLETT2_CONFIG_INPUT_SELECT_SWITCH)) { err = scarlett2_add_new_ctl( mixer, &scarlett2_input_select_ctl, 0, 1, "Input Select Capture Enum", &private->input_select_ctl); if (err < 0) return err; + } - for (i = 0; i < info->gain_input_count; i++) { - if (i % 2) { - scnprintf(s, sizeof(s), - "Line In %d-%d Link Capture Switch", - i, i + 1); - err = scarlett2_add_new_ctl( - mixer, &scarlett2_input_link_ctl, - i / 2, 1, s, - &private->input_link_ctls[i / 2]); - if (err < 0) - return err; - } - - scnprintf(s, sizeof(s), fmt, i + 1, - "Gain", "Volume"); + if (scarlett2_has_config_item(private, + SCARLETT2_CONFIG_INPUT_LINK_SWITCH)) { + for (i = 0; i < info->gain_input_count / 2; i++) { + scnprintf(s, sizeof(s), + "Line In %d-%d Link Capture Switch", + (i * 2) + 1, (i * 2) + 2); err = scarlett2_add_new_ctl( - mixer, &scarlett2_input_gain_ctl, - i, 1, s, &private->input_gain_ctls[i]); - if (err < 0) - return err; - - scnprintf(s, sizeof(s), fmt, i + 1, - "Autogain", "Switch"); - err = scarlett2_add_new_ctl( - mixer, &scarlett2_autogain_switch_ctl, - i, 1, s, &private->autogain_ctls[i]); - if (err < 0) - return err; - - scnprintf(s, sizeof(s), fmt, i + 1, - "Autogain Status", "Enum"); - err = scarlett2_add_new_ctl( - mixer, &scarlett2_autogain_status_ctl, - i, 1, s, &private->autogain_status_ctls[i]); - - scnprintf(s, sizeof(s), fmt, i + 1, - "Safe", "Switch"); - err = scarlett2_add_new_ctl( - mixer, &scarlett2_safe_ctl, - i, 1, s, &private->safe_ctls[i]); + mixer, &scarlett2_input_link_ctl, + i, 1, s, &private->input_link_ctls[i]); if (err < 0) return err; } } + /* Add software-controllable input gain controls */ + for (i = 0; i < info->gain_input_count; i++) { + scnprintf(s, sizeof(s), fmt, i + 1, + "Gain", "Volume"); + err = scarlett2_add_new_ctl( + mixer, &scarlett2_input_gain_ctl, + i, 1, s, &private->input_gain_ctls[i]); + if (err < 0) + return err; + + scnprintf(s, sizeof(s), fmt, i + 1, + "Autogain", "Switch"); + err = scarlett2_add_new_ctl( + mixer, &scarlett2_autogain_switch_ctl, + i, 1, s, &private->autogain_ctls[i]); + if (err < 0) + return err; + + scnprintf(s, sizeof(s), fmt, i + 1, + "Autogain Status", "Enum"); + err = scarlett2_add_new_ctl( + mixer, &scarlett2_autogain_status_ctl, + i, 1, s, &private->autogain_status_ctls[i]); + } + + /* Add safe-mode input switch controls */ + for (i = 0; i < info->safe_input_count; i++) { + scnprintf(s, sizeof(s), fmt, i + 1, + "Safe", "Switch"); + err = scarlett2_add_new_ctl( + mixer, &scarlett2_safe_ctl, + i, 1, s, &private->safe_ctls[i]); + if (err < 0) + return err; + } + /* Add PCM Input Switch control */ if (scarlett2_has_config_item(private, SCARLETT2_CONFIG_PCM_INPUT_SWITCH)) { @@ -6557,7 +6580,8 @@ static void scarlett2_notify_input_select(struct usb_mixer_interface *mixer) const struct scarlett2_device_info *info = private->info; int i; - if (!info->gain_input_count) + if (!scarlett2_has_config_item(private, + SCARLETT2_CONFIG_INPUT_SELECT_SWITCH)) return; private->input_select_updated = 1; @@ -6620,12 +6644,12 @@ static void scarlett2_notify_input_safe(struct usb_mixer_interface *mixer) const struct scarlett2_device_info *info = private->info; int i; - if (!info->gain_input_count) + if (!info->safe_input_count) return; private->input_safe_updated = 1; - for (i = 0; i < info->gain_input_count; i++) + for (i = 0; i < info->safe_input_count; i++) snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &private->safe_ctls[i]->id); } From patchwork Tue Mar 12 18:35:57 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: 13590473 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 9B9B313DBBD for ; Tue, 12 Mar 2024 18:35:58 +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=1710268560; cv=none; b=iIPMeQJHHTs96O3SkJWwQvfFG99IF3ESEpc6TusbBSgyw1qpPhoNrc9T7FMyXOJ67PJL9G0RSohRiLCHAMtcxLB7p1MTMczP3+IpTCcq77XIKsmcjuO252IvPFtfCcyLhG7JMVcAExTrzxDr3ED8WagbWjFAB3bLtm+P5mvdtHk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1710268560; c=relaxed/simple; bh=NpYcMdxeWEGeB9RImnZIoPiZTVL4XjVQF4zI/Q1bQOo=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=tpnof1Kb4MXyXPtCbDAlAc7MFUTZIXW5RWtVgzi9yBXZtfheWVe3RfKvrPNyxZ2nf/mxneVHlhChFSAk/nbTSmoHqKnsL9T9UFZbVmgBbaO9zBbRgp85KIu24iIeMRuO0WdNganSPCZMcsH2ivUe1KCJJeZVOgUjUOci0H8XoXE= 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=haK4j6Fz; 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="haK4j6Fz" Received: by m.b4.vu (Postfix, from userid 1000) id 16AB3604B628; Wed, 13 Mar 2024 05:05:57 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu 16AB3604B628 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1710268557; bh=rzhkE2N3RVEVOHihvgvzg1imkqoct9EeUgbCkz2z6U4=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=haK4j6FzukN92kVkXgW19R7aSPh3gXJ0UfS+I86tgpgboSFcoG3/mYqfj9ORrsxXh 21vuhuEMX//jYaxd6qGJ7eYmtDtp2B4H6eILcT/FaePbDPxZgx1fwZOPuy/CepiHOq /YnPqOa5rwdomB2U0nF2/xV4OeCJIpCYMADv5dbf+7ZyjN9S77AwCsGwYUGt1t15EQ I+VtrjfMOAENE78Flfjwkc58FindFgt0E/0bvOuuMFiddnqFKtaQVLbVPkAUDwUwAN 93wuiDG3NDivfFO4sv/XUdo/qK6kXHxGrF+CgM9W4BpXcw5z293ve3iYjmKNYUqAQy 6vQKiS8lUzrng== Date: Wed, 13 Mar 2024 05:05:57 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , linux-sound@vger.kernel.org Subject: [PATCH 08/14] ALSA: scarlett2: Define the maximum preamp input gain per-config-set Message-ID: References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Remove the #define SCARLETT2_MAX_GAIN_DB and replace with a per-config-set TLV as the Vocaster has a maximum gain of 70dB vs the 4th Gen 69dB. Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 4289661b453f..a891e92048b2 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -174,11 +174,10 @@ /* some gui mixers can't handle negative ctl values */ #define SCARLETT2_VOLUME_BIAS 127 -/* maximum preamp input gain and value - * values are from 0 to 70, preamp gain is from 0 to 69 dB +/* maximum preamp input gain value + * (the corresponding value in dB is per-device) */ #define SCARLETT2_MAX_GAIN_VALUE 70 -#define SCARLETT2_MAX_GAIN_DB 69 /* mixer range from -80dB to +6dB in 0.5dB steps */ #define SCARLETT2_MIXER_MIN_DB -80 @@ -460,9 +459,15 @@ struct scarlett2_config { struct scarlett2_config_set { const struct scarlett2_notification *notifications; u16 param_buf_addr; + const unsigned int *input_gain_tlv; const struct scarlett2_config items[SCARLETT2_CONFIG_COUNT]; }; +/* Input gain TLV dB ranges */ +static const DECLARE_TLV_DB_MINMAX( + db_scale_gen4_gain, 0, 69 * 100 +); + /* Gen 2 devices without SW/HW volume switch: 6i6, 18i8 */ static const struct scarlett2_config_set scarlett2_config_set_gen2a = { @@ -658,6 +663,7 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_solo = { static const struct scarlett2_config_set scarlett2_config_set_gen4_2i2 = { .notifications = scarlett4_2i2_notifications, .param_buf_addr = 0xfc, + .input_gain_tlv = db_scale_gen4_gain, .items = { [SCARLETT2_CONFIG_MSD_SWITCH] = { .offset = 0x49, .size = 8, .activate = 4 }, @@ -703,6 +709,7 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_2i2 = { static const struct scarlett2_config_set scarlett2_config_set_gen4_4i4 = { .notifications = scarlett4_4i4_notifications, .param_buf_addr = 0x130, + .input_gain_tlv = db_scale_gen4_gain, .items = { [SCARLETT2_CONFIG_MSD_SWITCH] = { .offset = 0x5c, .size = 8, .activate = 4 }, @@ -3587,10 +3594,6 @@ static int scarlett2_input_gain_ctl_put(struct snd_kcontrol *kctl, return err; } -static const DECLARE_TLV_DB_MINMAX( - db_scale_scarlett2_gain, 0, SCARLETT2_MAX_GAIN_DB * 100 -); - static const struct snd_kcontrol_new scarlett2_input_gain_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | @@ -3600,7 +3603,6 @@ static const struct snd_kcontrol_new scarlett2_input_gain_ctl = { .get = scarlett2_input_gain_ctl_get, .put = scarlett2_input_gain_ctl_put, .private_value = 0, /* max value */ - .tlv = { .p = db_scale_scarlett2_gain } }; /*** Safe Controls ***/ @@ -5557,6 +5559,8 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer) i, 1, s, &private->input_gain_ctls[i]); if (err < 0) return err; + private->input_gain_ctls[i]->tlv.p = + private->config_set->input_gain_tlv; scnprintf(s, sizeof(s), fmt, i + 1, "Autogain", "Switch"); From patchwork Tue Mar 12 18:36:04 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: 13590474 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 8CAA7140E5B for ; Tue, 12 Mar 2024 18:36:06 +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=1710268568; cv=none; b=Yfv2I2bgSg1tNF1vdqXCx2LLXeWz9hE6dXvyVdbbp+UrajSW2DeOTPzrZ3n+U4l89tZy3ez/2mszqE0ply32PNcLAYNFaPM3YyppzMEmgnqmddBd/uR9os4WcQ82WTSfQo1yG2EQlPZpgLfspttNKKhKtAc4lDbVmz58u9Yyu4Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1710268568; c=relaxed/simple; bh=8amXzmNwginEUr5jO1EUU1HI1/vQ21QGhNcsCKOY1LE=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=PspMS4DtdPaHvj7idgeDDYJ1ALhnED1YU2r0CWeVrcSZBx2mSfec7XCZilIzAXanu+89dDQ15OTCpGKoLGKKu1XUjzqgR0EZgb5SYxoKXBhAeDLqR1LzQBkHCQqpn+SUNSKNS22P5eq9sW4TgjnNkenikYcfSxgv055fedUt9+Q= 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=A2ObAdmY; 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="A2ObAdmY" Received: by m.b4.vu (Postfix, from userid 1000) id BB31A604B628; Wed, 13 Mar 2024 05:06:04 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu BB31A604B628 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1710268564; bh=SAIJvPQiAJ9RaAEdzmkuiaTx1WqyXWtOoZgrbFdqCQY=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=A2ObAdmY8lrzaL+KC95pnsXBC0mkfv+KNNSGfRAsDWagmbItf/ceq1wJLPQl6WQ9N vMSojWqAkoKJ1sEPS6ugRQf9O6ibPZDWkD6T9VjE5Mva0bqsdQuDQHQO1uOXBSwABi 9inFz1EEc/emNZzaASzd3xk7+i9H2zSn3usf7kgDn6dqByFG/dINWonyv0LJ+cgK6z lW5pFbbVOrU7Ii0qYFz35djILnm+KtFowD1nACx4EyIsaUGHOHYOuMfFt/RQGPtczh gzakyaXu1KKAMqc9PwFOcc4TsI1CB675p3UNWgSqzAB37vHVobgXTCDRmKFxxwnXbz MTNB5xuZTkzpA== Date: Wed, 13 Mar 2024 05:06:04 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , linux-sound@vger.kernel.org Subject: [PATCH 09/14] ALSA: scarlett2: Define autogain status texts per-config-set Message-ID: References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: The autogain status texts are different for Vocaster vs. Scarlett 4th Gen, so make them configurable per-config-set. Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index a891e92048b2..0962277947bf 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -294,7 +294,7 @@ static const char *const scarlett2_dim_mute_names[SCARLETT2_DIM_MUTE_COUNT] = { * If autogain_switch is set, autogain_status is set to 0 (Running). * The other status values are from the raw_autogain_status value + 1. */ -static const char *const scarlett2_autogain_status_texts[] = { +static const char *const scarlett2_autogain_status_gen4[] = { "Running", "Success", "SuccessDRover", @@ -303,7 +303,8 @@ static const char *const scarlett2_autogain_status_texts[] = { "FailMaxGainLimit", "FailClipped", "Cancelled", - "Invalid" + "Invalid", + NULL }; /* Power Status Values */ @@ -460,6 +461,7 @@ struct scarlett2_config_set { const struct scarlett2_notification *notifications; u16 param_buf_addr; const unsigned int *input_gain_tlv; + const char *const *autogain_status_texts; const struct scarlett2_config items[SCARLETT2_CONFIG_COUNT]; }; @@ -664,6 +666,7 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_2i2 = { .notifications = scarlett4_2i2_notifications, .param_buf_addr = 0xfc, .input_gain_tlv = db_scale_gen4_gain, + .autogain_status_texts = scarlett2_autogain_status_gen4, .items = { [SCARLETT2_CONFIG_MSD_SWITCH] = { .offset = 0x49, .size = 8, .activate = 4 }, @@ -710,6 +713,7 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_4i4 = { .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 }, @@ -981,6 +985,7 @@ struct scarlett2_data { u8 num_mix_out; u8 num_line_out; u8 num_monitor_mix_ctls; + u8 num_autogain_status_texts; u32 firmware_version; u8 flash_segment_nums[SCARLETT2_SEGMENT_ID_COUNT]; u8 flash_segment_blocks[SCARLETT2_SEGMENT_ID_COUNT]; @@ -2931,12 +2936,12 @@ static int scarlett2_update_autogain(struct usb_mixer_interface *mixer) if (private->autogain_switch[i]) private->autogain_status[i] = 0; else if (raw_autogain_status[i] < - ARRAY_SIZE(scarlett2_autogain_status_texts) - 1) + private->num_autogain_status_texts - 1) private->autogain_status[i] = raw_autogain_status[i] + 1; else private->autogain_status[i] = - ARRAY_SIZE(scarlett2_autogain_status_texts) - 1; + private->num_autogain_status_texts - 1; return 0; } @@ -3171,10 +3176,13 @@ static int scarlett2_autogain_switch_ctl_put( static int scarlett2_autogain_status_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; + return snd_ctl_enum_info( uinfo, 1, - ARRAY_SIZE(scarlett2_autogain_status_texts), - scarlett2_autogain_status_texts); + private->num_autogain_status_texts, + private->config_set->autogain_status_texts); } static const struct snd_kcontrol_new scarlett2_autogain_switch_ctl = { @@ -6839,8 +6847,9 @@ static void scarlett2_private_suspend(struct usb_mixer_interface *mixer) static void scarlett2_count_io(struct scarlett2_data *private) { const struct scarlett2_device_info *info = private->info; + const struct scarlett2_config_set *config_set = info->config_set; const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; - int port_type, srcs = 0, dsts = 0; + int port_type, srcs = 0, dsts = 0, i; /* Count the number of mux sources and destinations */ for (port_type = 0; @@ -6872,6 +6881,15 @@ static void scarlett2_count_io(struct scarlett2_data *private) /* Number of monitor mix controls */ private->num_monitor_mix_ctls = info->direct_monitor * 2 * private->num_mix_in; + + /* Number of autogain status texts */ + if (config_set->autogain_status_texts) { + const char * const *texts = config_set->autogain_status_texts; + + for (i = 0; texts[i]; i++) + ; + private->num_autogain_status_texts = i; + } } /* Look through the interface descriptors for the Focusrite Control From patchwork Tue Mar 12 18:36:23 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: 13590482 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 3EA9113E7DD for ; Tue, 12 Mar 2024 18:36:24 +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=1710268587; cv=none; b=LqRGlMpC2kgI3uTYmptpLCdS+ZgjTVJHApukJ+LpY9FNUNk1TZ61AXahWK5bgzPZjmcGJ1bchFVncsJtEVq5PTXwthpPUpnJGQja6e2JwmZ4ag8cowEWitbiQua0x+vedPXkBe7KgXChtJjP2RHCcpIzOwBhZhcrNBAUhUjFSNo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1710268587; c=relaxed/simple; bh=bGrJQ+yp28q5ADIuKIQ+YcQzy6Avxt/eFDL8GXGM5Bs=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=eJNvXoY691KETNntjypLFDjKntn4r5R1RwKN0YAqYTFsOV7wPZeaEl5hO4tAHB+2FOEBlsLlxdY3tVbgb/ohgPZ9G9RI2FiHllbLP82i4REL4vJwN4saAfKtvXl908xdBcU/amHZpTjO7Phc1Ja7fylZmJqY3LTTrsGZRBCC0LQ= 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=LUzJ4Tzr; 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="LUzJ4Tzr" Received: by m.b4.vu (Postfix, from userid 1000) id 4F264604B628; Wed, 13 Mar 2024 05:06:23 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu 4F264604B628 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1710268583; bh=YwNuF5GddlaNZCDZwloSIo5hvxO3qjlUg2kg6GmFf4Y=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=LUzJ4TzrNFnxxRox1UI2saDlpEGzx07HIsQiTw7e2vlHuD8scegtwqxSVtjZ7JDtm wxH03pkkmGwP9dZ0hpgXBpjLConzHp6Nz/izxdHNg4kKDKlJUmUfVv+gSRFQMnD8SC DomKe6amYlNON6/p8NgfbgGwUXhDO3UUE75EwxtRd/KTCYmaTfgB5knZ8Dm+PrLSSs AWF7kDzyjYwHCefs/CyXXpAwB8cFr/3japyjCdNwkySuZZKsYBvreTob95TMKRnwRi Yb+vePl7UwtBP2Z5rKymHXaKpyCD3zusR0uRJHucPvEBQNXWoaU+s1bTmozSZYbv+l XLg2U6C1v0Fkw== Date: Wed, 13 Mar 2024 05:06:23 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , linux-sound@vger.kernel.org Subject: [PATCH 10/14] ALSA: scarlett2: Add input mute controls Message-ID: <3b384b4e759241bd06f0c223e9f4f00467d88318.1710264833.git.g@b4.vu> References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Add controls for the input mute switches that the Vocaster interfaces have. Mark scarlett2_notify_input_mute() as __always_unused until it gets used when the Vocaster callback function array is added. Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 142 +++++++++++++++++++++++++++++++++++- 1 file changed, 141 insertions(+), 1 deletion(-) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 0962277947bf..17fadfddc240 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -85,7 +85,7 @@ * controls * - disable/enable MSD mode * - disable/enable standalone mode - * - input gain, autogain, safe mode + * - input mute, gain, autogain, safe mode * - direct monitor mixes * * @@ -214,6 +214,7 @@ static const u16 scarlett2_mixer_values[SCARLETT2_MIXER_VALUE_COUNT] = { #define SCARLETT2_LEVEL_SWITCH_MAX 2 #define SCARLETT2_PAD_SWITCH_MAX 8 #define SCARLETT2_AIR_SWITCH_MAX 8 +#define SCARLETT2_INPUT_MUTE_SWITCH_MAX 2 #define SCARLETT2_PHANTOM_SWITCH_MAX 2 #define SCARLETT2_INPUT_GAIN_MAX 2 @@ -329,6 +330,7 @@ static void scarlett2_notify_volume(struct usb_mixer_interface *mixer); static void scarlett2_notify_input_level(struct usb_mixer_interface *mixer); static void scarlett2_notify_input_pad(struct usb_mixer_interface *mixer); static void scarlett2_notify_input_air(struct usb_mixer_interface *mixer); +static void scarlett2_notify_input_mute(struct usb_mixer_interface *mixer); static void scarlett2_notify_input_phantom(struct usb_mixer_interface *mixer); static void scarlett2_notify_input_other(struct usb_mixer_interface *mixer); static void scarlett2_notify_input_select(struct usb_mixer_interface *mixer); @@ -415,6 +417,7 @@ enum { SCARLETT2_CONFIG_PAD_SWITCH, SCARLETT2_CONFIG_MSD_SWITCH, SCARLETT2_CONFIG_AIR_SWITCH, + SCARLETT2_CONFIG_INPUT_MUTE_SWITCH, SCARLETT2_CONFIG_STANDALONE_SWITCH, SCARLETT2_CONFIG_PHANTOM_SWITCH, SCARLETT2_CONFIG_PHANTOM_PERSISTENCE, @@ -915,6 +918,11 @@ struct scarlett2_device_info { */ u8 air_option; + /* the number of analogue inputs with a software switchable + * mute control + */ + u8 mute_input_count; + /* the number of phantom (48V) software switchable controls */ u8 phantom_count; @@ -996,6 +1004,7 @@ struct scarlett2_data { u8 input_level_updated; u8 input_pad_updated; u8 input_air_updated; + u8 input_mute_updated; u8 input_phantom_updated; u8 input_select_updated; u8 input_gain_updated; @@ -1018,6 +1027,7 @@ struct scarlett2_data { u8 pad_switch[SCARLETT2_PAD_SWITCH_MAX]; u8 dim_mute[SCARLETT2_DIM_MUTE_COUNT]; u8 air_switch[SCARLETT2_AIR_SWITCH_MAX]; + u8 input_mute_switch[SCARLETT2_INPUT_MUTE_SWITCH_MAX]; u8 phantom_switch[SCARLETT2_PHANTOM_SWITCH_MAX]; u8 phantom_persistence; u8 input_select_switch; @@ -1045,6 +1055,7 @@ struct scarlett2_data { struct snd_kcontrol *level_ctls[SCARLETT2_LEVEL_SWITCH_MAX]; struct snd_kcontrol *pad_ctls[SCARLETT2_PAD_SWITCH_MAX]; struct snd_kcontrol *air_ctls[SCARLETT2_AIR_SWITCH_MAX]; + struct snd_kcontrol *input_mute_ctls[SCARLETT2_INPUT_MUTE_SWITCH_MAX]; struct snd_kcontrol *phantom_ctls[SCARLETT2_PHANTOM_SWITCH_MAX]; struct snd_kcontrol *input_select_ctl; struct snd_kcontrol *input_link_ctls[SCARLETT2_INPUT_GAIN_MAX / 2]; @@ -2970,6 +2981,8 @@ static void scarlett2_autogain_update_access(struct usb_mixer_interface *mixer) scarlett2_set_ctl_access(private->level_ctls[i], val); for (i = 0; i < info->air_input_count; i++) scarlett2_set_ctl_access(private->air_ctls[i], val); + for (i = 0; i < info->mute_input_count; i++) + scarlett2_set_ctl_access(private->input_mute_ctls[i], val); for (i = 0; i < info->phantom_count; i++) scarlett2_set_ctl_access(private->phantom_ctls[i], val); } @@ -3005,6 +3018,9 @@ static void scarlett2_autogain_notify_access(struct usb_mixer_interface *mixer) for (i = 0; i < info->air_input_count; i++) snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, &private->air_ctls[i]->id); + for (i = 0; i < info->mute_input_count; i++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, + &private->input_mute_ctls[i]->id); for (i = 0; i < info->phantom_count; i++) snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, &private->phantom_ctls[i]->id); @@ -4619,6 +4635,100 @@ static const struct snd_kcontrol_new scarlett2_air_ctl[2] = { } }; +/*** Input Mute Switch Controls ***/ + +static int scarlett2_update_input_mute(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + + private->input_mute_updated = 0; + + if (!info->mute_input_count) + return 0; + + return scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_INPUT_MUTE_SWITCH, + info->mute_input_count, private->input_mute_switch); +} + +static int scarlett2_input_mute_ctl_get(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 err = 0; + + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + if (private->input_mute_updated) { + err = scarlett2_update_input_mute(mixer); + if (err < 0) + goto unlock; + } + ucontrol->value.integer.value[0] = + private->input_mute_switch[elem->control]; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static int scarlett2_input_mute_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 index = elem->control; + int oval, val, err; + + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + err = scarlett2_check_put_during_autogain(mixer); + if (err < 0) + goto unlock; + + oval = private->input_mute_switch[index]; + val = ucontrol->value.integer.value[0]; + + if (oval == val) + goto unlock; + + private->input_mute_switch[index] = val; + + /* Send switch change to the device */ + err = scarlett2_usb_set_config( + mixer, SCARLETT2_CONFIG_INPUT_MUTE_SWITCH, + index, val); + if (err == 0) + err = 1; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static const struct snd_kcontrol_new scarlett2_input_mute_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = scarlett2_autogain_disables_ctl_info, + .get = scarlett2_input_mute_ctl_get, + .put = scarlett2_input_mute_ctl_put, +}; + /*** Phantom Switch Controls ***/ static int scarlett2_update_input_phantom(struct usb_mixer_interface *mixer) @@ -5497,6 +5607,16 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer) return err; } + /* Add input mute controls */ + for (i = 0; i < info->mute_input_count; i++) { + scnprintf(s, sizeof(s), fmt, i + 1, "Mute", "Switch"); + err = scarlett2_add_new_ctl( + mixer, &scarlett2_input_mute_ctl, + i, 1, s, &private->input_mute_ctls[i]); + if (err < 0) + return err; + } + /* Add input phantom controls */ if (info->inputs_per_phantom == 1) { for (i = 0; i < info->phantom_count; i++) { @@ -6558,6 +6678,22 @@ static void scarlett2_notify_input_air(struct usb_mixer_interface *mixer) &private->air_ctls[i]->id); } +/* Notify on input mute switch change */ +static __always_unused void scarlett2_notify_input_mute( + struct usb_mixer_interface *mixer) +{ + struct snd_card *card = mixer->chip->card; + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + int i; + + private->input_mute_updated = 1; + + for (i = 0; i < info->mute_input_count; i++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->input_mute_ctls[i]->id); +} + /* Notify on input phantom switch change */ static void scarlett2_notify_input_phantom(struct usb_mixer_interface *mixer) { @@ -7171,6 +7307,10 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer) if (err < 0) return err; + err = scarlett2_update_input_mute(mixer); + if (err < 0) + return err; + err = scarlett2_update_input_phantom(mixer); if (err < 0) return err; From patchwork Tue Mar 12 18:37:12 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: 13590483 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 B048113E7DD for ; Tue, 12 Mar 2024 18:37:15 +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=1710268638; cv=none; b=ck2IQ2Usqu6wrnSM+iX/Y6cPYxXjPdYQ+D04Rmwcp4S8oYreWHpieNmfUA4FcR5KMnU/wb/Jf9S2AZDbcDkBLXZj2/dZ0aryTZi4Xrquqv9+57r9qcodZddZ/0GS/qfYRbN/DOb7IQirmPL0SRcK2D4e4Ejt2QgJ81nLtD8S7BY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1710268638; c=relaxed/simple; bh=dyYJllAxwWnylW9fDh5uCXGYTcRPZ8NtDR0TaLbbQuk=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=CtjdnVcw0/8HeQI3FY1seKHVk9g1aGLMn8eHeajmwIVO+DqsWM/mRsrtDSbI2mof/dDhe9kLi29ayuyM9SyRoOEfBU46XB3RaxipelXAcgRyPrOsueFUW+nMSlcedlH2HgJ9LbKbS+R7q/iZjwbdUZj0Yrio+aQMBQ6iezf7te8= 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=QRnlQqxQ; 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="QRnlQqxQ" Received: by m.b4.vu (Postfix, from userid 1000) id DD823604B628; Wed, 13 Mar 2024 05:07:12 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu DD823604B628 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1710268632; bh=EkLd33Qyc4kNdSj4yBLrsJDI5OeWlCtkiWvcKOVdv0s=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=QRnlQqxQI6k6SSioTnAnU+19ehP3h7J/mMII3uW1Z4v8hPVDRLljRTpCEeZUzmXjG NEseAvKJVBTDUwEsC575KwVaZGYlVDrlD7keZeZmkEdAKsncvVTnwA2dWAayzR8Y32 DecygYZhNKwSUx7ZVT6U+0zZb5etm8w6JECG7GCo4LCtsh/fyUaPfyvlYDp0XQzSSM VxjN+lV/R0UXqtb7eObdgsjerqdlh2rWKm06VX8sC3uSrvwegnYTuwb5s1c6Xg93lm 6Oaqxp+wfm4ahLqH5VhyiPtOJQ6TWmEodrtnl6AqVl0TNu53QRTzfktfM4yLwYJlt7 hCxt4Zj+mWGDA== Date: Wed, 13 Mar 2024 05:07:12 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , linux-sound@vger.kernel.org Subject: [PATCH 11/14] ALSA: scarlett2: Add DSP controls Message-ID: References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Add filter and compressor DSP controls for the Vocaster interfaces. Mark scarlett2_notify_input_dsp() as __always_unused until it gets used when the Vocaster callback function array is added. Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 855 ++++++++++++++++++++++++++++++++++++ 1 file changed, 855 insertions(+) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 17fadfddc240..77bfaadb99b8 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -87,6 +87,7 @@ * - disable/enable standalone mode * - input mute, gain, autogain, safe mode * - direct monitor mixes + * - compressor and EQ * * * /--------------\ 18chn 20chn /--------------\ @@ -214,6 +215,7 @@ static const u16 scarlett2_mixer_values[SCARLETT2_MIXER_VALUE_COUNT] = { #define SCARLETT2_LEVEL_SWITCH_MAX 2 #define SCARLETT2_PAD_SWITCH_MAX 8 #define SCARLETT2_AIR_SWITCH_MAX 8 +#define SCARLETT2_DSP_SWITCH_MAX 2 #define SCARLETT2_INPUT_MUTE_SWITCH_MAX 2 #define SCARLETT2_PHANTOM_SWITCH_MAX 2 #define SCARLETT2_INPUT_GAIN_MAX 2 @@ -245,6 +247,59 @@ static const u16 scarlett2_mixer_values[SCARLETT2_MIXER_VALUE_COUNT] = { /* Maximum number of meters (sum of output port counts) */ #define SCARLETT2_MAX_METERS 65 +/* Compressor parameter data + * + * The compressor parameters are 32-bit fixed point values with 24 + * bits of fraction. Integer values are sufficient for the parameters + * except for ratio which we can set in 0.5:1 steps. + */ +struct compressor_param { + const char *name; + snd_ctl_elem_type_t type; + s32 min; + s32 max; + int scale_bits; +}; + +/* The available compressor parameters on the Vocaster: + * - Enable: Off, On + * - Threshold: -40dB to 0dB + * - Ratio: 1:1 to 50:1 in 0.5:1 steps + * - Knee Width: 0dB to 10dB + * - Attack: 30ms to 127ms + * - Release: 30ms to 127ms + * - Makeup Gain: 0dB to 24dB + */ +static const struct compressor_param compressor_params[] = { + { "Enable", SNDRV_CTL_ELEM_TYPE_BOOLEAN, 0, 1, 0 }, + { "Threshold", SNDRV_CTL_ELEM_TYPE_INTEGER, -40, 0, 24 }, + { "Ratio", SNDRV_CTL_ELEM_TYPE_INTEGER, 2, 100, 23 }, + { "Knee Width", SNDRV_CTL_ELEM_TYPE_INTEGER, 0, 10, 24 }, + { "Attack", SNDRV_CTL_ELEM_TYPE_INTEGER, 30, 127, 24 }, + { "Release", SNDRV_CTL_ELEM_TYPE_INTEGER, 30, 127, 24 }, + { "Makeup Gain", SNDRV_CTL_ELEM_TYPE_INTEGER, 0, 24, 24 }, +}; + +#define SCARLETT2_COMPRESSOR_PARAM_COUNT ARRAY_SIZE(compressor_params) +#define SCARLETT2_COMPRESSOR_CTLS_MAX \ + (SCARLETT2_COMPRESSOR_PARAM_COUNT * SCARLETT2_DSP_SWITCH_MAX) + +/* Maximum number of filter controls */ +#define SCARLETT2_PRECOMP_FLT_CTLS_MAX (2 * SCARLETT2_DSP_SWITCH_MAX) +#define SCARLETT2_PEQ_FLT_CTLS_MAX (3 * SCARLETT2_DSP_SWITCH_MAX) + +/* Number of biquad filter coefficients */ +#define SCARLETT2_BIQUAD_COEFFS 5 + +/* Maximum number of filter coefficient values */ +#define SCARLETT2_PRECOMP_FLT_VALUES_MAX \ + (SCARLETT2_PRECOMP_FLT_CTLS_MAX * SCARLETT2_BIQUAD_COEFFS) +#define SCARLETT2_PEQ_FLT_VALUES_MAX \ + (SCARLETT2_PEQ_FLT_CTLS_MAX * SCARLETT2_BIQUAD_COEFFS) + +/* Maximum number of PEQ filter slots */ +#define SCARLETT2_PEQ_FLT_SLOTS_MAX 4 + /* Hardware port types: * - None (no input to mux) * - Analogue I/O @@ -330,6 +385,7 @@ static void scarlett2_notify_volume(struct usb_mixer_interface *mixer); static void scarlett2_notify_input_level(struct usb_mixer_interface *mixer); static void scarlett2_notify_input_pad(struct usb_mixer_interface *mixer); static void scarlett2_notify_input_air(struct usb_mixer_interface *mixer); +static void scarlett2_notify_input_dsp(struct usb_mixer_interface *mixer); static void scarlett2_notify_input_mute(struct usb_mixer_interface *mixer); static void scarlett2_notify_input_phantom(struct usb_mixer_interface *mixer); static void scarlett2_notify_input_other(struct usb_mixer_interface *mixer); @@ -417,6 +473,12 @@ enum { SCARLETT2_CONFIG_PAD_SWITCH, SCARLETT2_CONFIG_MSD_SWITCH, SCARLETT2_CONFIG_AIR_SWITCH, + SCARLETT2_CONFIG_DSP_SWITCH, + SCARLETT2_CONFIG_COMPRESSOR_PARAMS, + SCARLETT2_CONFIG_PRECOMP_FLT_SWITCH, + SCARLETT2_CONFIG_PRECOMP_FLT_PARAMS, + SCARLETT2_CONFIG_PEQ_FLT_SWITCH, + SCARLETT2_CONFIG_PEQ_FLT_PARAMS, SCARLETT2_CONFIG_INPUT_MUTE_SWITCH, SCARLETT2_CONFIG_STANDALONE_SWITCH, SCARLETT2_CONFIG_PHANTOM_SWITCH, @@ -918,6 +980,18 @@ struct scarlett2_device_info { */ u8 air_option; + /* the number of analogue inputs with DSP control */ + u8 dsp_input_count; + + /* number of pre-compressor filters */ + u8 precomp_flt_count; + + /* number of parametric EQ filters */ + u8 peq_flt_count; + + /* number of PEQ filters plus unused slots */ + u8 peq_flt_total_count; + /* the number of analogue inputs with a software switchable * mute control */ @@ -1004,6 +1078,7 @@ struct scarlett2_data { u8 input_level_updated; u8 input_pad_updated; u8 input_air_updated; + u8 input_dsp_updated; u8 input_mute_updated; u8 input_phantom_updated; u8 input_select_updated; @@ -1027,6 +1102,12 @@ struct scarlett2_data { u8 pad_switch[SCARLETT2_PAD_SWITCH_MAX]; u8 dim_mute[SCARLETT2_DIM_MUTE_COUNT]; u8 air_switch[SCARLETT2_AIR_SWITCH_MAX]; + u8 dsp_switch[SCARLETT2_DSP_SWITCH_MAX]; + s32 compressor_values[SCARLETT2_COMPRESSOR_CTLS_MAX]; + s32 precomp_flt_values[SCARLETT2_PRECOMP_FLT_VALUES_MAX]; + s32 peq_flt_values[SCARLETT2_PEQ_FLT_VALUES_MAX]; + u8 precomp_flt_switch[SCARLETT2_DSP_SWITCH_MAX]; + u8 peq_flt_switch[SCARLETT2_DSP_SWITCH_MAX]; u8 input_mute_switch[SCARLETT2_INPUT_MUTE_SWITCH_MAX]; u8 phantom_switch[SCARLETT2_PHANTOM_SWITCH_MAX]; u8 phantom_persistence; @@ -1055,6 +1136,7 @@ struct scarlett2_data { struct snd_kcontrol *level_ctls[SCARLETT2_LEVEL_SWITCH_MAX]; struct snd_kcontrol *pad_ctls[SCARLETT2_PAD_SWITCH_MAX]; struct snd_kcontrol *air_ctls[SCARLETT2_AIR_SWITCH_MAX]; + struct snd_kcontrol *dsp_ctls[SCARLETT2_DSP_SWITCH_MAX]; struct snd_kcontrol *input_mute_ctls[SCARLETT2_INPUT_MUTE_SWITCH_MAX]; struct snd_kcontrol *phantom_ctls[SCARLETT2_PHANTOM_SWITCH_MAX]; struct snd_kcontrol *input_select_ctl; @@ -1066,6 +1148,11 @@ struct scarlett2_data { struct snd_kcontrol *pcm_input_switch_ctl; struct snd_kcontrol *mux_ctls[SCARLETT2_MUX_MAX]; struct snd_kcontrol *mix_ctls[SCARLETT2_MIX_MAX]; + struct snd_kcontrol *compressor_ctls[SCARLETT2_COMPRESSOR_CTLS_MAX]; + struct snd_kcontrol *precomp_flt_ctls[SCARLETT2_PRECOMP_FLT_CTLS_MAX]; + struct snd_kcontrol *peq_flt_ctls[SCARLETT2_PEQ_FLT_CTLS_MAX]; + struct snd_kcontrol *precomp_flt_switch_ctls[SCARLETT2_DSP_SWITCH_MAX]; + struct snd_kcontrol *peq_flt_switch_ctls[SCARLETT2_DSP_SWITCH_MAX]; struct snd_kcontrol *direct_monitor_ctl; struct snd_kcontrol *speaker_switching_ctl; struct snd_kcontrol *talkback_ctl; @@ -2174,6 +2261,54 @@ static int scarlett2_usb_set_data( &req, sizeof(u32) * 2 + size, NULL, 0); } +/* Send a SCARLETT2_USB_SET_DATA command with multiple values. + * offset: location in the device's data space + * size: size in bytes of each value (1, 2, 4) + * count: number of values + */ +static int scarlett2_usb_set_data_buf( + struct usb_mixer_interface *mixer, + int offset, int size, int count, void *buf) +{ + struct scarlett2_data *private = mixer->private_data; + int bytes = size * count; + struct { + __le32 offset; + __le32 size; + u8 data[]; + } __packed *req; + int err; + int buf_size = struct_size(req, data, bytes); + + req = kmalloc(buf_size, GFP_KERNEL); + if (!req) + return -ENOMEM; + + req->offset = cpu_to_le32(offset); + req->size = cpu_to_le32(bytes); + if (size == 1) { + memcpy(req->data, buf, count); + } else if (size == 2) { + u16 *buf_16 = buf; + int i; + + for (i = 0; i < count; i++) + ((__le16 *)req->data)[i] = cpu_to_le16(buf_16[i]); + } else { + u32 *buf_32 = buf; + int i; + + for (i = 0; i < count; i++) + ((__le32 *)req->data)[i] = cpu_to_le32(buf_32[i]); + } + + err = scarlett2_usb(private->mixer, SCARLETT2_USB_SET_DATA, + req, buf_size, NULL, 0); + + kfree(req); + return err; +} + /* Send a SCARLETT2_USB_DATA_CMD command. * Configuration changes require activation with this after they have * been uploaded by a previous SCARLETT2_USB_SET_DATA. @@ -2288,6 +2423,47 @@ static int scarlett2_usb_set_config( return 0; } +/* Send USB messages to set a SCARLETT2_CONFIG_* parameter with + * multiple values + */ +static int scarlett2_usb_set_config_buf( + struct usb_mixer_interface *mixer, + int config_item_num, int index, int count, void *buf) +{ + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_config_set *config_set = private->config_set; + const struct scarlett2_config *config_item = + &config_set->items[config_item_num]; + int offset, size; + int err; + + /* Check that the configuration item is present in the + * configuration set used by this device + */ + if (!config_item->offset) + return -EFAULT; + + /* Convert config_item->size in bits to size in bytes and + * calculate offset + */ + if (config_item->size >= 8) { + size = config_item->size / 8; + offset = config_item->offset + index * size; + + /* Bit updates not supported */ + } else { + return -EFAULT; + } + + /* Write the new values */ + err = scarlett2_usb_set_data_buf(mixer, offset, size, count, buf); + if (err < 0) + return err; + + /* Activate the change */ + return scarlett2_usb_activate_config(mixer, config_item->activate); +} + /* Send SCARLETT2_USB_DATA_CMD SCARLETT2_USB_CONFIG_SAVE */ static void scarlett2_config_save(struct usb_mixer_interface *mixer) { @@ -2985,6 +3161,8 @@ static void scarlett2_autogain_update_access(struct usb_mixer_interface *mixer) scarlett2_set_ctl_access(private->input_mute_ctls[i], val); for (i = 0; i < info->phantom_count; i++) scarlett2_set_ctl_access(private->phantom_ctls[i], val); + for (i = 0; i < info->dsp_input_count; i++) + scarlett2_set_ctl_access(private->dsp_ctls[i], val); } /* Notify of access mode change for all controls read-only while @@ -3018,6 +3196,9 @@ static void scarlett2_autogain_notify_access(struct usb_mixer_interface *mixer) for (i = 0; i < info->air_input_count; i++) snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, &private->air_ctls[i]->id); + for (i = 0; i < info->dsp_input_count; i++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, + &private->dsp_ctls[i]->id); for (i = 0; i < info->mute_input_count; i++) snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, &private->input_mute_ctls[i]->id); @@ -4635,6 +4816,576 @@ static const struct snd_kcontrol_new scarlett2_air_ctl[2] = { } }; +/*** DSP Switch Control ***/ + +static int scarlett2_update_input_dsp(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + + private->input_dsp_updated = 0; + + if (!info->dsp_input_count) + return 0; + + return scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_DSP_SWITCH, + info->dsp_input_count, private->dsp_switch); +} + +static int scarlett2_dsp_ctl_get(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 err = 0; + + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + if (private->input_dsp_updated) { + err = scarlett2_update_input_dsp(mixer); + if (err < 0) + goto unlock; + } + ucontrol->value.integer.value[0] = private->dsp_switch[elem->control]; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static int scarlett2_dsp_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 index = elem->control; + int oval, val, err; + + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + err = scarlett2_check_put_during_autogain(mixer); + if (err < 0) + goto unlock; + + oval = private->dsp_switch[index]; + val = ucontrol->value.integer.value[0]; + + if (oval == val) + goto unlock; + + private->dsp_switch[index] = val; + + /* Send switch change to the device */ + err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_DSP_SWITCH, + index, val); + if (err == 0) + err = 1; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static const struct snd_kcontrol_new scarlett2_dsp_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = scarlett2_autogain_disables_ctl_info, + .get = scarlett2_dsp_ctl_get, + .put = scarlett2_dsp_ctl_put, +}; + +/*** DSP Compressor Parameter Controls ***/ + +static int scarlett2_update_compressor_values(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + int err, i, j; + + if (!info->dsp_input_count) + return 0; + + err = scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_COMPRESSOR_PARAMS, + SCARLETT2_COMPRESSOR_PARAM_COUNT * info->dsp_input_count, + private->compressor_values); + + if (err < 0) + return err; + + for (i = 0; i < SCARLETT2_COMPRESSOR_PARAM_COUNT; i++) { + const struct compressor_param *param = &compressor_params[i]; + + for (j = 0; j < info->dsp_input_count; j++) { + int idx = i + j * SCARLETT2_COMPRESSOR_PARAM_COUNT; + int val = private->compressor_values[idx]; + + val >>= param->scale_bits; + val = clamp(val, param->min, param->max); + private->compressor_values[idx] = val; + } + } + + return 0; +} + +static int scarlett2_compressor_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->compressor_values[elem->control]; + return 0; +} + +static int scarlett2_compressor_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 index = elem->control; + int channel = index / SCARLETT2_COMPRESSOR_PARAM_COUNT; + int param_index = index % SCARLETT2_COMPRESSOR_PARAM_COUNT; + int oval, val, err; + s32 scaled_val; + + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + err = scarlett2_check_put_during_autogain(mixer); + if (err < 0) + goto unlock; + + oval = private->compressor_values[index]; + val = ucontrol->value.integer.value[0]; + if (oval == val) + goto unlock; + + private->compressor_values[index] = val; + + const struct compressor_param *param = &compressor_params[param_index]; + + scaled_val = val << param->scale_bits; + + /* Send change to the device */ + + /* The channel needs to be put in the parameter buffer index + * field (param_buf_addr + 1); the value field isn't used in + * this case. + */ + err = scarlett2_usb_set_data( + mixer, private->config_set->param_buf_addr + 1, 1, channel); + if (err < 0) + goto unlock; + + err = scarlett2_usb_set_config( + mixer, SCARLETT2_CONFIG_COMPRESSOR_PARAMS, index, scaled_val); + if (err < 0) + goto unlock; + + if (err == 0) + err = 1; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static int scarlett2_compressor_ctl_info( + struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + int control = elem->control % SCARLETT2_COMPRESSOR_PARAM_COUNT; + + uinfo->type = compressor_params[control].type; + uinfo->count = 1; + uinfo->value.integer.min = compressor_params[control].min; + uinfo->value.integer.max = compressor_params[control].max; + uinfo->value.integer.step = 1; + return 0; +} + +static const struct snd_kcontrol_new scarlett2_compressor_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .name = "", + .info = scarlett2_compressor_ctl_info, + .get = scarlett2_compressor_ctl_get, + .put = scarlett2_compressor_ctl_put, +}; + +/*** DSP Pre-Compressor and PEQ Filter Controls ***/ + +static int scarlett2_precomp_flt_switch_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->precomp_flt_switch[elem->control]; + + return 0; +} + +static int scarlett2_peq_flt_switch_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->peq_flt_switch[elem->control]; + + return 0; +} + +static int scarlett2_precomp_flt_switch_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; + + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + oval = private->precomp_flt_switch[elem->control]; + val = ucontrol->value.integer.value[0]; + + if (oval == val) + goto unlock; + + private->precomp_flt_switch[elem->control] = val; + + /* Send change to the device */ + err = scarlett2_usb_set_config( + mixer, SCARLETT2_CONFIG_PRECOMP_FLT_SWITCH, + elem->control, val); + if (err == 0) + err = 1; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static int scarlett2_peq_flt_switch_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; + + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + oval = private->peq_flt_switch[elem->control]; + val = ucontrol->value.integer.value[0]; + + if (oval == val) + goto unlock; + + private->peq_flt_switch[elem->control] = val; + + /* Send change to the device */ + err = scarlett2_usb_set_config( + mixer, SCARLETT2_CONFIG_PEQ_FLT_SWITCH, + elem->control, val); + if (err == 0) + err = 1; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static const struct snd_kcontrol_new scarlett2_precomp_flt_switch_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .name = "", + .info = snd_ctl_boolean_mono_info, + .get = scarlett2_precomp_flt_switch_ctl_get, + .put = scarlett2_precomp_flt_switch_ctl_put, +}; + +static const struct snd_kcontrol_new scarlett2_peq_flt_switch_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .name = "", + .info = snd_ctl_boolean_mono_info, + .get = scarlett2_peq_flt_switch_ctl_get, + .put = scarlett2_peq_flt_switch_ctl_put, +}; + +static int scarlett2_update_filter_values(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + int err, i, j, k, src_idx, dst_idx; + s32 peq_flt_values[SCARLETT2_DSP_SWITCH_MAX * + SCARLETT2_PEQ_FLT_SLOTS_MAX * + SCARLETT2_BIQUAD_COEFFS]; + + if (!info->dsp_input_count) + return 0; + + /* Get filter switch values */ + err = scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_PRECOMP_FLT_SWITCH, + info->dsp_input_count, private->precomp_flt_switch); + if (err < 0) + return err; + + err = scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_PEQ_FLT_SWITCH, + info->dsp_input_count * info->peq_flt_count, + private->peq_flt_switch); + if (err < 0) + return err; + + /* Get pre-compressor filter values directly */ + err = scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_PRECOMP_FLT_PARAMS, + info->dsp_input_count * + info->precomp_flt_count * + SCARLETT2_BIQUAD_COEFFS, + private->precomp_flt_values); + + if (err < 0) + return err; + + /* PEQ filter values need to be copied via buffer because of + * padding after peq_flt_count up to peq_flt_total_count + */ + err = scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_PEQ_FLT_PARAMS, + info->dsp_input_count * + info->peq_flt_total_count * + SCARLETT2_BIQUAD_COEFFS, + peq_flt_values); + + for (i = 0, dst_idx = 0; i < info->dsp_input_count; i++) { + src_idx = i * + info->peq_flt_total_count * + SCARLETT2_BIQUAD_COEFFS; + for (j = 0; j < info->peq_flt_count; j++) + for (k = 0; + k < SCARLETT2_BIQUAD_COEFFS; + k++, src_idx++, dst_idx++) + private->peq_flt_values[dst_idx] = + peq_flt_values[src_idx]; + } + + return 0; +} + +static int scarlett2_precomp_flt_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; + int i, idx; + + for (i = 0, idx = elem->control * SCARLETT2_BIQUAD_COEFFS; + i < SCARLETT2_BIQUAD_COEFFS; + i++, idx++) + ucontrol->value.integer.value[i] = + private->precomp_flt_values[idx]; + + return 0; +} + +static int scarlett2_peq_flt_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; + int i, idx; + + for (i = 0, idx = elem->control * SCARLETT2_BIQUAD_COEFFS; + i < SCARLETT2_BIQUAD_COEFFS; + i++, idx++) + ucontrol->value.integer.value[i] = + private->peq_flt_values[idx]; + + return 0; +} + +static int scarlett2_precomp_flt_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 index = elem->control * SCARLETT2_BIQUAD_COEFFS; + int i, oval, val, err; + + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + err = scarlett2_check_put_during_autogain(mixer); + if (err < 0) + goto unlock; + + /* Check if any of the values have changed; if not, return */ + for (i = 0; i < SCARLETT2_BIQUAD_COEFFS; i++) { + oval = private->precomp_flt_values[index + i]; + val = ucontrol->value.integer.value[i]; + if (oval != val) + break; + } + + if (i == SCARLETT2_BIQUAD_COEFFS) + goto unlock; + + /* Update the values */ + for (i = 0; i < SCARLETT2_BIQUAD_COEFFS; i++) + private->precomp_flt_values[index + i] = + ucontrol->value.integer.value[i]; + + /* Send change to the device */ + err = scarlett2_usb_set_data( + mixer, private->config_set->param_buf_addr, 1, index); + if (err < 0) + goto unlock; + + err = scarlett2_usb_set_config_buf( + mixer, SCARLETT2_CONFIG_PRECOMP_FLT_PARAMS, + index, SCARLETT2_BIQUAD_COEFFS, + &private->precomp_flt_values[index]); + + if (err == 0) + err = 1; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static int scarlett2_peq_flt_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; + const struct scarlett2_device_info *info = private->info; + + int src_index = elem->control * SCARLETT2_BIQUAD_COEFFS; + int dst_index = ( + elem->control / + info->peq_flt_count * + info->peq_flt_total_count + + elem->control % info->peq_flt_count + ) * SCARLETT2_BIQUAD_COEFFS; + int i, oval, val, err; + + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + err = scarlett2_check_put_during_autogain(mixer); + if (err < 0) + goto unlock; + + /* Check if any of the values have changed; if not, return */ + for (i = 0; i < SCARLETT2_BIQUAD_COEFFS; i++) { + oval = private->peq_flt_values[src_index + i]; + val = ucontrol->value.integer.value[i]; + if (oval != val) + break; + } + + if (i == SCARLETT2_BIQUAD_COEFFS) + goto unlock; + + /* Update the values */ + for (i = 0; i < SCARLETT2_BIQUAD_COEFFS; i++) + private->peq_flt_values[src_index + i] = + ucontrol->value.integer.value[i]; + + /* Send change to the device */ + err = scarlett2_usb_set_data( + mixer, private->config_set->param_buf_addr, 1, dst_index); + if (err < 0) + goto unlock; + + err = scarlett2_usb_set_config_buf( + mixer, SCARLETT2_CONFIG_PEQ_FLT_PARAMS, + dst_index, SCARLETT2_BIQUAD_COEFFS, + &private->peq_flt_values[src_index]); + + if (err == 0) + err = 1; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static int scarlett2_flt_ctl_info( + struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = SCARLETT2_BIQUAD_COEFFS; + uinfo->value.integer.min = INT_MIN; + uinfo->value.integer.max = INT_MAX; + uinfo->value.integer.step = 1; + return 0; +} + +static const struct snd_kcontrol_new scarlett2_precomp_flt_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .name = "", + .info = scarlett2_flt_ctl_info, + .get = scarlett2_precomp_flt_ctl_get, + .put = scarlett2_precomp_flt_ctl_put, +}; + +static const struct snd_kcontrol_new scarlett2_peq_flt_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .name = "", + .info = scarlett2_flt_ctl_info, + .get = scarlett2_peq_flt_ctl_get, + .put = scarlett2_peq_flt_ctl_put, +}; + /*** Input Mute Switch Controls ***/ static int scarlett2_update_input_mute(struct usb_mixer_interface *mixer) @@ -5568,6 +6319,69 @@ static int scarlett2_add_line_out_ctls(struct usb_mixer_interface *mixer) /*** Create the analogue input controls ***/ +static int scarlett2_add_dsp_ctls(struct usb_mixer_interface *mixer, int i) +{ + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + int j, err; + char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + const char *compr_fmt = "Line In %d Compressor %s"; + const char *flt_switch_fmt = "Line In %d %s Filter Enable"; + const char *flt_fmt = "Line In %d %s Coefficients %d"; + + /* Add compressor controls */ + for (j = 0; j < SCARLETT2_COMPRESSOR_PARAM_COUNT; j++) { + const struct compressor_param *param = &compressor_params[j]; + int idx = i * SCARLETT2_COMPRESSOR_PARAM_COUNT + j; + + scnprintf(s, sizeof(s), compr_fmt, i + 1, param->name); + err = scarlett2_add_new_ctl( + mixer, &scarlett2_compressor_ctl, + i * SCARLETT2_COMPRESSOR_PARAM_COUNT + j, + 1, s, &private->compressor_ctls[idx]); + if (err < 0) + return err; + } + + /* Add filter enable controls */ + scnprintf(s, sizeof(s), flt_switch_fmt, i + 1, "Pre-Comp"); + err = scarlett2_add_new_ctl( + mixer, &scarlett2_precomp_flt_switch_ctl, + i, 1, s, &private->precomp_flt_switch_ctls[i]); + if (err < 0) + return err; + + scnprintf(s, sizeof(s), flt_switch_fmt, i + 1, "PEQ"); + err = scarlett2_add_new_ctl( + mixer, &scarlett2_peq_flt_switch_ctl, + i, 1, s, &private->peq_flt_switch_ctls[i]); + if (err < 0) + return err; + + /* Add filter coefficient controls */ + for (j = 0; j < info->precomp_flt_count; j++) { + scnprintf(s, sizeof(s), flt_fmt, i + 1, "Pre-Comp", j + 1); + err = scarlett2_add_new_ctl( + mixer, &scarlett2_precomp_flt_ctl, + i * info->precomp_flt_count + j, + 1, s, &private->precomp_flt_switch_ctls[j]); + if (err < 0) + return err; + } + + for (j = 0; j < info->peq_flt_count; j++) { + scnprintf(s, sizeof(s), flt_fmt, i + 1, "PEQ", j + 1); + err = scarlett2_add_new_ctl( + mixer, &scarlett2_peq_flt_ctl, + i * info->peq_flt_count + j, + 1, s, &private->peq_flt_switch_ctls[j]); + if (err < 0) + return err; + } + + return 0; +} + static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer) { struct scarlett2_data *private = mixer->private_data; @@ -5607,6 +6421,19 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer) return err; } + /* Add input DSP controls */ + for (i = 0; i < info->dsp_input_count; i++) { + scnprintf(s, sizeof(s), fmt, i + 1, "DSP", "Switch"); + err = scarlett2_add_new_ctl(mixer, &scarlett2_dsp_ctl, + i, 1, s, &private->dsp_ctls[i]); + if (err < 0) + return err; + + err = scarlett2_add_dsp_ctls(mixer, i); + if (err < 0) + return err; + } + /* Add input mute controls */ for (i = 0; i < info->mute_input_count; i++) { scnprintf(s, sizeof(s), fmt, i + 1, "Mute", "Switch"); @@ -6678,6 +7505,22 @@ static void scarlett2_notify_input_air(struct usb_mixer_interface *mixer) &private->air_ctls[i]->id); } +/* Notify on input DSP switch change */ +static __always_unused void scarlett2_notify_input_dsp( + struct usb_mixer_interface *mixer) +{ + struct snd_card *card = mixer->chip->card; + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + int i; + + private->input_dsp_updated = 1; + + for (i = 0; i < info->dsp_input_count; i++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->dsp_ctls[i]->id); +} + /* Notify on input mute switch change */ static __always_unused void scarlett2_notify_input_mute( struct usb_mixer_interface *mixer) @@ -7307,6 +8150,18 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer) if (err < 0) return err; + err = scarlett2_update_input_dsp(mixer); + if (err < 0) + return err; + + err = scarlett2_update_compressor_values(mixer); + if (err < 0) + return err; + + err = scarlett2_update_filter_values(mixer); + if (err < 0) + return err; + err = scarlett2_update_input_mute(mixer); if (err < 0) return err; From patchwork Tue Mar 12 18:37:43 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: 13590484 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 A341E14373A for ; Tue, 12 Mar 2024 18:37:44 +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=1710268666; cv=none; b=GZt3Rlzv9xmgr4onAAdKhAe5FvQOjD5rdIqKkHS5ufJVtwYNjgjfAFtZ9Vqv6Bxm06CiBwm098Qwg7ncAz7X4DjIG/PbDF0fewRY9S4CfSbdWJ0wI25RDsDMy2fskh2E+YroSq7lQWDD4s2QwPFjco7EcJ74qeS92XhGBqJ1A3k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1710268666; c=relaxed/simple; bh=BnScXQhDyPT1n1ZNYU8iToh1zgvRk6GFSR89f7DSXQI=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=ecIQpbYUzHxdmDn0Lhq6Xceq2LHwCX3maudwEyurNo/wScI93bQkUTBVOsoppDNUi35AS5ROtB4On06EGaaOnV/rMuW6Vawvfi1r4MH97ez88qBIZCuA8inHPni9rzgOjr6c5YFI8PpZvBeOuK1smamOhJfVvq0blPNx37YGbm0= 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=VlKcv8f/; 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="VlKcv8f/" Received: by m.b4.vu (Postfix, from userid 1000) id 14D3F604B628; Wed, 13 Mar 2024 05:07:43 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu 14D3F604B628 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1710268663; bh=o010mHyoPtCuTmGTFIJxm1H/uPvo8A4jR0IbelKyY2M=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=VlKcv8f/DVvzRMd5IeKJf3TMKzkXx8HWSCCsqGJDcaAOff11IzItH0ukj6q8UM7V/ 22I5iSuxipCoP01v+O8/PtnjxedkqA6TjrhKeVYTGdfua9fjsU7c7YCBXoREzJAfMO eTNMmSFIzd4Da6cFJaMGTZskU2q4wVFaF4Kqeq/GYC/vTAc9XMos9ORPAvOWg4iS06 Y9aCfgn4Xu72awQrHNPSXAuNKi989hhv9+jMt/r9+96eHAuYFWb/j5yyvIO97nbsZ7 4LglYkXvq+tHssqLtGy/17TQzP+z6vkVqZJjRDRZutBxw2ONA70pMY6UmA539xBP7C SMFfijIqOgoCQ== Date: Wed, 13 Mar 2024 05:07:43 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , linux-sound@vger.kernel.org Subject: [PATCH 12/14] ALSA: scarlett2: Add support for Focusrite Vocaster One and Two Message-ID: <5fb48555a8db7bb322b25784b165829357cd6e42.1710264833.git.g@b4.vu> References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Add Focusrite Vocaster One and Two USB IDs, notification arrays, config sets, and device info data. Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_quirks.c | 2 + sound/usb/mixer_scarlett2.c | 167 +++++++++++++++++++++++++++++++++++- 2 files changed, 165 insertions(+), 4 deletions(-) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 065a4be0d771..212b5e6443d8 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -3447,6 +3447,8 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) case USB_ID(0x1235, 0x8213): /* Focusrite Scarlett 8i6 3rd Gen */ case USB_ID(0x1235, 0x8214): /* Focusrite Scarlett 18i8 3rd Gen */ case USB_ID(0x1235, 0x8215): /* Focusrite Scarlett 18i20 3rd Gen */ + case USB_ID(0x1235, 0x8216): /* Focusrite Vocaster One */ + case USB_ID(0x1235, 0x8217): /* Focusrite Vocaster Two */ case USB_ID(0x1235, 0x8218): /* Focusrite Scarlett Solo 4th Gen */ case USB_ID(0x1235, 0x8219): /* Focusrite Scarlett 2i2 4th Gen */ case USB_ID(0x1235, 0x821a): /* Focusrite Scarlett 4i4 4th Gen */ diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 77bfaadb99b8..43922e217503 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -363,6 +363,18 @@ static const char *const scarlett2_autogain_status_gen4[] = { NULL }; +static const char *const scarlett2_autogain_status_vocaster[] = { + "Running", + "Success", + "FailPG", + "FailRange", + "WarnMaxCap", + "WarnMinCap", + "Cancelled", + "Invalid", + NULL +}; + /* Power Status Values */ enum { SCARLETT2_POWER_STATUS_EXT, @@ -418,6 +430,17 @@ static const struct scarlett2_notification scarlett3a_notifications[] = { { 0, NULL } }; +static const struct scarlett2_notification vocaster_notifications[] = { + { 0x00000001, scarlett2_notify_ack }, + { 0x00000008, scarlett2_notify_sync }, + { 0x00200000, scarlett2_notify_input_mute }, + { 0x00400000, scarlett2_notify_autogain }, + { 0x04000000, scarlett2_notify_input_dsp }, + { 0x08000000, scarlett2_notify_input_gain }, + { 0x10000000, scarlett2_notify_input_phantom }, + { 0, NULL } +}; + static const struct scarlett2_notification scarlett4_solo_notifications[] = { { 0x00000001, scarlett2_notify_ack }, { 0x00000008, scarlett2_notify_sync }, @@ -531,6 +554,11 @@ struct scarlett2_config_set { }; /* Input gain TLV dB ranges */ + +static const DECLARE_TLV_DB_MINMAX( + db_scale_vocaster_gain, 0, 70 * 100 +); + static const DECLARE_TLV_DB_MINMAX( db_scale_gen4_gain, 0, 69 * 100 ); @@ -696,6 +724,51 @@ static const struct scarlett2_config_set scarlett2_config_set_gen3c = { } }; +/* Vocaster */ +static const struct scarlett2_config_set scarlett2_config_set_vocaster = { + .notifications = vocaster_notifications, + .param_buf_addr = 0x1bc, + .input_gain_tlv = db_scale_vocaster_gain, + .autogain_status_texts = scarlett2_autogain_status_vocaster, + .items = { + [SCARLETT2_CONFIG_MSD_SWITCH] = { + .offset = 0x9d, .size = 8, .activate = 6 }, + + [SCARLETT2_CONFIG_AUTOGAIN_SWITCH] = { + .offset = 0x1c0, .size = 8, .activate = 19, .pbuf = 1 }, + + [SCARLETT2_CONFIG_AUTOGAIN_STATUS] = { + .offset = 0x1c2, .size = 8, }, + + [SCARLETT2_CONFIG_INPUT_GAIN] = { + .offset = 0x9f, .size = 8, .activate = 21, .pbuf = 1 }, + + [SCARLETT2_CONFIG_PHANTOM_SWITCH] = { + .offset = 0x9c, .size = 1, .activate = 20, .pbuf = 1 }, + + [SCARLETT2_CONFIG_DSP_SWITCH] = { + .offset = 0x1c4, .size = 8, .activate = 22, .pbuf = 1 }, + + [SCARLETT2_CONFIG_COMPRESSOR_PARAMS] = { + .offset = 0x1c8, .size = 32, .activate = 23 }, + + [SCARLETT2_CONFIG_PRECOMP_FLT_SWITCH] = { + .offset = 0x7c, .size = 32, .activate = 27 }, + + [SCARLETT2_CONFIG_PRECOMP_FLT_PARAMS] = { + .offset = 0x200, .size = 32, .activate = 27 }, + + [SCARLETT2_CONFIG_PEQ_FLT_SWITCH] = { + .offset = 0x84, .size = 32, .activate = 27 }, + + [SCARLETT2_CONFIG_PEQ_FLT_PARAMS] = { + .offset = 0x250, .size = 32, .activate = 27 }, + + [SCARLETT2_CONFIG_INPUT_MUTE_SWITCH] = { + .offset = 0x1be, .size = 8, .activate = 17, .pbuf = 1 }, + } +}; + /* Solo Gen 4 */ static const struct scarlett2_config_set scarlett2_config_set_gen4_solo = { .notifications = scarlett4_solo_notifications, @@ -1599,6 +1672,90 @@ static const struct scarlett2_device_info s18i20_gen3_info = { } }; +static const struct scarlett2_device_info vocaster_one_info = { + .config_set = &scarlett2_config_set_vocaster, + .min_firmware_version = 1769, + + .phantom_count = 1, + .inputs_per_phantom = 1, + .dsp_count = 1, + .dsp_input_count = 1, + .precomp_flt_count = 2, + .peq_flt_count = 3, + .peq_flt_total_count = 4, + .mute_input_count = 1, + .gain_input_count = 1, + + .port_count = { + [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, + [SCARLETT2_PORT_TYPE_ANALOGUE] = { 2, 4 }, + [SCARLETT2_PORT_TYPE_MIX] = { 9, 9 }, + [SCARLETT2_PORT_TYPE_PCM] = { 4, 10 }, + }, + + .mux_assignment = { { + { SCARLETT2_PORT_TYPE_MIX, 8, 1 }, + { SCARLETT2_PORT_TYPE_PCM, 5, 5 }, + { SCARLETT2_PORT_TYPE_MIX, 6, 2 }, + { SCARLETT2_PORT_TYPE_PCM, 0, 5 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 6 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 }, + { 0, 0, 0 }, + } }, + + .meter_map = { + { 12, 1 }, + { 18, 5 }, + { 10, 2 }, + { 13, 5 }, + { 4, 6 }, + { 0, 4 }, + { 0, 0 } + } +}; + +static const struct scarlett2_device_info vocaster_two_info = { + .config_set = &scarlett2_config_set_vocaster, + .min_firmware_version = 1769, + + .phantom_count = 2, + .inputs_per_phantom = 1, + .dsp_count = 2, + .dsp_input_count = 2, + .precomp_flt_count = 2, + .peq_flt_count = 3, + .peq_flt_total_count = 4, + .mute_input_count = 2, + .gain_input_count = 2, + + .port_count = { + [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, + [SCARLETT2_PORT_TYPE_ANALOGUE] = { 6, 6 }, + [SCARLETT2_PORT_TYPE_MIX] = { 12, 14 }, + [SCARLETT2_PORT_TYPE_PCM] = { 4, 14 }, + }, + + .mux_assignment = { { + { SCARLETT2_PORT_TYPE_MIX, 12, 2 }, + { SCARLETT2_PORT_TYPE_PCM, 6, 8 }, + { SCARLETT2_PORT_TYPE_MIX, 10, 2 }, + { SCARLETT2_PORT_TYPE_PCM, 0, 6 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 10 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 6 }, + { 0, 0, 0 }, + } }, + + .meter_map = { + { 18, 2 }, + { 26, 8 }, + { 16, 2 }, + { 20, 6 }, + { 6, 10 }, + { 0, 6 }, + { 0, 0 } + } +}; + static const struct scarlett2_device_info solo_gen4_info = { .config_set = &scarlett2_config_set_gen4_solo, .min_firmware_version = 2115, @@ -1932,6 +2089,10 @@ static const struct scarlett2_device_entry scarlett2_devices[] = { { USB_ID(0x1235, 0x8214), &s18i8_gen3_info, "Scarlett Gen 3" }, { USB_ID(0x1235, 0x8215), &s18i20_gen3_info, "Scarlett Gen 3" }, + /* Supported Vocaster devices */ + { USB_ID(0x1235, 0x8216), &vocaster_one_info, "Vocaster" }, + { USB_ID(0x1235, 0x8217), &vocaster_two_info, "Vocaster" }, + /* Supported Gen 4 devices */ { USB_ID(0x1235, 0x8218), &solo_gen4_info, "Scarlett Gen 4" }, { USB_ID(0x1235, 0x8219), &s2i2_gen4_info, "Scarlett Gen 4" }, @@ -7506,8 +7667,7 @@ static void scarlett2_notify_input_air(struct usb_mixer_interface *mixer) } /* Notify on input DSP switch change */ -static __always_unused void scarlett2_notify_input_dsp( - struct usb_mixer_interface *mixer) +static void scarlett2_notify_input_dsp(struct usb_mixer_interface *mixer) { struct snd_card *card = mixer->chip->card; struct scarlett2_data *private = mixer->private_data; @@ -7522,8 +7682,7 @@ static __always_unused void scarlett2_notify_input_dsp( } /* Notify on input mute switch change */ -static __always_unused void scarlett2_notify_input_mute( - struct usb_mixer_interface *mixer) +static void scarlett2_notify_input_mute(struct usb_mixer_interface *mixer) { struct snd_card *card = mixer->chip->card; struct scarlett2_data *private = mixer->private_data; From patchwork Tue Mar 12 18:37:56 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: 13590485 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 C88EB14373A for ; Tue, 12 Mar 2024 18:37:57 +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=1710268680; cv=none; b=o4tYu052uvRe/2Y1BIoLyp/VsszqR1MNdRdh5cJzJ3afjXneiQWdXUJmo7OJp+JmU561dQDH0b3mV+hnAlcMfROqfz5UuhXMlBrMOheamrtWT9TxbkdT4y8JOEEif7qesLqIoz/dt0X1GjNtOLFSuywzyHUZ4CsEW2GgnXX63oE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1710268680; c=relaxed/simple; bh=pwjkm2fxqMuTHCLrxR7l1/lBE22syQLoOuPAyaWHRGI=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=INN2mQKAF7Uqoj7NA251yc07F1UFMwt9dQnvn33yYZpqtDOvLOt0BanEoNXjS5jtA7bj0RkURFRK4zUeeMy5ZYs1PA/HZyZw3Gai7TX6oc9g9zuWT3jGoMMm5PL7UjqhEp4IXLF+iVymFLeuv55gcpzvkfG0qEjqchhm1XPgH4A= 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=OpO1ywxU; 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="OpO1ywxU" Received: by m.b4.vu (Postfix, from userid 1000) id 3DC66604B628; Wed, 13 Mar 2024 05:07:56 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu 3DC66604B628 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1710268676; bh=6lAeo1OR1rAhyXqrtJCCpY47RmpKVIaWH0kntUnU4iQ=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=OpO1ywxUhrgSC3bvc95y8bH2pRyMeDVDpCW4gwWOqBTcQECnz4vxDDs9TUwwhEC3h GVbX43AxhOQgVA/D73hW06w88IjzMenSX7qScuniJDdRaIE+LwRrouUqoK+wWhPp4S Vq5jvB7+NIXv/ETmiM/dWGV2GKgXlGlZPkt6Tpoxpgfk9rdHpA2F1ZEvQMfd3CIKlw 4fXzVbH//cy6O5GrWYJHIplrdQKpzyVi0v5zUq5gPSL29dQK4f0ppAPdB3YSEPHit4 eI5sueXNRhuTyGP9g5/HU+j99fPZXRNAX4jGyL6ZQHrVzt3In/VyFKyqGYcExcdWW4 Ua0D00EzUAnvw== Date: Wed, 13 Mar 2024 05:07:56 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , linux-sound@vger.kernel.org Subject: [PATCH 13/14] ALSA: scarlett2: Add autogain target controls Message-ID: <33d7f6dc965ab09522361ec99745a0685e4b8272.1710264833.git.g@b4.vu> References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: The Scarlett 4th Gen and Vocaster interfaces allow the autogain target dBFS value(s) to be configured. Add Mean and Peak Target controls for 4th Gen, and a Hot Target control for Vocaster. Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 207 ++++++++++++++++++++++++++++++++++++ 1 file changed, 207 insertions(+) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 43922e217503..6040c4df356f 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -332,6 +332,17 @@ enum { SCARLETT2_DIM_MUTE_COUNT }; +/* Autogain target values */ + +#define SCARLETT2_AG_TARGET_MIN (-30) + +enum { + SCARLETT2_AG_HOT_TARGET, + SCARLETT2_AG_MEAN_TARGET, + SCARLETT2_AG_PEAK_TARGET, + SCARLETT2_AG_TARGET_COUNT +}; + /* Flash Write State */ enum { SCARLETT2_FLASH_WRITE_STATE_IDLE, @@ -512,6 +523,9 @@ enum { SCARLETT2_CONFIG_TALKBACK_MAP, SCARLETT2_CONFIG_AUTOGAIN_SWITCH, SCARLETT2_CONFIG_AUTOGAIN_STATUS, + SCARLETT2_CONFIG_AG_HOT_TARGET, + SCARLETT2_CONFIG_AG_MEAN_TARGET, + SCARLETT2_CONFIG_AG_PEAK_TARGET, SCARLETT2_CONFIG_INPUT_GAIN, SCARLETT2_CONFIG_SAFE_SWITCH, SCARLETT2_CONFIG_INPUT_SELECT_SWITCH, @@ -523,6 +537,18 @@ enum { SCARLETT2_CONFIG_COUNT }; +/* Autogain target configuration parameters and names */ + +static const int scarlett2_ag_target_configs[] = { + [SCARLETT2_AG_HOT_TARGET] = SCARLETT2_CONFIG_AG_HOT_TARGET, + [SCARLETT2_AG_MEAN_TARGET] = SCARLETT2_CONFIG_AG_MEAN_TARGET, + [SCARLETT2_AG_PEAK_TARGET] = SCARLETT2_CONFIG_AG_PEAK_TARGET +}; + +static const char *const scarlett2_ag_target_names[] = { + "Hot", "Mean", "Peak" +}; + /* Location, size, and activation command number for the configuration * parameters. Size is in bits and may be 1, 8, 16, or 32. * @@ -740,6 +766,9 @@ static const struct scarlett2_config_set scarlett2_config_set_vocaster = { [SCARLETT2_CONFIG_AUTOGAIN_STATUS] = { .offset = 0x1c2, .size = 8, }, + [SCARLETT2_CONFIG_AG_HOT_TARGET] = { + .offset = 0xc1, .size = 8, .activate = 29, .pbuf = 1 }, + [SCARLETT2_CONFIG_INPUT_GAIN] = { .offset = 0x9f, .size = 8, .activate = 21, .pbuf = 1 }, @@ -818,6 +847,12 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_2i2 = { [SCARLETT2_CONFIG_AUTOGAIN_STATUS] = { .offset = 0x137, .size = 8 }, + [SCARLETT2_CONFIG_AG_MEAN_TARGET] = { + .offset = 0x131, .size = 8, .activate = 29, .pbuf = 1 }, + + [SCARLETT2_CONFIG_AG_PEAK_TARGET] = { + .offset = 0x132, .size = 8, .activate = 30, .pbuf = 1 }, + [SCARLETT2_CONFIG_PHANTOM_SWITCH] = { .offset = 0x48, .size = 8, .activate = 11, .pbuf = 1, .mute = 1 }, @@ -862,6 +897,12 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_4i4 = { [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 }, @@ -1189,6 +1230,7 @@ struct scarlett2_data { u8 gain[SCARLETT2_INPUT_GAIN_MAX]; u8 autogain_switch[SCARLETT2_INPUT_GAIN_MAX]; u8 autogain_status[SCARLETT2_INPUT_GAIN_MAX]; + s8 ag_targets[SCARLETT2_AG_TARGET_COUNT]; u8 safe_switch[SCARLETT2_INPUT_GAIN_MAX]; u8 pcm_input_switch; u8 direct_monitor_switch; @@ -1217,6 +1259,7 @@ struct scarlett2_data { struct snd_kcontrol *input_gain_ctls[SCARLETT2_INPUT_GAIN_MAX]; struct snd_kcontrol *autogain_ctls[SCARLETT2_INPUT_GAIN_MAX]; struct snd_kcontrol *autogain_status_ctls[SCARLETT2_INPUT_GAIN_MAX]; + struct snd_kcontrol *ag_target_ctls[SCARLETT2_AG_TARGET_COUNT]; struct snd_kcontrol *safe_ctls[SCARLETT2_INPUT_GAIN_MAX]; struct snd_kcontrol *pcm_input_switch_ctl; struct snd_kcontrol *mux_ctls[SCARLETT2_MUX_MAX]; @@ -3253,6 +3296,7 @@ static int scarlett2_update_autogain(struct usb_mixer_interface *mixer) const struct scarlett2_device_info *info = private->info; int err, i; u8 raw_autogain_status[SCARLETT2_INPUT_GAIN_MAX]; + s8 ag_target_values[SCARLETT2_AG_TARGET_COUNT]; private->autogain_updated = 0; @@ -3291,6 +3335,21 @@ static int scarlett2_update_autogain(struct usb_mixer_interface *mixer) private->autogain_status[i] = private->num_autogain_status_texts - 1; + + for (int i = 0; i < SCARLETT2_AG_TARGET_COUNT; i++) + if (scarlett2_has_config_item(private, + scarlett2_ag_target_configs[i])) { + err = scarlett2_usb_get_config( + mixer, scarlett2_ag_target_configs[i], + 1, &ag_target_values[i]); + if (err < 0) + return err; + } + + /* convert from negative dBFS as used by the device */ + for (int i = 0; i < SCARLETT2_AG_TARGET_COUNT; i++) + private->ag_targets[i] = -ag_target_values[i]; + return 0; } @@ -3324,6 +3383,12 @@ static void scarlett2_autogain_update_access(struct usb_mixer_interface *mixer) scarlett2_set_ctl_access(private->phantom_ctls[i], val); for (i = 0; i < info->dsp_input_count; i++) scarlett2_set_ctl_access(private->dsp_ctls[i], val); + + for (i = 0; i < SCARLETT2_AG_TARGET_COUNT; i++) + if (scarlett2_has_config_item(private, + scarlett2_ag_target_configs[i])) + scarlett2_set_ctl_access( + private->ag_target_ctls[i], val); } /* Notify of access mode change for all controls read-only while @@ -3366,6 +3431,12 @@ static void scarlett2_autogain_notify_access(struct usb_mixer_interface *mixer) for (i = 0; i < info->phantom_count; i++) snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, &private->phantom_ctls[i]->id); + + for (i = 0; i < SCARLETT2_AG_TARGET_COUNT; i++) + if (scarlett2_has_config_item(private, + scarlett2_ag_target_configs[i])) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, + &private->ag_target_ctls[i]->id); } /* Call scarlett2_update_autogain() and @@ -3559,6 +3630,122 @@ static const struct snd_kcontrol_new scarlett2_autogain_status_ctl = { .get = scarlett2_autogain_status_ctl_get, }; +/*** Autogain Target Controls ***/ + +static int scarlett2_ag_target_ctl_info( + struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) +{ + 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 err; + + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + err = scarlett2_check_autogain_updated(mixer); + if (err < 0) + goto unlock; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = SCARLETT2_AG_TARGET_MIN; + uinfo->value.integer.max = 0; + uinfo->value.integer.step = 1; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static int scarlett2_ag_target_ctl_get( + 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 err; + + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + if (private->autogain_updated) { + err = scarlett2_update_autogain(mixer); + if (err < 0) + goto unlock; + } + + ucontrol->value.integer.value[0] = private->ag_targets[elem->control]; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static int scarlett2_ag_target_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 index = elem->control; + int oval, val, err; + + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + err = scarlett2_check_put_during_autogain(mixer); + if (err < 0) + goto unlock; + + oval = private->ag_targets[index]; + val = clamp(ucontrol->value.integer.value[0], + (long)SCARLETT2_AG_TARGET_MIN, 0L); + + if (oval == val) + goto unlock; + + private->ag_targets[index] = val; + + /* Send new value to the device */ + err = scarlett2_usb_set_config( + mixer, scarlett2_ag_target_configs[index], 1, -val); + if (err == 0) + err = 1; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static const DECLARE_TLV_DB_MINMAX( + db_scale_ag_target, SCARLETT2_AG_TARGET_MIN * 100, 0 +); + +static const struct snd_kcontrol_new scarlett2_ag_target_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .name = "", + .info = scarlett2_ag_target_ctl_info, + .get = scarlett2_ag_target_ctl_get, + .put = scarlett2_ag_target_ctl_put, + .tlv = { .p = db_scale_ag_target } +}; + /*** Input Select Control ***/ static int scarlett2_update_input_select(struct usb_mixer_interface *mixer) @@ -6693,6 +6880,20 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer) i, 1, s, &private->autogain_status_ctls[i]); } + /* Add autogain target controls */ + for (i = 0; i < SCARLETT2_AG_TARGET_COUNT; i++) + if (scarlett2_has_config_item(private, + scarlett2_ag_target_configs[i])) { + + scnprintf(s, sizeof(s), "Autogain %s Target", + scarlett2_ag_target_names[i]); + err = scarlett2_add_new_ctl( + mixer, &scarlett2_ag_target_ctl, + i, 1, s, &private->ag_target_ctls[i]); + if (err < 0) + return err; + } + /* Add safe-mode input switch controls */ for (i = 0; i < info->safe_input_count; i++) { scnprintf(s, sizeof(s), fmt, i + 1, @@ -7783,6 +7984,12 @@ static void scarlett2_notify_autogain(struct usb_mixer_interface *mixer) &private->autogain_status_ctls[i]->id); } + for (i = 0; i < SCARLETT2_AG_TARGET_COUNT; i++) + if (scarlett2_has_config_item(private, + scarlett2_ag_target_configs[i])) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, + &private->ag_target_ctls[i]->id); + scarlett2_autogain_notify_access(mixer); } From patchwork Tue Mar 12 18:38:10 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: 13590486 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 45F3A1428F8 for ; Tue, 12 Mar 2024 18:38:12 +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=1710268694; cv=none; b=MjhgANduWfKSPe7PXlEKuyIb/9WSqHnNj3VIE/lHzY7eLAllhYhRNPrJeSG+B5gC2SWivNsuCCWqTClKlYhBBQ3wa5HTNdr3fbBxTpmnX9V89yt9eqOHQrhiH7mDXgjhznlUBHgtgwJggXqUrBRDnaQEtTnR7oeTdkMxTHLq+SI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1710268694; c=relaxed/simple; bh=+gpx7+5nB8u0P8n/Z7JeZENLRyJDItL45dl2mGgjge0=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=mK0AOHvIo9geFrK5kRQqaCQxEX9M9KFwbL1nE4SE8MiTGcgQwO6MNQiaXLUZO9DAQW9yTnU7nGj1fUqbjP2MzJEvSVGhjvjuKCsN583gTKCqSXXhag+rj/Eu3YN1TtUGeAJ3kDlBhRRc1+X7VzIyLhyiRtkr/LEOC1I7/1Rd/es= 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=Mnv2FLbK; 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="Mnv2FLbK" Received: by m.b4.vu (Postfix, from userid 1000) id 82CAE604B628; Wed, 13 Mar 2024 05:08:10 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu 82CAE604B628 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1710268690; bh=OY9n+HYxA9NhpubmAwZfRRexPGDn6CXkSDTpHAZqZj8=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=Mnv2FLbKk1O2j7bPafilBZ5pkIXJ66xQZ8TVE4NZYbGkiQiY4Tdo2lxWwpeaEM+XP lyC/2DJ0bMbMVdVGFIrVNBrCO4o5mP4gLpUJH7FLnCSK4XWjFnpjn2oYPo78r1R6aw 7QvTRcSIJZwMPVX1XzFE0W+/f2sQ3gD3ts5NyNfsSxv+Nj45dSaUPIg9eo3vEsGf+l mdyg5wLrz8tx8IEhc52mttJ793pNWUqmjNKTbHeN8blxrPh3gVCPRZ3cZv0NhKylwQ 1CAJ0wFYl/vrYOB4vP4GQsE6H/BlBNehM/hMNc1k3wgmC/9rwMdpVynKGE30FX7G3V Z01DD/QGB3W1A== Date: Wed, 13 Mar 2024 05:08:10 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , linux-sound@vger.kernel.org Subject: [PATCH 14/14] ALSA: scarlett2: Add Bluetooth volume control for Vocaster Two Message-ID: References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: The Vocaster Two has a Bluetooth module with a volume control. Add a corresponding ALSA mixer control. Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 156 ++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 6040c4df356f..a2de31a0371b 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -88,6 +88,7 @@ * - input mute, gain, autogain, safe mode * - direct monitor mixes * - compressor and EQ + * - Bluetooth volume * * * /--------------\ 18chn 20chn /--------------\ @@ -180,6 +181,9 @@ */ #define SCARLETT2_MAX_GAIN_VALUE 70 +/* maximum Bluetooth volume value */ +#define SCARLETT2_MAX_BLUETOOTH_VOLUME 30 + /* mixer range from -80dB to +6dB in 0.5dB steps */ #define SCARLETT2_MIXER_MIN_DB -80 #define SCARLETT2_MIXER_BIAS (-SCARLETT2_MIXER_MIN_DB * 2) @@ -421,6 +425,7 @@ static void scarlett2_notify_direct_monitor(struct usb_mixer_interface *mixer); static void scarlett2_notify_power_status(struct usb_mixer_interface *mixer); static void scarlett2_notify_pcm_input_switch( struct usb_mixer_interface *mixer); +static void scarlett2_notify_bluetooth(struct usb_mixer_interface *mixer); /* Arrays of notification callback functions */ @@ -449,6 +454,7 @@ static const struct scarlett2_notification vocaster_notifications[] = { { 0x04000000, scarlett2_notify_input_dsp }, { 0x08000000, scarlett2_notify_input_gain }, { 0x10000000, scarlett2_notify_input_phantom }, + { 0x20000000, scarlett2_notify_bluetooth }, { 0, NULL } }; @@ -534,6 +540,7 @@ enum { SCARLETT2_CONFIG_POWER_LOW, SCARLETT2_CONFIG_PCM_INPUT_SWITCH, SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN, + SCARLETT2_CONFIG_BLUETOOTH_VOLUME, SCARLETT2_CONFIG_COUNT }; @@ -795,6 +802,9 @@ static const struct scarlett2_config_set scarlett2_config_set_vocaster = { [SCARLETT2_CONFIG_INPUT_MUTE_SWITCH] = { .offset = 0x1be, .size = 8, .activate = 17, .pbuf = 1 }, + + [SCARLETT2_CONFIG_BLUETOOTH_VOLUME] = { + .offset = 0xbf, .size = 8, .activate = 28 }, } }; @@ -1134,6 +1144,9 @@ struct scarlett2_device_info { /* the number of DSP channels */ u8 dsp_count; + /* has a Bluetooth module with volume control */ + u8 has_bluetooth; + /* remap analogue outputs; 18i8 Gen 3 has "line 3/4" connected * internally to the analogue 7/8 outputs */ @@ -1206,6 +1219,7 @@ struct scarlett2_data { u8 mix_updated; u8 speaker_switching_switched; u8 power_status_updated; + u8 bluetooth_updated; u8 sync; u8 master_vol; u8 headphone_vol; @@ -1240,6 +1254,7 @@ struct scarlett2_data { u8 msd_switch; u8 standalone_switch; u8 power_status; + u8 bluetooth_volume; u8 meter_level_map[SCARLETT2_MAX_METERS]; struct snd_kcontrol *sync_ctl; struct snd_kcontrol *master_vol_ctl; @@ -1273,6 +1288,7 @@ struct scarlett2_data { struct snd_kcontrol *speaker_switching_ctl; struct snd_kcontrol *talkback_ctl; struct snd_kcontrol *power_status_ctl; + struct snd_kcontrol *bluetooth_volume_ctl; u8 mux[SCARLETT2_MUX_MAX]; u8 mix[SCARLETT2_MIX_MAX]; u8 monitor_mix[SCARLETT2_MONITOR_MIX_MAX]; @@ -1770,6 +1786,7 @@ static const struct scarlett2_device_info vocaster_two_info = { .peq_flt_total_count = 4, .mute_input_count = 2, .gain_input_count = 2, + .has_bluetooth = 1, .port_count = { [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, @@ -7753,6 +7770,121 @@ static int scarlett2_add_power_status_ctl(struct usb_mixer_interface *mixer) &private->power_status_ctl); } +/*** Bluetooth Volume ***/ + +static int scarlett2_update_bluetooth_volume(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + int err; + + private->bluetooth_updated = 0; + + if (!private->info->has_bluetooth) + return 0; + + err = scarlett2_usb_get_config(mixer, + SCARLETT2_CONFIG_BLUETOOTH_VOLUME, + 1, &private->bluetooth_volume); + if (err < 0) + return err; + + return 0; +} + +static int scarlett2_bluetooth_volume_ctl_get(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 err = 0; + + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + if (private->bluetooth_updated) { + err = scarlett2_update_bluetooth_volume(mixer); + if (err < 0) + goto unlock; + } + ucontrol->value.integer.value[0] = private->bluetooth_volume; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static int scarlett2_bluetooth_volume_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; + + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + oval = private->bluetooth_volume; + val = clamp(ucontrol->value.integer.value[0], + 0L, (long)SCARLETT2_MAX_BLUETOOTH_VOLUME); + + if (oval == val) + goto unlock; + + private->bluetooth_volume = val; + err = scarlett2_usb_set_config(mixer, + SCARLETT2_CONFIG_BLUETOOTH_VOLUME, + 0, val); + if (err == 0) + err = 1; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static int scarlett2_bluetooth_volume_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_BLUETOOTH_VOLUME; + uinfo->value.integer.step = 1; + return 0; +} + +static const struct snd_kcontrol_new scarlett2_bluetooth_volume_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = scarlett2_bluetooth_volume_ctl_info, + .get = scarlett2_bluetooth_volume_ctl_get, + .put = scarlett2_bluetooth_volume_ctl_put, +}; + +static int scarlett2_add_bluetooth_volume_ctl( + struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + + if (!private->info->has_bluetooth) + return 0; + + /* Add Bluetooth volume control */ + return scarlett2_add_new_ctl(mixer, &scarlett2_bluetooth_volume_ctl, + 0, 1, "Bluetooth Capture Volume", + &private->bluetooth_volume_ctl); +} + /*** Notification Handlers ***/ /* Notify on sync change */ @@ -8109,6 +8241,21 @@ static void scarlett2_notify_pcm_input_switch(struct usb_mixer_interface *mixer) scarlett2_notify_mux(mixer); } +/* Notify on Bluetooth change */ +static void scarlett2_notify_bluetooth(struct usb_mixer_interface *mixer) +{ + struct snd_card *card = mixer->chip->card; + struct scarlett2_data *private = mixer->private_data; + + if (!private->info->has_bluetooth) + return; + + private->bluetooth_updated = 1; + + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->bluetooth_volume_ctl->id); +} + /* Handle acknowledgement that a command was received; let * scarlett2_usb() know that it can proceed */ @@ -8646,6 +8793,10 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer) return err; } + err = scarlett2_update_bluetooth_volume(mixer); + if (err < 0) + return err; + err = scarlett2_update_mix(mixer); if (err < 0) return err; @@ -8773,6 +8924,11 @@ static int snd_scarlett2_controls_create( if (err < 0) return err; + /* Create the Bluetooth volume control */ + err = scarlett2_add_bluetooth_volume_ctl(mixer); + if (err < 0) + return err; + /* Set the access mode of controls disabled during * autogain/phantom power switching. */