From patchwork Tue Sep 24 13:31:56 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roxana Nicolescu X-Patchwork-Id: 1988971 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.ubuntu.com (client-ip=185.125.189.65; helo=lists.ubuntu.com; envelope-from=kernel-team-bounces@lists.ubuntu.com; receiver=patchwork.ozlabs.org) Received: from lists.ubuntu.com (lists.ubuntu.com [185.125.189.65]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4XCglF2hftz1xsw for ; Tue, 24 Sep 2024 23:32:25 +1000 (AEST) Received: from localhost ([127.0.0.1] helo=lists.ubuntu.com) by lists.ubuntu.com with esmtp (Exim 4.86_2) (envelope-from ) id 1st5eJ-0003nl-RJ; Tue, 24 Sep 2024 13:32:15 +0000 Received: from smtp-relay-internal-1.internal ([10.131.114.114] helo=smtp-relay-internal-1.canonical.com) by lists.ubuntu.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1st5eF-0003kL-Mv for kernel-team@lists.ubuntu.com; Tue, 24 Sep 2024 13:32:11 +0000 Received: from mail-pf1-f200.google.com (mail-pf1-f200.google.com [209.85.210.200]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-1.canonical.com (Postfix) with ESMTPS id 17CE13F215 for ; Tue, 24 Sep 2024 13:32:11 +0000 (UTC) Received: by mail-pf1-f200.google.com with SMTP id d2e1a72fcca58-718db8e61bfso8326719b3a.0 for ; Tue, 24 Sep 2024 06:32:11 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1727184729; x=1727789529; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=uqP/JDh97qTX92f47PFPxDtke7tRPe40hwb3IWYa0qc=; b=hMl7BQmtexpJULaSp64mOedui8Uy9b7H2i1gtJ7vdhgG3njcCmFTDjrxKM4D46swh/ iCFXS6QtGl708ezVqEai/2PuWJKrAfyEnXb+UTWwLYkiys8AFSNNq2aFgzTQCAqwUYMO F7n6CHNfMk1AR38yleGxdHCNGYjzJ2u3toTFEgukSQmAdWEVvC9tg6IUR7Go5DlCzEab pyVZD1koB0lJhutN/KmTkd1V2O/AI2xgnODwbbXgvLEMggjrq5VVeMLp7AwjSQxv5FPc dEzmvaULbZ1SpqhBMN40KK7cQOLYEX3t7nuh7OaHgd5+JSuVjF3ArlQGHrWOXaU6cwML PlZA== X-Gm-Message-State: AOJu0Yx6OFPIMcb2MCldClHu1Pqnhn6zxI+1hxveks4xl3RllEnVGi2S mVGLOVrvnKpIwkxt77J8xpgnuk9F56iGYqn/1MXoObKEeL33bokTOroFPQOVx+Es/Al3f6ySBIa Z/tT0zjhDmOPYbU/WUHy1MXAqx2jZR9mzZNfUdp/uOnpL31jTXuYwtITCZ0tf95xpvu715eEgYF d02n8II7NsswubjUw= X-Received: by 2002:a05:6a00:2314:b0:70b:176e:b3bc with SMTP id d2e1a72fcca58-7199ce48b01mr22118396b3a.28.1727184728750; Tue, 24 Sep 2024 06:32:08 -0700 (PDT) X-Google-Smtp-Source: AGHT+IHTlBqxEqNLRuzJSK+EXLfwlGNX7H6rbN5s0yDO8pe2rY/kK6liehdn3sVHOVC/hQTU5AtiRw== X-Received: by 2002:a05:6a00:2314:b0:70b:176e:b3bc with SMTP id d2e1a72fcca58-7199ce48b01mr22118291b3a.28.1727184727574; Tue, 24 Sep 2024 06:32:07 -0700 (PDT) Received: from work.lan ([2001:67c:1560:8007::aac:c490]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-71afc85bbbasm1224965b3a.95.2024.09.24.06.32.05 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 24 Sep 2024 06:32:07 -0700 (PDT) From: Roxana Nicolescu To: kernel-team@lists.ubuntu.com Subject: [SRU][N][PATCH 1/2] ALSA: scarlett2: Move initialisation code lower in the source Date: Tue, 24 Sep 2024 15:31:56 +0200 Message-Id: <20240924133157.15168-2-roxana.nicolescu@canonical.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240924133157.15168-1-roxana.nicolescu@canonical.com> References: <20240924133157.15168-1-roxana.nicolescu@canonical.com> MIME-Version: 1.0 X-BeenThere: kernel-team@lists.ubuntu.com X-Mailman-Version: 2.1.20 Precedence: list List-Id: Kernel team discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: kernel-team-bounces@lists.ubuntu.com Sender: "kernel-team" From: "Geoffrey D. Bennett" BugLink: https://bugs.launchpad.net/bugs/2076402 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 Signed-off-by: Takashi Iwai Message-ID: <0922071cb8be99a2394705de27b917d1e4e46f3f.1710264833.git.g@b4.vu> (cherry picked from commit 4074f8d23278e9e32aabe9dba60f6dddaf68e6ef) Signed-off-by: Roxana Nicolescu --- sound/usb/mixer_scarlett2.c | 1187 ++++++++++++++++++----------------- 1 file changed, 595 insertions(+), 592 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,798 +6383,801 @@ static int scarlett2_add_power_status_ctl(struct usb_mixer_interface *mixer) &private->power_status_ctl); } -/*** Cleanup/Suspend Callbacks ***/ +/*** Notification Handlers ***/ -static void scarlett2_private_free(struct usb_mixer_interface *mixer) +/* Notify on sync change */ +static void scarlett2_notify_sync(struct usb_mixer_interface *mixer) { struct scarlett2_data *private = mixer->private_data; - cancel_delayed_work_sync(&private->work); - kfree(private); - mixer->private_data = NULL; + private->sync_updated = 1; + + snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->sync_ctl->id); } -static void scarlett2_private_suspend(struct usb_mixer_interface *mixer) +/* Notify on monitor change (Gen 2/3) */ +static void scarlett2_notify_monitor(struct usb_mixer_interface *mixer) { + struct snd_card *card = mixer->chip->card; struct scarlett2_data *private = mixer->private_data; + int i; - 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; + if (!scarlett2_has_config_item(private, SCARLETT2_CONFIG_SW_HW_SWITCH)) + return; - /* 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->vol_updated = 1; - private->num_mux_srcs = srcs; - private->num_mux_dsts = dsts; + snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->master_vol_ctl->id); - /* 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; + for (i = 0; i < private->num_line_out; i++) + if (private->vol_sw_hw_switch[line_out_remap(private, i)]) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->vol_ctls[i]->id); +} - private->num_mix_out = - port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN] - - info->dsp_count; +/* Notify on volume change (Gen 4) */ +static void scarlett2_notify_volume(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; - /* Number of analogue line outputs */ - private->num_line_out = - port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT]; + private->vol_updated = 1; - /* Number of monitor mix controls */ - private->num_monitor_mix_ctls = - info->direct_monitor * 2 * private->num_mix_in; + snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->master_vol_ctl->id); + snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->headphone_vol_ctl->id); } -/* 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) +/* Notify on dim/mute change */ +static void scarlett2_notify_dim_mute(struct usb_mixer_interface *mixer) { - struct usb_host_config *config = dev->actconfig; + struct snd_card *card = mixer->chip->card; + struct scarlett2_data *private = mixer->private_data; 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 (!scarlett2_has_config_item(private, SCARLETT2_CONFIG_SW_HW_SWITCH)) + return; - if (desc->bInterfaceClass != 255) - continue; + private->dim_mute_updated = 1; - 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; - } + for (i = 0; i < SCARLETT2_DIM_MUTE_COUNT; i++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->dim_mute_ctls[i]->id); - return -EINVAL; + for (i = 0; i < private->num_line_out; i++) + if (private->vol_sw_hw_switch[line_out_remap(private, i)]) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->mute_ctls[i]->id); } -/* Initialise private data */ -static int scarlett2_init_private(struct usb_mixer_interface *mixer, - const struct scarlett2_device_entry *entry) +/* Notify on input level switch change */ +static void scarlett2_notify_input_level(struct usb_mixer_interface *mixer) { - struct scarlett2_data *private = - kzalloc(sizeof(struct scarlett2_data), GFP_KERNEL); + struct snd_card *card = mixer->chip->card; + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + int i; - if (!private) - return -ENOMEM; + private->input_level_updated = 1; - mutex_init(&private->usb_mutex); - mutex_init(&private->data_mutex); - INIT_DELAYED_WORK(&private->work, scarlett2_config_save_work); + for (i = 0; i < info->level_input_count; i++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->level_ctls[i]->id); +} - mixer->private_data = private; - mixer->private_free = scarlett2_private_free; - mixer->private_suspend = scarlett2_private_suspend; +/* Notify on input pad switch change */ +static void scarlett2_notify_input_pad(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->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; + private->input_pad_updated = 1; - return scarlett2_find_fc_interface(mixer->chip->dev, private); + for (i = 0; i < info->pad_input_count; i++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->pad_ctls[i]->id); } -/* Cargo cult proprietary initialisation sequence */ -static int scarlett2_usb_init(struct usb_mixer_interface *mixer) +/* Notify on input air switch change */ +static void scarlett2_notify_input_air(struct usb_mixer_interface *mixer) { - struct usb_device *dev = mixer->chip->dev; + struct snd_card *card = mixer->chip->card; struct scarlett2_data *private = mixer->private_data; - u8 step0_buf[24]; - u8 step2_buf[84]; - int err; + const struct scarlett2_device_info *info = private->info; + int i; - if (usb_pipe_type_check(dev, usb_sndctrlpipe(dev, 0))) - return -EINVAL; + private->input_air_updated = 1; - /* step 0 */ - err = scarlett2_usb_rx(dev, private->bInterfaceNumber, - SCARLETT2_USB_CMD_INIT, - step0_buf, sizeof(step0_buf)); - if (err < 0) - return err; + for (i = 0; i < info->air_input_count; i++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->air_ctls[i]->id); +} - /* step 1 */ - private->scarlett2_seq = 1; - err = scarlett2_usb(mixer, SCARLETT2_USB_INIT_1, NULL, 0, NULL, 0); - if (err < 0) - return err; +/* Notify on input phantom switch change */ +static void scarlett2_notify_input_phantom(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; - /* 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; + private->input_phantom_updated = 1; - /* 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); + for (i = 0; i < info->phantom_count; i++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->phantom_ctls[i]->id); - return 0; + scarlett2_phantom_notify_access(mixer); } -/* 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) +/* Notify on "input other" change (level/pad/air/phantom) */ +static void scarlett2_notify_input_other(struct usb_mixer_interface *mixer) +{ + scarlett2_notify_input_level(mixer); + scarlett2_notify_input_pad(mixer); + scarlett2_notify_input_air(mixer); + scarlett2_notify_input_phantom(mixer); +} + +/* Notify on input select change */ +static void scarlett2_notify_input_select(struct usb_mixer_interface *mixer) { + struct snd_card *card = mixer->chip->card; struct scarlett2_data *private = mixer->private_data; - int err, count, i; + const struct scarlett2_device_info *info = private->info; + int i; - struct { - __le32 size; - __le32 count; - u8 unknown[8]; - } __packed flash_info; + if (!info->gain_input_count) + return; - struct { - __le32 size; - __le32 flags; - char name[16]; - } __packed segment_info; + private->input_select_updated = 1; - err = scarlett2_usb(mixer, SCARLETT2_USB_INFO_FLASH, - NULL, 0, - &flash_info, sizeof(flash_info)); - if (err < 0) - return err; + snd_ctl_notify(card, + SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, + &private->input_select_ctl->id); - count = le32_to_cpu(flash_info.count); + for (i = 0; i < info->gain_input_count / 2; i++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->input_link_ctls[i]->id); +} - /* 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; +/* Notify on input gain change */ +static void scarlett2_notify_input_gain(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->flash_segment_nums[flash_segment_id] = i; - private->flash_segment_blocks[flash_segment_id] = - le32_to_cpu(segment_info.size) / - SCARLETT2_FLASH_BLOCK_SIZE; - } + if (!info->gain_input_count) + return; - /* 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; - } + private->input_gain_updated = 1; - return 0; + for (i = 0; i < info->gain_input_count; i++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->input_gain_ctls[i]->id); } -/* Read configuration from the interface on start */ -static int scarlett2_read_configs(struct usb_mixer_interface *mixer) +/* Notify on autogain change */ +static void scarlett2_notify_autogain(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 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; + int i; - err = scarlett2_update_input_air(mixer); - if (err < 0) - return err; + if (!info->gain_input_count) + return; - err = scarlett2_update_input_phantom(mixer); - if (err < 0) - return err; + private->autogain_updated = 1; - err = scarlett2_update_direct_monitor(mixer); - if (err < 0) - return err; + for (i = 0; i < info->gain_input_count; i++) { + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->autogain_ctls[i]->id); + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->autogain_status_ctls[i]->id); + } - /* the rest of the configuration is for devices with a mixer */ - if (!scarlett2_has_mixer(private)) - return 0; + scarlett2_autogain_notify_access(mixer); +} - err = scarlett2_update_monitor_mix(mixer); - if (err < 0) - return err; +/* Notify on input safe switch change */ +static void scarlett2_notify_input_safe(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; - err = scarlett2_update_monitor_other(mixer); - if (err < 0) - return err; + if (!info->gain_input_count) + return; - 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; - } + private->input_safe_updated = 1; - if (scarlett2_has_config_item(private, - SCARLETT2_CONFIG_POWER_EXT)) { - err = scarlett2_update_power_status(mixer); - if (err < 0) - return err; - } + for (i = 0; i < info->gain_input_count; i++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->safe_ctls[i]->id); +} - err = scarlett2_update_sync(mixer); - if (err < 0) - return err; +/* Notify on "monitor other" change (speaker switching, talkback) */ +static void scarlett2_notify_monitor_other(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; - if (scarlett2_has_config_item(private, - SCARLETT2_CONFIG_LINE_OUT_VOLUME)) { - s16 sw_vol[SCARLETT2_ANALOGUE_MAX]; + private->monitor_other_updated = 1; - /* 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; + if (info->has_speaker_switching) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->speaker_switching_ctl->id); - for (i = 0; i < private->num_line_out; i++) - private->vol[i] = clamp( - sw_vol[i] + SCARLETT2_VOLUME_BIAS, - 0, SCARLETT2_VOLUME_BIAS); + if (info->has_talkback) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->talkback_ctl->id); - /* 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; + /* if speaker switching was recently enabled or disabled, + * invalidate the dim/mute and mux enum controls + */ + if (private->speaker_switching_switched) { + int i; - for (i = 0; i < private->num_line_out; i++) - private->mute_switch[i] = - !!private->mute_switch[i]; + scarlett2_notify_dim_mute(mixer); - /* 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; + private->speaker_switching_switched = 0; + private->mux_updated = 1; - for (i = 0; i < private->num_line_out; i++) - private->vol_sw_hw_switch[i] = - !!private->vol_sw_hw_switch[i]; - } + for (i = 0; i < private->num_mux_dsts; i++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->mux_ctls[i]->id); } +} - 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; +/* Notify on direct monitor switch change */ +static void scarlett2_notify_direct_monitor(struct usb_mixer_interface *mixer) +{ + struct snd_card *card = mixer->chip->card; + struct scarlett2_data *private = mixer->private_data; + int count = private->num_mix_in * private->num_mix_out; + int i; - err = scarlett2_update_autogain(mixer); - if (err < 0) - return err; + private->direct_monitor_updated = 1; - err = scarlett2_update_input_safe(mixer); - if (err < 0) - return err; + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->direct_monitor_ctl->id); - if (scarlett2_has_config_item(private, - SCARLETT2_CONFIG_PCM_INPUT_SWITCH)) { - err = scarlett2_update_pcm_input_switch(mixer); - if (err < 0) - return err; - } + if (!scarlett2_has_mixer(private)) + return; - err = scarlett2_update_mix(mixer); - if (err < 0) - return err; + private->mix_updated = 1; - return scarlett2_usb_get_mux(mixer); + /* Notify of change to the mix controls */ + for (i = 0; i < count; i++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->mix_ctls[i]->id); } -/* Notify on sync change */ -static void scarlett2_notify_sync(struct usb_mixer_interface *mixer) +/* Notify on power change */ +static void scarlett2_notify_power_status(struct usb_mixer_interface *mixer) { + struct snd_card *card = mixer->chip->card; struct scarlett2_data *private = mixer->private_data; - private->sync_updated = 1; + private->power_status_updated = 1; - snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, - &private->sync_ctl->id); + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->power_status_ctl->id); } -/* Notify on monitor change (Gen 2/3) */ -static void scarlett2_notify_monitor(struct usb_mixer_interface *mixer) +/* Notify on mux change */ +static void scarlett2_notify_mux(struct usb_mixer_interface *mixer) { struct snd_card *card = mixer->chip->card; struct scarlett2_data *private = mixer->private_data; int i; - if (!scarlett2_has_config_item(private, SCARLETT2_CONFIG_SW_HW_SWITCH)) - return; - - private->vol_updated = 1; - - snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, - &private->master_vol_ctl->id); + private->mux_updated = 1; - for (i = 0; i < private->num_line_out; i++) - if (private->vol_sw_hw_switch[line_out_remap(private, i)]) - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, - &private->vol_ctls[i]->id); + for (i = 0; i < private->num_mux_dsts; i++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->mux_ctls[i]->id); } -/* Notify on volume change (Gen 4) */ -static void scarlett2_notify_volume(struct usb_mixer_interface *mixer) +/* Notify on PCM input switch change */ +static void scarlett2_notify_pcm_input_switch(struct usb_mixer_interface *mixer) { + struct snd_card *card = mixer->chip->card; struct scarlett2_data *private = mixer->private_data; - private->vol_updated = 1; + private->pcm_input_switch_updated = 1; - snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, - &private->master_vol_ctl->id); - snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, - &private->headphone_vol_ctl->id); + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->pcm_input_switch_ctl->id); + + scarlett2_notify_mux(mixer); } -/* Notify on dim/mute change */ -static void scarlett2_notify_dim_mute(struct usb_mixer_interface *mixer) +/* Interrupt callback */ +static void scarlett2_notify(struct urb *urb) { - struct snd_card *card = mixer->chip->card; + struct usb_mixer_interface *mixer = urb->context; + int len = urb->actual_length; + int ustatus = urb->status; + u32 data; struct scarlett2_data *private = mixer->private_data; - int i; + const struct scarlett2_notification *notifications = + private->config_set->notifications; - if (!scarlett2_has_config_item(private, SCARLETT2_CONFIG_SW_HW_SWITCH)) - return; + if (ustatus != 0 || len != 8) + goto requeue; - private->dim_mute_updated = 1; + data = le32_to_cpu(*(__le32 *)urb->transfer_buffer); - for (i = 0; i < SCARLETT2_DIM_MUTE_COUNT; i++) - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, - &private->dim_mute_ctls[i]->id); + while (data && notifications->mask) { + if (data & notifications->mask) { + data &= ~notifications->mask; + if (notifications->func) + notifications->func(mixer); + } + notifications++; + } - for (i = 0; i < private->num_line_out; i++) - if (private->vol_sw_hw_switch[line_out_remap(private, i)]) - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, - &private->mute_ctls[i]->id); + if (data) + usb_audio_warn(mixer->chip, + "%s: Unhandled notification: 0x%08x\n", + __func__, data); + +requeue: + if (ustatus != -ENOENT && + ustatus != -ECONNRESET && + ustatus != -ESHUTDOWN) { + urb->dev = mixer->chip->dev; + usb_submit_urb(urb, GFP_ATOMIC); + } } -/* Notify on input level switch change */ -static void scarlett2_notify_input_level(struct usb_mixer_interface *mixer) +/*** Cleanup/Suspend Callbacks ***/ + +static void scarlett2_private_free(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_level_updated = 1; - for (i = 0; i < info->level_input_count; i++) - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, - &private->level_ctls[i]->id); + cancel_delayed_work_sync(&private->work); + kfree(private); + mixer->private_data = NULL; } -/* Notify on input pad switch change */ -static void scarlett2_notify_input_pad(struct usb_mixer_interface *mixer) +static void scarlett2_private_suspend(struct usb_mixer_interface *mixer) { - struct snd_card *card = mixer->chip->card; 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; - int i; + const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; + int port_type, srcs = 0, dsts = 0; - private->input_pad_updated = 1; + /* 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]; + } - for (i = 0; i < info->pad_input_count; i++) - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, - &private->pad_ctls[i]->id); + 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; } -/* Notify on input air switch change */ -static void scarlett2_notify_input_air(struct usb_mixer_interface *mixer) +/* 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 snd_card *card = mixer->chip->card; - struct scarlett2_data *private = mixer->private_data; - const struct scarlett2_device_info *info = private->info; + struct usb_host_config *config = dev->actconfig; int i; - private->input_air_updated = 1; + 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; - for (i = 0; i < info->air_input_count; i++) - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, - &private->air_ctls[i]->id); + 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; } -/* Notify on input phantom switch change */ -static void scarlett2_notify_input_phantom(struct usb_mixer_interface *mixer) +/* Initialise private data */ +static int scarlett2_init_private(struct usb_mixer_interface *mixer, + const struct scarlett2_device_entry *entry) { - struct snd_card *card = mixer->chip->card; - struct scarlett2_data *private = mixer->private_data; - const struct scarlett2_device_info *info = private->info; - int i; + struct scarlett2_data *private = + kzalloc(sizeof(struct scarlett2_data), GFP_KERNEL); - private->input_phantom_updated = 1; + if (!private) + return -ENOMEM; - for (i = 0; i < info->phantom_count; i++) - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, - &private->phantom_ctls[i]->id); + mutex_init(&private->usb_mutex); + mutex_init(&private->data_mutex); + INIT_DELAYED_WORK(&private->work, scarlett2_config_save_work); - scarlett2_phantom_notify_access(mixer); -} + mixer->private_data = private; + mixer->private_free = scarlett2_private_free; + mixer->private_suspend = scarlett2_private_suspend; -/* Notify on "input other" change (level/pad/air/phantom) */ -static void scarlett2_notify_input_other(struct usb_mixer_interface *mixer) -{ - scarlett2_notify_input_level(mixer); - scarlett2_notify_input_pad(mixer); - scarlett2_notify_input_air(mixer); - scarlett2_notify_input_phantom(mixer); + 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); } -/* Notify on input select change */ -static void scarlett2_notify_input_select(struct usb_mixer_interface *mixer) +/* Submit a URB to receive notifications from the device */ +static int scarlett2_init_notify(struct usb_mixer_interface *mixer) { - struct snd_card *card = mixer->chip->card; + struct usb_device *dev = mixer->chip->dev; struct scarlett2_data *private = mixer->private_data; - const struct scarlett2_device_info *info = private->info; - int i; + unsigned int pipe = usb_rcvintpipe(dev, private->bEndpointAddress); + void *transfer_buffer; - if (!info->gain_input_count) - return; + if (mixer->urb) { + usb_audio_err(mixer->chip, + "%s: mixer urb already in use!\n", __func__); + return 0; + } - private->input_select_updated = 1; + if (usb_pipe_type_check(dev, pipe)) + return -EINVAL; - snd_ctl_notify(card, - SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, - &private->input_select_ctl->id); + mixer->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!mixer->urb) + return -ENOMEM; - for (i = 0; i < info->gain_input_count / 2; i++) - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, - &private->input_link_ctls[i]->id); + transfer_buffer = kmalloc(private->wMaxPacketSize, GFP_KERNEL); + if (!transfer_buffer) + return -ENOMEM; + + usb_fill_int_urb(mixer->urb, dev, pipe, + transfer_buffer, private->wMaxPacketSize, + scarlett2_notify, mixer, private->bInterval); + + return usb_submit_urb(mixer->urb, GFP_KERNEL); } -/* Notify on input gain change */ -static void scarlett2_notify_input_gain(struct usb_mixer_interface *mixer) +/* Cargo cult proprietary initialisation sequence */ +static int scarlett2_usb_init(struct usb_mixer_interface *mixer) { - struct snd_card *card = mixer->chip->card; + struct usb_device *dev = mixer->chip->dev; struct scarlett2_data *private = mixer->private_data; - const struct scarlett2_device_info *info = private->info; - int i; + u8 step0_buf[24]; + u8 step2_buf[84]; + int err; - if (!info->gain_input_count) - return; + if (usb_pipe_type_check(dev, usb_sndctrlpipe(dev, 0))) + return -EINVAL; - private->input_gain_updated = 1; + /* step 0 */ + err = scarlett2_usb_rx(dev, private->bInterfaceNumber, + SCARLETT2_USB_CMD_INIT, + step0_buf, sizeof(step0_buf)); + if (err < 0) + return err; - for (i = 0; i < info->gain_input_count; i++) - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, - &private->input_gain_ctls[i]->id); + /* 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; } -/* Notify on autogain change */ -static void scarlett2_notify_autogain(struct usb_mixer_interface *mixer) +/* 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 snd_card *card = mixer->chip->card; struct scarlett2_data *private = mixer->private_data; - const struct scarlett2_device_info *info = private->info; - int i; + int err, count, i; - if (!info->gain_input_count) - return; + struct { + __le32 size; + __le32 count; + u8 unknown[8]; + } __packed flash_info; - private->autogain_updated = 1; + struct { + __le32 size; + __le32 flags; + char name[16]; + } __packed segment_info; - for (i = 0; i < info->gain_input_count; i++) { - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, - &private->autogain_ctls[i]->id); - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, - &private->autogain_status_ctls[i]->id); + 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; } - scarlett2_autogain_notify_access(mixer); -} + for (i = 0; i < count; i++) { + __le32 segment_num_req = cpu_to_le32(i); + int flash_segment_id; -/* Notify on input safe switch change */ -static void scarlett2_notify_input_safe(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; + 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 (!info->gain_input_count) - return; + 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->input_safe_updated = 1; + 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; + } - for (i = 0; i < info->gain_input_count; i++) - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, - &private->safe_ctls[i]->id); + /* 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; } -/* Notify on "monitor other" change (speaker switching, talkback) */ -static void scarlett2_notify_monitor_other(struct usb_mixer_interface *mixer) +/* Read configuration from the interface on start */ +static int scarlett2_read_configs(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 err, i; - private->monitor_other_updated = 1; - - if (info->has_speaker_switching) - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, - &private->speaker_switching_ctl->id); - - if (info->has_talkback) - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, - &private->talkback_ctl->id); - - /* if speaker switching was recently enabled or disabled, - * invalidate the dim/mute and mux enum controls - */ - if (private->speaker_switching_switched) { - int i; - - scarlett2_notify_dim_mute(mixer); - - private->speaker_switching_switched = 0; - private->mux_updated = 1; - - for (i = 0; i < private->num_mux_dsts; i++) - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, - &private->mux_ctls[i]->id); + 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; } -} -/* Notify on direct monitor switch change */ -static void scarlett2_notify_direct_monitor(struct usb_mixer_interface *mixer) -{ - struct snd_card *card = mixer->chip->card; - struct scarlett2_data *private = mixer->private_data; - int count = private->num_mix_in * private->num_mix_out; - int i; + 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; + } - private->direct_monitor_updated = 1; + /* no other controls are created if MSD mode is on */ + if (private->msd_switch) + return 0; - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, - &private->direct_monitor_ctl->id); + err = scarlett2_update_input_level(mixer); + if (err < 0) + return err; - if (!scarlett2_has_mixer(private)) - return; + err = scarlett2_update_input_pad(mixer); + if (err < 0) + return err; - private->mix_updated = 1; + err = scarlett2_update_input_air(mixer); + if (err < 0) + return err; - /* Notify of change to the mix controls */ - for (i = 0; i < count; i++) - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, - &private->mix_ctls[i]->id); -} + err = scarlett2_update_input_phantom(mixer); + if (err < 0) + return err; -/* Notify on power change */ -static void scarlett2_notify_power_status(struct usb_mixer_interface *mixer) -{ - struct snd_card *card = mixer->chip->card; - struct scarlett2_data *private = mixer->private_data; + err = scarlett2_update_direct_monitor(mixer); + if (err < 0) + return err; - private->power_status_updated = 1; + /* the rest of the configuration is for devices with a mixer */ + if (!scarlett2_has_mixer(private)) + return 0; - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, - &private->power_status_ctl->id); -} + err = scarlett2_update_monitor_mix(mixer); + if (err < 0) + return err; -/* Notify on mux change */ -static void scarlett2_notify_mux(struct usb_mixer_interface *mixer) -{ - struct snd_card *card = mixer->chip->card; - struct scarlett2_data *private = mixer->private_data; - int i; + err = scarlett2_update_monitor_other(mixer); + if (err < 0) + return err; - private->mux_updated = 1; + 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; + } - for (i = 0; i < private->num_mux_dsts; i++) - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, - &private->mux_ctls[i]->id); -} + if (scarlett2_has_config_item(private, + SCARLETT2_CONFIG_POWER_EXT)) { + err = scarlett2_update_power_status(mixer); + if (err < 0) + return err; + } -/* Notify on PCM input switch change */ -static void scarlett2_notify_pcm_input_switch(struct usb_mixer_interface *mixer) -{ - struct snd_card *card = mixer->chip->card; - struct scarlett2_data *private = mixer->private_data; + err = scarlett2_update_sync(mixer); + if (err < 0) + return err; - private->pcm_input_switch_updated = 1; + if (scarlett2_has_config_item(private, + SCARLETT2_CONFIG_LINE_OUT_VOLUME)) { + s16 sw_vol[SCARLETT2_ANALOGUE_MAX]; - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, - &private->pcm_input_switch_ctl->id); + /* 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; - scarlett2_notify_mux(mixer); -} + for (i = 0; i < private->num_line_out; i++) + private->vol[i] = clamp( + sw_vol[i] + SCARLETT2_VOLUME_BIAS, + 0, SCARLETT2_VOLUME_BIAS); -/* Interrupt callback */ -static void scarlett2_notify(struct urb *urb) -{ - struct usb_mixer_interface *mixer = urb->context; - int len = urb->actual_length; - int ustatus = urb->status; - u32 data; - struct scarlett2_data *private = mixer->private_data; - const struct scarlett2_notification *notifications = - private->config_set->notifications; + /* 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; - if (ustatus != 0 || len != 8) - goto requeue; + for (i = 0; i < private->num_line_out; i++) + private->mute_switch[i] = + !!private->mute_switch[i]; - data = le32_to_cpu(*(__le32 *)urb->transfer_buffer); + /* 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; - while (data && notifications->mask) { - if (data & notifications->mask) { - data &= ~notifications->mask; - if (notifications->func) - notifications->func(mixer); + for (i = 0; i < private->num_line_out; i++) + private->vol_sw_hw_switch[i] = + !!private->vol_sw_hw_switch[i]; } - notifications++; } - if (data) - usb_audio_warn(mixer->chip, - "%s: Unhandled notification: 0x%08x\n", - __func__, data); + err = scarlett2_update_volumes(mixer); + if (err < 0) + return err; -requeue: - if (ustatus != -ENOENT && - ustatus != -ECONNRESET && - ustatus != -ESHUTDOWN) { - urb->dev = mixer->chip->dev; - usb_submit_urb(urb, GFP_ATOMIC); - } -} + err = scarlett2_update_dim_mute(mixer); + if (err < 0) + return err; -static int scarlett2_init_notify(struct usb_mixer_interface *mixer) -{ - struct usb_device *dev = mixer->chip->dev; - struct scarlett2_data *private = mixer->private_data; - unsigned int pipe = usb_rcvintpipe(dev, private->bEndpointAddress); - void *transfer_buffer; + err = scarlett2_update_input_select(mixer); + if (err < 0) + return err; - if (mixer->urb) { - usb_audio_err(mixer->chip, - "%s: mixer urb already in use!\n", __func__); - return 0; - } + err = scarlett2_update_input_gain(mixer); + if (err < 0) + return err; - if (usb_pipe_type_check(dev, pipe)) - return -EINVAL; + err = scarlett2_update_autogain(mixer); + if (err < 0) + return err; - mixer->urb = usb_alloc_urb(0, GFP_KERNEL); - if (!mixer->urb) - return -ENOMEM; + err = scarlett2_update_input_safe(mixer); + if (err < 0) + return err; - transfer_buffer = kmalloc(private->wMaxPacketSize, GFP_KERNEL); - if (!transfer_buffer) - return -ENOMEM; + if (scarlett2_has_config_item(private, + SCARLETT2_CONFIG_PCM_INPUT_SWITCH)) { + err = scarlett2_update_pcm_input_switch(mixer); + if (err < 0) + return err; + } - usb_fill_int_urb(mixer->urb, dev, pipe, - transfer_buffer, private->wMaxPacketSize, - scarlett2_notify, mixer, private->bInterval); + err = scarlett2_update_mix(mixer); + if (err < 0) + return err; - return usb_submit_urb(mixer->urb, GFP_KERNEL); + return scarlett2_usb_get_mux(mixer); } static const struct scarlett2_device_entry *get_scarlett2_device_entry( From patchwork Tue Sep 24 13:31:57 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roxana Nicolescu X-Patchwork-Id: 1988970 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.ubuntu.com (client-ip=185.125.189.65; helo=lists.ubuntu.com; envelope-from=kernel-team-bounces@lists.ubuntu.com; receiver=patchwork.ozlabs.org) Received: from lists.ubuntu.com (lists.ubuntu.com [185.125.189.65]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4XCglD416Vz1xsN for ; Tue, 24 Sep 2024 23:32:24 +1000 (AEST) Received: from localhost ([127.0.0.1] helo=lists.ubuntu.com) by lists.ubuntu.com with esmtp (Exim 4.86_2) (envelope-from ) id 1st5eK-0003o0-1E; Tue, 24 Sep 2024 13:32:16 +0000 Received: from smtp-relay-internal-1.internal ([10.131.114.114] helo=smtp-relay-internal-1.canonical.com) by lists.ubuntu.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1st5eH-0003l0-0I for kernel-team@lists.ubuntu.com; Tue, 24 Sep 2024 13:32:13 +0000 Received: from mail-pf1-f200.google.com (mail-pf1-f200.google.com [209.85.210.200]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-1.canonical.com (Postfix) with ESMTPS id F20553F215 for ; Tue, 24 Sep 2024 13:32:11 +0000 (UTC) Received: by mail-pf1-f200.google.com with SMTP id d2e1a72fcca58-718db8e61bfso8326748b3a.0 for ; Tue, 24 Sep 2024 06:32:11 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1727184730; x=1727789530; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=8BFF5qt2XQqhUqe7UmHASf7HYsR72n0gRr8Yhhi7Mpg=; b=c3v/elZqDcd2s5V+eAdCmnkWpDopF075rf0foudL0crJRG9jcL0e7/kR4FjvBIWVOQ 3Cq3q5eux+bNICTsRi51j8Wf9SJrHQxUL22JaLaPUwXPKbdQjpf0HqwnvapVerNUWikJ c3RiTeUtUUS8/V6DYBURTLXolPxaiaCtH7B9o41/8EzcX3W/abb2Yk0g770pOGymhplk nQsoQZv0AHut19Nq4KaVvtYdKNZ1MZ9bjLFLJmW/FRsr5qy7gRzHOxaYkxD9YvOuY5E7 qrEy2K3WSBe13JZts8gKTdO9xQE1VOHMfl5kfA7sS5Fs454tiT1JlaGAQIXoXLJXdYSY 9RFw== X-Gm-Message-State: AOJu0YxNEgW0KHOydSqrnnIKdwwpek9X3n4Hn15wjQsqdeWPUQmzLvg/ reU3qaKU9ecX4/7O9KJZbG+rMwdvdvvyp3liLWsQ5rUBWW4SyNtgJvPW1UgOZmtL9/haH5fVIaU b6Z+56755jnM5OXYoAzzViI8ZMJP06pZomt3PCaYqcIwjuOli+25YBXwEyqgzo89VHAWaAgy1SY 1UOLtx9o0wVqAyXUo= X-Received: by 2002:a05:6a21:9216:b0:1cf:506a:cdcc with SMTP id adf61e73a8af0-1d30cb65dedmr23545862637.43.1727184730581; Tue, 24 Sep 2024 06:32:10 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEoHWSDVMv+I001+vuZeK96Mpcu75Q1LXyNz1ztfj4e/A0kmR7o7wSO6s5Lf6bUt36gT+WpJA== X-Received: by 2002:a05:6a21:9216:b0:1cf:506a:cdcc with SMTP id adf61e73a8af0-1d30cb65dedmr23545835637.43.1727184730193; Tue, 24 Sep 2024 06:32:10 -0700 (PDT) Received: from work.lan ([2001:67c:1560:8007::aac:c490]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-71afc85bbbasm1224965b3a.95.2024.09.24.06.32.08 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 24 Sep 2024 06:32:09 -0700 (PDT) From: Roxana Nicolescu To: kernel-team@lists.ubuntu.com Subject: [SRU][N][PATCH 2/2] ALSA: scarlett2: Implement handling of the ACK notification Date: Tue, 24 Sep 2024 15:31:57 +0200 Message-Id: <20240924133157.15168-3-roxana.nicolescu@canonical.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240924133157.15168-1-roxana.nicolescu@canonical.com> References: <20240924133157.15168-1-roxana.nicolescu@canonical.com> MIME-Version: 1.0 X-BeenThere: kernel-team@lists.ubuntu.com X-Mailman-Version: 2.1.20 Precedence: list List-Id: Kernel team discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: kernel-team-bounces@lists.ubuntu.com Sender: "kernel-team" From: "Geoffrey D. Bennett" BugLink: https://bugs.launchpad.net/bugs/2076402 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 Signed-off-by: Takashi Iwai Message-ID: <452d1263c40fa8eba1cfb24e2055e40a84cbc437.1710264833.git.g@b4.vu> (cherry picked from commit 1b65088958cadab04d5d34a8615e2466b1b48ecb) Signed-off-by: Roxana Nicolescu --- 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; }