Commit 2edb84e3 authored by Alexander Tsoy's avatar Alexander Tsoy Committed by Takashi Iwai

ALSA: usb-audio: Add support for MOTU MicroBook IIc

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: default avatarAlexander Tsoy <alexander@tsoy.me>
Link: https://lore.kernel.org/r/20200229151815.14199-1-alexander@tsoy.meSigned-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 66db2958
...@@ -151,16 +151,15 @@ static int uac_clock_selector_set_val(struct snd_usb_audio *chip, int selector_i ...@@ -151,16 +151,15 @@ static int uac_clock_selector_set_val(struct snd_usb_audio *chip, int selector_i
return ret; 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, static bool uac_clock_source_is_valid_quirk(struct snd_usb_audio *chip,
struct audioformat *fmt, struct audioformat *fmt,
int source_id) int source_id)
{ {
bool ret = false;
int count;
unsigned char data;
struct usb_device *dev = chip->dev;
if (fmt->protocol == UAC_VERSION_2) { if (fmt->protocol == UAC_VERSION_2) {
struct uac_clock_source_descriptor *cs_desc = struct uac_clock_source_descriptor *cs_desc =
snd_usb_find_clock_source(chip->ctrl_intf, source_id); 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, ...@@ -168,13 +167,51 @@ static bool uac_clock_source_is_valid_quirk(struct snd_usb_audio *chip,
if (!cs_desc) if (!cs_desc)
return false; return false;
return (fmt->nr_rates == 1 && /*
* 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 && (fmt->clock & 0xff) == cs_desc->bClockID &&
(cs_desc->bmAttributes & 0x3) != (cs_desc->bmAttributes & 0x3) !=
UAC_CLOCK_SOURCE_TYPE_EXT); 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; return false;
}
ret = !!data;
count++;
}
}
return ret;
} }
static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, static bool uac_clock_source_is_valid(struct snd_usb_audio *chip,
......
...@@ -357,7 +357,12 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, ...@@ -357,7 +357,12 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs,
ep = 0x81; ep = 0x81;
ifnum = 1; ifnum = 1;
goto add_sync_ep_from_ifnum; 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; ep = 0x84;
ifnum = 0; ifnum = 0;
goto add_sync_ep_from_ifnum; goto add_sync_ep_from_ifnum;
......
...@@ -3472,7 +3472,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), ...@@ -3472,7 +3472,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
}, },
/* MOTU Microbook II */ /* MOTU Microbook II */
{ {
USB_DEVICE(0x07fd, 0x0004), USB_DEVICE_VENDOR_SPEC(0x07fd, 0x0004),
.driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
.vendor_name = "MOTU", .vendor_name = "MOTU",
.product_name = "MicroBookII", .product_name = "MicroBookII",
......
...@@ -1352,7 +1352,15 @@ int snd_usb_apply_boot_quirk(struct usb_device *dev, ...@@ -1352,7 +1352,15 @@ int snd_usb_apply_boot_quirk(struct usb_device *dev,
case USB_ID(0x2466, 0x8010): /* Fractal Audio Axe-Fx 3 */ case USB_ID(0x2466, 0x8010): /* Fractal Audio Axe-Fx 3 */
return snd_usb_axefx3_boot_quirk(dev); return snd_usb_axefx3_boot_quirk(dev);
case USB_ID(0x07fd, 0x0004): /* MOTU MicroBook II */ case USB_ID(0x07fd, 0x0004): /* MOTU MicroBook II */
/*
* 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); return snd_usb_motu_microbookii_boot_quirk(dev);
break;
} }
return 0; return 0;
...@@ -1790,5 +1798,13 @@ void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip, ...@@ -1790,5 +1798,13 @@ void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip,
else else
fp->ep_attr |= USB_ENDPOINT_SYNC_SYNC; fp->ep_attr |= USB_ENDPOINT_SYNC_SYNC;
break; 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;
} }
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment