ALSA: usb-audio: Add support for MOTU MicroBook IIc
authorAlexander Tsoy <alexander@tsoy.me>
Sat, 29 Feb 2020 15:18:15 +0000 (18:18 +0300)
committerTakashi Iwai <tiwai@suse.de>
Fri, 6 Mar 2020 08:03:17 +0000 (09:03 +0100)
MicroBook IIc operates in UAC2 mode by default. This patch addresses
several issues with it:

- MicroBook II and IIc shares the same USB ID. We can distinguish them
  by interface class.
- MaxPacketsOnly attribute is erroneously set in endpoint descriptors.
  As a result this card produces noise with all sample rates other than
  96 KHz. This also causes issues like IOMMU page faults and other
  problems with host controller.
- Sample rate changes takes more than 2 seconds for this device. Clock
  validity request returns false during that period, so the clock validity
  quirk is required.

Signed-off-by: Alexander Tsoy <alexander@tsoy.me>
Link: https://lore.kernel.org/r/20200229151815.14199-1-alexander@tsoy.me
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/usb/clock.c
sound/usb/pcm.c
sound/usb/quirks-table.h
sound/usb/quirks.c

index a48313dfa967a15836785b9961ffb7df346e5f7a..b118cf97607f3d24a68c84e7e1e2a456ca1cdc6f 100644 (file)
@@ -151,16 +151,15 @@ static int uac_clock_selector_set_val(struct snd_usb_audio *chip, int selector_i
        return ret;
 }
 
-/*
- * Assume the clock is valid if clock source supports only one single sample
- * rate, the terminal is connected directly to it (there is no clock selector)
- * and clock type is internal. This is to deal with some Denon DJ controllers
- * that always reports that clock is invalid.
- */
 static bool uac_clock_source_is_valid_quirk(struct snd_usb_audio *chip,
                                            struct audioformat *fmt,
                                            int source_id)
 {
+       bool ret = false;
+       int count;
+       unsigned char data;
+       struct usb_device *dev = chip->dev;
+
        if (fmt->protocol == UAC_VERSION_2) {
                struct uac_clock_source_descriptor *cs_desc =
                        snd_usb_find_clock_source(chip->ctrl_intf, source_id);
@@ -168,13 +167,51 @@ static bool uac_clock_source_is_valid_quirk(struct snd_usb_audio *chip,
                if (!cs_desc)
                        return false;
 
-               return (fmt->nr_rates == 1 &&
-                       (fmt->clock & 0xff) == cs_desc->bClockID &&
-                       (cs_desc->bmAttributes & 0x3) !=
-                               UAC_CLOCK_SOURCE_TYPE_EXT);
+               /*
+                * Assume the clock is valid if clock source supports only one
+                * single sample rate, the terminal is connected directly to it
+                * (there is no clock selector) and clock type is internal.
+                * This is to deal with some Denon DJ controllers that always
+                * reports that clock is invalid.
+                */
+               if (fmt->nr_rates == 1 &&
+                   (fmt->clock & 0xff) == cs_desc->bClockID &&
+                   (cs_desc->bmAttributes & 0x3) !=
+                               UAC_CLOCK_SOURCE_TYPE_EXT)
+                       return true;
+       }
+
+       /*
+        * MOTU MicroBook IIc
+        * Sample rate changes takes more than 2 seconds for this device. Clock
+        * validity request returns false during that period.
+        */
+       if (chip->usb_id == USB_ID(0x07fd, 0x0004)) {
+               count = 0;
+
+               while ((!ret) && (count < 50)) {
+                       int err;
+
+                       msleep(100);
+
+                       err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
+                                             USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
+                                             UAC2_CS_CONTROL_CLOCK_VALID << 8,
+                                             snd_usb_ctrl_intf(chip) | (source_id << 8),
+                                             &data, sizeof(data));
+                       if (err < 0) {
+                               dev_warn(&dev->dev,
+                                        "%s(): cannot get clock validity for id %d\n",
+                                          __func__, source_id);
+                               return false;
+                       }
+
+                       ret = !!data;
+                       count++;
+               }
        }
 
-       return false;
+       return ret;
 }
 
 static bool uac_clock_source_is_valid(struct snd_usb_audio *chip,
index bd258f1ec2dd3c43c0c10da247caa30917fc554c..a4e4064f9aee7b1f365ce12e4189d58852e3e00a 100644 (file)
@@ -357,7 +357,12 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs,
                ep = 0x81;
                ifnum = 1;
                goto add_sync_ep_from_ifnum;
-       case USB_ID(0x07fd, 0x0004): /* MOTU MicroBook II */
+       case USB_ID(0x07fd, 0x0004): /* MOTU MicroBook II/IIc */
+               /* MicroBook IIc */
+               if (altsd->bInterfaceClass == USB_CLASS_AUDIO)
+                       return 0;
+
+               /* MicroBook II */
                ep = 0x84;
                ifnum = 0;
                goto add_sync_ep_from_ifnum;
index d187aa6d50db0cd1f810495cdc84318019f61f16..1c8719292eee414874fa310357cc8330649fe8fc 100644 (file)
@@ -3472,7 +3472,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
 },
 /* MOTU Microbook II */
 {
-       USB_DEVICE(0x07fd, 0x0004),
+       USB_DEVICE_VENDOR_SPEC(0x07fd, 0x0004),
        .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
                .vendor_name = "MOTU",
                .product_name = "MicroBookII",
index 915aea256f65c357a98c783e99a17a23f83995d2..3cc745fe24d8dd9c3a104f863f1b2c3177949ab7 100644 (file)
@@ -1352,7 +1352,15 @@ int snd_usb_apply_boot_quirk(struct usb_device *dev,
        case USB_ID(0x2466, 0x8010): /* Fractal Audio Axe-Fx 3 */
                return snd_usb_axefx3_boot_quirk(dev);
        case USB_ID(0x07fd, 0x0004): /* MOTU MicroBook II */
-               return snd_usb_motu_microbookii_boot_quirk(dev);
+               /*
+                * For some reason interface 3 with vendor-spec class is
+                * detected on MicroBook IIc.
+                */
+               if (get_iface_desc(intf->altsetting)->bInterfaceClass ==
+                   USB_CLASS_VENDOR_SPEC &&
+                   get_iface_desc(intf->altsetting)->bInterfaceNumber < 3)
+                       return snd_usb_motu_microbookii_boot_quirk(dev);
+               break;
        }
 
        return 0;
@@ -1790,5 +1798,13 @@ void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip,
                else
                        fp->ep_attr |= USB_ENDPOINT_SYNC_SYNC;
                break;
+       case USB_ID(0x07fd, 0x0004):  /* MOTU MicroBook IIc */
+               /*
+                * MaxPacketsOnly attribute is erroneously set in endpoint
+                * descriptors. As a result this card produces noise with
+                * all sample rates other than 96 KHz.
+                */
+               fp->attributes &= ~UAC_EP_CS_ATTR_FILL_MAX;
+               break;
        }
 }