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(