From 1f93a7dfd1f8d5ff7a5c53246c7534fe2332d6f4 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Mon, 11 Dec 2023 02:46:07 +0000 Subject: [PATCH] add audio --- kernel/sound/usb/quirks.c | 423 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 files changed, 382 insertions(+), 41 deletions(-) diff --git a/kernel/sound/usb/quirks.c b/kernel/sound/usb/quirks.c index 28f2880..7524221 100644 --- a/kernel/sound/usb/quirks.c +++ b/kernel/sound/usb/quirks.c @@ -1,17 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/init.h> @@ -19,6 +7,7 @@ #include <linux/usb.h> #include <linux/usb/audio.h> #include <linux/usb/midi.h> +#include <linux/bits.h> #include <sound/control.h> #include <sound/core.h> @@ -537,6 +526,15 @@ return 1; /* Continue with creating streams and mixer */ } +static int setup_disable_autosuspend(struct snd_usb_audio *chip, + struct usb_interface *iface, + struct usb_driver *driver, + const struct snd_usb_audio_quirk *quirk) +{ + usb_disable_autosuspend(interface_to_usbdev(iface)); + return 1; /* Continue with creating streams and mixer */ +} + /* * audio-interface quirks * @@ -576,6 +574,7 @@ [QUIRK_AUDIO_ALIGN_TRANSFER] = create_align_transfer_quirk, [QUIRK_AUDIO_STANDARD_MIXER] = create_standard_mixer_quirk, [QUIRK_SETUP_FMT_AFTER_RESUME] = setup_fmt_after_resume_quirk, + [QUIRK_SETUP_DISABLE_AUTOSUSPEND] = setup_disable_autosuspend, }; if (quirk->type < QUIRK_TYPE_COUNT) { @@ -690,15 +689,133 @@ } /* - * C-Media CM6206 is based on CM106 with two additional - * registers that are not documented in the data sheet. - * Values here are chosen based on sniffing USB traffic - * under Windows. + * CM6206 registers from the CM6206 datasheet rev 2.1 */ +#define CM6206_REG0_DMA_MASTER BIT(15) +#define CM6206_REG0_SPDIFO_RATE_48K (2 << 12) +#define CM6206_REG0_SPDIFO_RATE_96K (7 << 12) +/* Bit 4 thru 11 is the S/PDIF category code */ +#define CM6206_REG0_SPDIFO_CAT_CODE_GENERAL (0 << 4) +#define CM6206_REG0_SPDIFO_EMPHASIS_CD BIT(3) +#define CM6206_REG0_SPDIFO_COPYRIGHT_NA BIT(2) +#define CM6206_REG0_SPDIFO_NON_AUDIO BIT(1) +#define CM6206_REG0_SPDIFO_PRO_FORMAT BIT(0) + +#define CM6206_REG1_TEST_SEL_CLK BIT(14) +#define CM6206_REG1_PLLBIN_EN BIT(13) +#define CM6206_REG1_SOFT_MUTE_EN BIT(12) +#define CM6206_REG1_GPIO4_OUT BIT(11) +#define CM6206_REG1_GPIO4_OE BIT(10) +#define CM6206_REG1_GPIO3_OUT BIT(9) +#define CM6206_REG1_GPIO3_OE BIT(8) +#define CM6206_REG1_GPIO2_OUT BIT(7) +#define CM6206_REG1_GPIO2_OE BIT(6) +#define CM6206_REG1_GPIO1_OUT BIT(5) +#define CM6206_REG1_GPIO1_OE BIT(4) +#define CM6206_REG1_SPDIFO_INVALID BIT(3) +#define CM6206_REG1_SPDIF_LOOP_EN BIT(2) +#define CM6206_REG1_SPDIFO_DIS BIT(1) +#define CM6206_REG1_SPDIFI_MIX BIT(0) + +#define CM6206_REG2_DRIVER_ON BIT(15) +#define CM6206_REG2_HEADP_SEL_SIDE_CHANNELS (0 << 13) +#define CM6206_REG2_HEADP_SEL_SURROUND_CHANNELS (1 << 13) +#define CM6206_REG2_HEADP_SEL_CENTER_SUBW (2 << 13) +#define CM6206_REG2_HEADP_SEL_FRONT_CHANNELS (3 << 13) +#define CM6206_REG2_MUTE_HEADPHONE_RIGHT BIT(12) +#define CM6206_REG2_MUTE_HEADPHONE_LEFT BIT(11) +#define CM6206_REG2_MUTE_REAR_SURROUND_RIGHT BIT(10) +#define CM6206_REG2_MUTE_REAR_SURROUND_LEFT BIT(9) +#define CM6206_REG2_MUTE_SIDE_SURROUND_RIGHT BIT(8) +#define CM6206_REG2_MUTE_SIDE_SURROUND_LEFT BIT(7) +#define CM6206_REG2_MUTE_SUBWOOFER BIT(6) +#define CM6206_REG2_MUTE_CENTER BIT(5) +#define CM6206_REG2_MUTE_RIGHT_FRONT BIT(3) +#define CM6206_REG2_MUTE_LEFT_FRONT BIT(3) +#define CM6206_REG2_EN_BTL BIT(2) +#define CM6206_REG2_MCUCLKSEL_1_5_MHZ (0) +#define CM6206_REG2_MCUCLKSEL_3_MHZ (1) +#define CM6206_REG2_MCUCLKSEL_6_MHZ (2) +#define CM6206_REG2_MCUCLKSEL_12_MHZ (3) + +/* Bit 11..13 sets the sensitivity to FLY tuner volume control VP/VD signal */ +#define CM6206_REG3_FLYSPEED_DEFAULT (2 << 11) +#define CM6206_REG3_VRAP25EN BIT(10) +#define CM6206_REG3_MSEL1 BIT(9) +#define CM6206_REG3_SPDIFI_RATE_44_1K BIT(0 << 7) +#define CM6206_REG3_SPDIFI_RATE_48K BIT(2 << 7) +#define CM6206_REG3_SPDIFI_RATE_32K BIT(3 << 7) +#define CM6206_REG3_PINSEL BIT(6) +#define CM6206_REG3_FOE BIT(5) +#define CM6206_REG3_ROE BIT(4) +#define CM6206_REG3_CBOE BIT(3) +#define CM6206_REG3_LOSE BIT(2) +#define CM6206_REG3_HPOE BIT(1) +#define CM6206_REG3_SPDIFI_CANREC BIT(0) + +#define CM6206_REG5_DA_RSTN BIT(13) +#define CM6206_REG5_AD_RSTN BIT(12) +#define CM6206_REG5_SPDIFO_AD2SPDO BIT(12) +#define CM6206_REG5_SPDIFO_SEL_FRONT (0 << 9) +#define CM6206_REG5_SPDIFO_SEL_SIDE_SUR (1 << 9) +#define CM6206_REG5_SPDIFO_SEL_CEN_LFE (2 << 9) +#define CM6206_REG5_SPDIFO_SEL_REAR_SUR (3 << 9) +#define CM6206_REG5_CODECM BIT(8) +#define CM6206_REG5_EN_HPF BIT(7) +#define CM6206_REG5_T_SEL_DSDA4 BIT(6) +#define CM6206_REG5_T_SEL_DSDA3 BIT(5) +#define CM6206_REG5_T_SEL_DSDA2 BIT(4) +#define CM6206_REG5_T_SEL_DSDA1 BIT(3) +#define CM6206_REG5_T_SEL_DSDAD_NORMAL 0 +#define CM6206_REG5_T_SEL_DSDAD_FRONT 4 +#define CM6206_REG5_T_SEL_DSDAD_S_SURROUND 5 +#define CM6206_REG5_T_SEL_DSDAD_CEN_LFE 6 +#define CM6206_REG5_T_SEL_DSDAD_R_SURROUND 7 + static int snd_usb_cm6206_boot_quirk(struct usb_device *dev) { int err = 0, reg; - int val[] = {0x2004, 0x3000, 0xf800, 0x143f, 0x0000, 0x3000}; + int val[] = { + /* + * Values here are chosen based on sniffing USB traffic + * under Windows. + * + * REG0: DAC is master, sample rate 48kHz, no copyright + */ + CM6206_REG0_SPDIFO_RATE_48K | + CM6206_REG0_SPDIFO_COPYRIGHT_NA, + /* + * REG1: PLL binary search enable, soft mute enable. + */ + CM6206_REG1_PLLBIN_EN | + CM6206_REG1_SOFT_MUTE_EN, + /* + * REG2: enable output drivers, + * select front channels to the headphone output, + * then mute the headphone channels, run the MCU + * at 1.5 MHz. + */ + CM6206_REG2_DRIVER_ON | + CM6206_REG2_HEADP_SEL_FRONT_CHANNELS | + CM6206_REG2_MUTE_HEADPHONE_RIGHT | + CM6206_REG2_MUTE_HEADPHONE_LEFT, + /* + * REG3: default flyspeed, set 2.5V mic bias + * enable all line out ports and enable SPDIF + */ + CM6206_REG3_FLYSPEED_DEFAULT | + CM6206_REG3_VRAP25EN | + CM6206_REG3_FOE | + CM6206_REG3_ROE | + CM6206_REG3_CBOE | + CM6206_REG3_LOSE | + CM6206_REG3_HPOE | + CM6206_REG3_SPDIFI_CANREC, + /* REG4 is just a bunch of GPIO lines */ + 0x0000, + /* REG5: de-assert AD/DA reset signals */ + CM6206_REG5_DA_RSTN | + CM6206_REG5_AD_RSTN }; for (reg = 0; reg < ARRAY_SIZE(val); reg++) { err = snd_usb_cm106_write_int_reg(dev, reg, val[reg]); @@ -743,11 +860,13 @@ static int snd_usb_accessmusic_boot_quirk(struct usb_device *dev) { int err, actual_length; - /* "midi send" enable */ static const u8 seq[] = { 0x4e, 0x73, 0x52, 0x01 }; + void *buf; - void *buf = kmemdup(seq, ARRAY_SIZE(seq), GFP_KERNEL); + if (usb_pipe_type_check(dev, usb_sndintpipe(dev, 0x05))) + return -EINVAL; + buf = kmemdup(seq, ARRAY_SIZE(seq), GFP_KERNEL); if (!buf) return -ENOMEM; err = usb_interrupt_msg(dev, usb_sndintpipe(dev, 0x05), buf, @@ -772,7 +891,9 @@ static int snd_usb_nativeinstruments_boot_quirk(struct usb_device *dev) { - int ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + int ret; + + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0xaf, USB_TYPE_VENDOR | USB_RECIP_DEVICE, 1, 0, NULL, 0, 1000); @@ -903,20 +1024,146 @@ return 0; } + +#define MICROBOOK_BUF_SIZE 128 + +static int snd_usb_motu_microbookii_communicate(struct usb_device *dev, u8 *buf, + int buf_size, int *length) +{ + int err, actual_length; + + if (usb_pipe_type_check(dev, usb_sndintpipe(dev, 0x01))) + return -EINVAL; + err = usb_interrupt_msg(dev, usb_sndintpipe(dev, 0x01), buf, *length, + &actual_length, 1000); + if (err < 0) + return err; + + print_hex_dump(KERN_DEBUG, "MicroBookII snd: ", DUMP_PREFIX_NONE, 16, 1, + buf, actual_length, false); + + memset(buf, 0, buf_size); + + if (usb_pipe_type_check(dev, usb_rcvintpipe(dev, 0x82))) + return -EINVAL; + err = usb_interrupt_msg(dev, usb_rcvintpipe(dev, 0x82), buf, buf_size, + &actual_length, 1000); + if (err < 0) + return err; + + print_hex_dump(KERN_DEBUG, "MicroBookII rcv: ", DUMP_PREFIX_NONE, 16, 1, + buf, actual_length, false); + + *length = actual_length; + return 0; +} + +static int snd_usb_motu_microbookii_boot_quirk(struct usb_device *dev) +{ + int err, actual_length, poll_attempts = 0; + static const u8 set_samplerate_seq[] = { 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0b, 0x14, + 0x00, 0x00, 0x00, 0x01 }; + static const u8 poll_ready_seq[] = { 0x00, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x0b, 0x18 }; + u8 *buf = kzalloc(MICROBOOK_BUF_SIZE, GFP_KERNEL); + + if (!buf) + return -ENOMEM; + + dev_info(&dev->dev, "Waiting for MOTU Microbook II to boot up...\n"); + + /* First we tell the device which sample rate to use. */ + memcpy(buf, set_samplerate_seq, sizeof(set_samplerate_seq)); + actual_length = sizeof(set_samplerate_seq); + err = snd_usb_motu_microbookii_communicate(dev, buf, MICROBOOK_BUF_SIZE, + &actual_length); + + if (err < 0) { + dev_err(&dev->dev, + "failed setting the sample rate for Motu MicroBook II: %d\n", + err); + goto free_buf; + } + + /* Then we poll every 100 ms until the device informs of its readiness. */ + while (true) { + if (++poll_attempts > 100) { + dev_err(&dev->dev, + "failed booting Motu MicroBook II: timeout\n"); + err = -ENODEV; + goto free_buf; + } + + memset(buf, 0, MICROBOOK_BUF_SIZE); + memcpy(buf, poll_ready_seq, sizeof(poll_ready_seq)); + + actual_length = sizeof(poll_ready_seq); + err = snd_usb_motu_microbookii_communicate( + dev, buf, MICROBOOK_BUF_SIZE, &actual_length); + if (err < 0) { + dev_err(&dev->dev, + "failed booting Motu MicroBook II: communication error %d\n", + err); + goto free_buf; + } + + /* the device signals its readiness through a message of the + * form + * XX 06 00 00 00 00 0b 18 00 00 00 01 + * If the device is not yet ready to accept audio data, the + * last byte of that sequence is 00. + */ + if (actual_length == 12 && buf[actual_length - 1] == 1) + break; + + msleep(100); + } + + dev_info(&dev->dev, "MOTU MicroBook II ready\n"); + +free_buf: + kfree(buf); + return err; +} + +static int snd_usb_motu_m_series_boot_quirk(struct usb_device *dev) +{ + int ret; + + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 1, USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x0, 0, NULL, 0, 1000); + + if (ret < 0) + return ret; + + msleep(2000); + + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 1, USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x20, 0, NULL, 0, 1000); + + if (ret < 0) + return ret; + + return 0; +} + /* * Setup quirks */ #define MAUDIO_SET 0x01 /* parse device_setup */ #define MAUDIO_SET_COMPATIBLE 0x80 /* use only "win-compatible" interfaces */ #define MAUDIO_SET_DTS 0x02 /* enable DTS Digital Output */ -#define MAUDIO_SET_96K 0x04 /* 48-96KHz rate if set, 8-48KHz otherwise */ +#define MAUDIO_SET_96K 0x04 /* 48-96kHz rate if set, 8-48kHz otherwise */ #define MAUDIO_SET_24B 0x08 /* 24bits sample if set, 16bits otherwise */ #define MAUDIO_SET_DI 0x10 /* enable Digital Input */ #define MAUDIO_SET_MASK 0x1f /* bit mask for setup value */ -#define MAUDIO_SET_24B_48K_DI 0x19 /* 24bits+48KHz+Digital Input */ -#define MAUDIO_SET_24B_48K_NOTDI 0x09 /* 24bits+48KHz+No Digital Input */ -#define MAUDIO_SET_16B_48K_DI 0x11 /* 16bits+48KHz+Digital Input */ -#define MAUDIO_SET_16B_48K_NOTDI 0x01 /* 16bits+48KHz+No Digital Input */ +#define MAUDIO_SET_24B_48K_DI 0x19 /* 24bits+48kHz+Digital Input */ +#define MAUDIO_SET_24B_48K_NOTDI 0x09 /* 24bits+48kHz+No Digital Input */ +#define MAUDIO_SET_16B_48K_DI 0x11 /* 16bits+48kHz+Digital Input */ +#define MAUDIO_SET_16B_48K_NOTDI 0x01 /* 16bits+48kHz+No Digital Input */ static int quattro_skip_setting_quirk(struct snd_usb_audio *chip, int iface, int altno) @@ -1017,6 +1264,38 @@ return 0; /* keep this altsetting */ } +static int s1810c_skip_setting_quirk(struct snd_usb_audio *chip, + int iface, int altno) +{ + /* + * Altno settings: + * + * Playback (Interface 1): + * 1: 6 Analog + 2 S/PDIF + * 2: 6 Analog + 2 S/PDIF + * 3: 6 Analog + * + * Capture (Interface 2): + * 1: 8 Analog + 2 S/PDIF + 8 ADAT + * 2: 8 Analog + 2 S/PDIF + 4 ADAT + * 3: 8 Analog + */ + + /* + * I'll leave 2 as the default one and + * use device_setup to switch to the + * other two. + */ + if ((chip->setup == 0 || chip->setup > 2) && altno != 2) + return 1; + else if (chip->setup == 1 && altno != 1) + return 1; + else if (chip->setup == 2 && altno != 3) + return 1; + + return 0; +} + int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip, int iface, int altno) @@ -1030,6 +1309,10 @@ /* fasttrackpro usb: skip altsets incompatible with device_setup */ if (chip->usb_id == USB_ID(0x0763, 0x2012)) return fasttrackpro_skip_setting_quirk(chip, iface, altno); + /* presonus studio 1810c: skip altsets incompatible with device_setup */ + if (chip->usb_id == USB_ID(0x194f, 0x010c)) + return s1810c_skip_setting_quirk(chip, iface, altno); + return 0; } @@ -1080,6 +1363,29 @@ return snd_usb_gamecon780_boot_quirk(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 */ + /* + * 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; +} + +int snd_usb_apply_boot_quirk_once(struct usb_device *dev, + struct usb_interface *intf, + const struct snd_usb_audio_quirk *quirk, + unsigned int id) +{ + switch (id) { + case USB_ID(0x07fd, 0x0008): /* MOTU M Series */ + return snd_usb_motu_m_series_boot_quirk(dev); } return 0; @@ -1164,6 +1470,30 @@ subs->pkt_offset_adj = (emu_samplerate_id >= EMU_QUIRK_SR_176400HZ) ? 4 : 0; } + +/* + * Pioneer DJ DJM-900NXS2 + * Device needs to know the sample rate each time substream is started + */ +static int pioneer_djm_set_format_quirk(struct snd_usb_substream *subs) +{ + + /* Convert sample rate value to little endian */ + u8 sr[3]; + + sr[0] = subs->cur_rate & 0xff; + sr[1] = (subs->cur_rate >> 8) & 0xff; + sr[2] = (subs->cur_rate >> 16) & 0xff; + + /* Configure device */ + usb_set_interface(subs->dev, 0, 1); + snd_usb_ctl_msg(subs->stream->chip->dev, + usb_rcvctrlpipe(subs->stream->chip->dev, 0), + 0x01, 0x22, 0x0100, 0x0082, &sr, 0x0003); + + return 0; +} + void snd_usb_set_format_quirk(struct snd_usb_substream *subs, struct audioformat *fmt) { @@ -1174,6 +1504,14 @@ case USB_ID(0x041e, 0x3f19): /* E-Mu 0204 USB */ set_format_emu_quirk(subs, fmt); break; + case USB_ID(0x2b73, 0x000a): /* Pioneer DJ DJM-900NXS2 */ + case USB_ID(0x2b73, 0x0017): /* Pioneer DJ DJM-250MK2 */ + pioneer_djm_set_format_quirk(subs); + break; + case USB_ID(0x534d, 0x0021): /* MacroSilicon MS2100/MS2106 */ + case USB_ID(0x534d, 0x2109): /* MacroSilicon MS2109 */ + subs->stream_offset_adj = 2; + break; } } @@ -1181,18 +1519,15 @@ { /* devices which do not support reading the sample rate. */ switch (chip->usb_id) { - case USB_ID(0x041E, 0x4080): /* Creative Live Cam VF0610 */ - case USB_ID(0x04D8, 0xFEEA): /* Benchmark DAC1 Pre */ + case USB_ID(0x041e, 0x4080): /* Creative Live Cam VF0610 */ + case USB_ID(0x04d8, 0xfeea): /* Benchmark DAC1 Pre */ case USB_ID(0x0556, 0x0014): /* Phoenix Audio TMX320VC */ - case USB_ID(0x05A3, 0x9420): /* ELP HD USB Camera */ + case USB_ID(0x05a3, 0x9420): /* ELP HD USB Camera */ case USB_ID(0x05a7, 0x1020): /* Bose Companion 5 */ -#ifdef CONFIG_HID_RKVR - case USB_ID(0x071B, 0x3205): /* RockChip NanoC VR */ -#endif - case USB_ID(0x074D, 0x3553): /* Outlaw RR2150 (Micronas UAC3553B) */ + case USB_ID(0x074d, 0x3553): /* Outlaw RR2150 (Micronas UAC3553B) */ case USB_ID(0x1395, 0x740a): /* Sennheiser DECT */ case USB_ID(0x1901, 0x0191): /* GE B850V3 CP2114 audio interface */ - case USB_ID(0x21B4, 0x0081): /* AudioQuest DragonFly */ + case USB_ID(0x21b4, 0x0081): /* AudioQuest DragonFly */ case USB_ID(0x2912, 0x30c8): /* Audioengine D1 */ case USB_ID(0x413c, 0xa506): /* Dell AE515 sound bar */ case USB_ID(0x046d, 0x084c): /* Logitech ConferenceCam Connect */ @@ -1201,8 +1536,8 @@ /* devices of these vendors don't support reading rate, either */ switch (USB_ID_VENDOR(chip->usb_id)) { - case 0x045E: /* MS Lifecam */ - case 0x047F: /* Plantronics */ + case 0x045e: /* MS Lifecam */ + case 0x047f: /* Plantronics */ case 0x1de7: /* Phoenix Audio */ return true; } @@ -1281,7 +1616,7 @@ /* * M-Audio Fast Track C400/C600 - when packets are not skipped, real - * world latency varies by approx. +/- 50 frames (at 96KHz) each time + * world latency varies by approx. +/- 50 frames (at 96kHz) each time * the stream is (re)started. When skipping packets 16 at endpoint * start up, the real world latency is stable within +/- 1 frame (also * across power cycles). @@ -1409,6 +1744,7 @@ /* XMOS based USB DACs */ switch (chip->usb_id) { case USB_ID(0x1511, 0x0037): /* AURALiC VEGA */ + case USB_ID(0x21ed, 0xd75a): /* Accuphase DAC-60 option card */ case USB_ID(0x2522, 0x0012): /* LH Labs VI DAC Infinity */ case USB_ID(0x2772, 0x0230): /* Pro-Ject Pre Box S2 Digital */ if (fp->altsetting == 2) @@ -1504,9 +1840,6 @@ int stream) { switch (chip->usb_id) { -#ifdef CONFIG_HID_RKVR - case USB_ID(0x071B, 0x3205): /* RockChip NanoC VR */ -#endif case USB_ID(0x0a92, 0x0053): /* AudioTrak Optoplay */ /* Optoplay sets the sample rate attribute although * it seems not supporting it in fact. @@ -1532,6 +1865,14 @@ fp->ep_attr |= USB_ENDPOINT_SYNC_ADAPTIVE; 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; } } @@ -1572,7 +1913,7 @@ for (q = registration_quirks; q->usb_id; q++) if (chip->usb_id == q->usb_id) - return iface != q->interface; + return iface < q->interface; /* Register as normal */ return false; -- Gitblit v1.6.2