2019-05-27 14:55:05 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2010-03-05 02:46:13 +08:00
|
|
|
/*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/init.h>
|
2010-03-29 13:02:50 +08:00
|
|
|
#include <linux/slab.h>
|
2010-03-05 02:46:13 +08:00
|
|
|
#include <linux/usb.h>
|
|
|
|
#include <linux/usb/audio.h>
|
2013-04-01 05:43:12 +08:00
|
|
|
#include <linux/usb/midi.h>
|
2018-11-03 05:11:33 +08:00
|
|
|
#include <linux/bits.h>
|
2010-03-05 02:46:13 +08:00
|
|
|
|
2011-05-25 15:09:01 +08:00
|
|
|
#include <sound/control.h>
|
2010-03-05 02:46:13 +08:00
|
|
|
#include <sound/core.h>
|
|
|
|
#include <sound/info.h>
|
|
|
|
#include <sound/pcm.h>
|
|
|
|
|
|
|
|
#include "usbaudio.h"
|
|
|
|
#include "card.h"
|
2010-03-12 04:13:23 +08:00
|
|
|
#include "mixer.h"
|
2010-03-12 04:13:22 +08:00
|
|
|
#include "mixer_quirks.h"
|
2010-03-05 02:46:13 +08:00
|
|
|
#include "midi.h"
|
|
|
|
#include "quirks.h"
|
|
|
|
#include "helper.h"
|
|
|
|
#include "endpoint.h"
|
|
|
|
#include "pcm.h"
|
2010-06-16 23:57:31 +08:00
|
|
|
#include "clock.h"
|
2011-09-13 00:54:12 +08:00
|
|
|
#include "stream.h"
|
2010-03-05 02:46:13 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* handle the quirks for the contained interfaces
|
|
|
|
*/
|
|
|
|
static int create_composite_quirk(struct snd_usb_audio *chip,
|
|
|
|
struct usb_interface *iface,
|
|
|
|
struct usb_driver *driver,
|
2014-11-10 14:41:59 +08:00
|
|
|
const struct snd_usb_audio_quirk *quirk_comp)
|
2010-03-05 02:46:13 +08:00
|
|
|
{
|
|
|
|
int probed_ifnum = get_iface_desc(iface->altsetting)->bInterfaceNumber;
|
2014-11-10 14:41:59 +08:00
|
|
|
const struct snd_usb_audio_quirk *quirk;
|
2010-03-05 02:46:13 +08:00
|
|
|
int err;
|
|
|
|
|
2014-11-10 14:41:59 +08:00
|
|
|
for (quirk = quirk_comp->data; quirk->ifnum >= 0; ++quirk) {
|
2010-03-05 02:46:13 +08:00
|
|
|
iface = usb_ifnum_to_if(chip->dev, quirk->ifnum);
|
|
|
|
if (!iface)
|
|
|
|
continue;
|
|
|
|
if (quirk->ifnum != probed_ifnum &&
|
|
|
|
usb_interface_claimed(iface))
|
|
|
|
continue;
|
|
|
|
err = snd_usb_create_quirk(chip, iface, driver, quirk);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
2014-11-10 01:21:23 +08:00
|
|
|
}
|
|
|
|
|
2014-11-10 14:41:59 +08:00
|
|
|
for (quirk = quirk_comp->data; quirk->ifnum >= 0; ++quirk) {
|
2014-11-10 01:21:23 +08:00
|
|
|
iface = usb_ifnum_to_if(chip->dev, quirk->ifnum);
|
|
|
|
if (!iface)
|
|
|
|
continue;
|
|
|
|
if (quirk->ifnum != probed_ifnum &&
|
|
|
|
!usb_interface_claimed(iface))
|
2010-03-05 02:46:13 +08:00
|
|
|
usb_driver_claim_interface(driver, iface, (void *)-1L);
|
|
|
|
}
|
2014-11-10 01:21:23 +08:00
|
|
|
|
2010-03-05 02:46:13 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ignore_interface_quirk(struct snd_usb_audio *chip,
|
|
|
|
struct usb_interface *iface,
|
|
|
|
struct usb_driver *driver,
|
|
|
|
const struct snd_usb_audio_quirk *quirk)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allow alignment on audio sub-slot (channel samples) rather than
|
|
|
|
* on audio slots (audio frames)
|
|
|
|
*/
|
|
|
|
static int create_align_transfer_quirk(struct snd_usb_audio *chip,
|
|
|
|
struct usb_interface *iface,
|
|
|
|
struct usb_driver *driver,
|
|
|
|
const struct snd_usb_audio_quirk *quirk)
|
|
|
|
{
|
|
|
|
chip->txfr_quirk = 1;
|
|
|
|
return 1; /* Continue with creating streams and mixer */
|
|
|
|
}
|
|
|
|
|
|
|
|
static int create_any_midi_quirk(struct snd_usb_audio *chip,
|
|
|
|
struct usb_interface *intf,
|
|
|
|
struct usb_driver *driver,
|
|
|
|
const struct snd_usb_audio_quirk *quirk)
|
|
|
|
{
|
|
|
|
return snd_usbmidi_create(chip->card, intf, &chip->midi_list, quirk);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* create a stream for an interface with proper descriptors
|
|
|
|
*/
|
|
|
|
static int create_standard_audio_quirk(struct snd_usb_audio *chip,
|
|
|
|
struct usb_interface *iface,
|
|
|
|
struct usb_driver *driver,
|
|
|
|
const struct snd_usb_audio_quirk *quirk)
|
|
|
|
{
|
|
|
|
struct usb_host_interface *alts;
|
|
|
|
struct usb_interface_descriptor *altsd;
|
|
|
|
int err;
|
|
|
|
|
ALSA: USB-audio: Add quirk for Zoom R16/24 playback
The Zoom R16/24 have a nonstandard playback format where each isochronous
packet contains a length descriptor in the first four bytes. (Curiously,
capture data does not contain this and requires no quirk.)
The quirk involves adding the extra length descriptor whenever outgoing
isochronous packets are generated, both in pcm.c (outgoing audio) and
endpoint.c (silent data).
In order to make the quirk as unintrusive as possible, for
pcm.c:prepare_playback_urb(), the isochronous packet descriptors are
initially set up in the same way no matter if the quirk is enabled or not.
Once it is time to actually copy the data into the outgoing packet buffer
(together with the added length descriptors) the isochronous descriptors
are adjusted in order take the increased payload length into account.
For endpoint.c:prepare_silent_urb() it makes more sense to modify the
actual function, partly because the function is less complex to start with
and partly because it is not as time-critical as prepare_playback_urb()
(whose bulk is run with interrupts disabled), so the (minute) additional
time spent in the non-quirk case is motivated by the simplicity of having
a single function for all cases.
The quirk is controlled by the new tx_length_quirk member in struct
snd_usb_substream and struct snd_usb_audio, which is conveyed to pcm.c
and endpoint.c from quirks.c in a similar manner to the txfr_quirk member
in the same structs.
In contrast to txfr_quirk however, the quirk is enabled directly in
quirks.c:create_standard_audio_quirk() by checking the USB ID in that
function. Another option would be to introduce a new
QUIRK_AUDIO_ZOOM_INTERFACE or somesuch, which would have made the quirk
very plain to see in the quirk table, but it was felt that the additional
code needed to implement it this way would just make the implementation
more complex with no real gain.
Tested with a Zoom R16, both by doing capture and playback separately
using arecord and aplay (8 channel capture and 2 channel playback,
respectively), as well as capture and playback together using Ardour, as
well as Audacity and Qtractor together with jackd.
The R24 is reportedly compatible with the R16 when used as an audio
interface. Both devices share the same USB ID and have the same number of
inputs (8) and outputs (2). Therefore "R16/24" is mentioned throughout the
patch.
Regression tested using an Edirol UA-5 in both class compliant (16-bit)
and "advanced" (24 bit, forces the use of quirks) modes.
Signed-off-by: Ricard Wanderlof <ricardw@axis.com>
Tested-by: Panu Matilainen <pmatilai@laiskiainen.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2015-10-19 14:52:53 +08:00
|
|
|
if (chip->usb_id == USB_ID(0x1686, 0x00dd)) /* Zoom R16/24 */
|
|
|
|
chip->tx_length_quirk = 1;
|
|
|
|
|
2010-03-05 02:46:13 +08:00
|
|
|
alts = &iface->altsetting[0];
|
|
|
|
altsd = get_iface_desc(alts);
|
2011-09-13 00:54:12 +08:00
|
|
|
err = snd_usb_parse_audio_interface(chip, altsd->bInterfaceNumber);
|
2010-03-05 02:46:13 +08:00
|
|
|
if (err < 0) {
|
2014-02-26 20:02:17 +08:00
|
|
|
usb_audio_err(chip, "cannot setup if %d: error %d\n",
|
2010-03-05 02:46:13 +08:00
|
|
|
altsd->bInterfaceNumber, err);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
/* reset the current interface */
|
|
|
|
usb_set_interface(chip->dev, altsd->bInterfaceNumber, 0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* create a stream for an endpoint/altsetting without proper descriptors
|
|
|
|
*/
|
|
|
|
static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
|
|
|
|
struct usb_interface *iface,
|
|
|
|
struct usb_driver *driver,
|
|
|
|
const struct snd_usb_audio_quirk *quirk)
|
|
|
|
{
|
|
|
|
struct audioformat *fp;
|
|
|
|
struct usb_host_interface *alts;
|
2013-07-10 02:36:15 +08:00
|
|
|
struct usb_interface_descriptor *altsd;
|
2010-03-05 02:46:13 +08:00
|
|
|
int stream, err;
|
|
|
|
unsigned *rate_table = NULL;
|
|
|
|
|
|
|
|
fp = kmemdup(quirk->data, sizeof(*fp), GFP_KERNEL);
|
2017-08-12 00:38:25 +08:00
|
|
|
if (!fp)
|
2010-03-05 02:46:13 +08:00
|
|
|
return -ENOMEM;
|
2017-08-12 00:38:25 +08:00
|
|
|
|
2016-04-01 00:05:43 +08:00
|
|
|
INIT_LIST_HEAD(&fp->list);
|
2012-02-14 18:18:48 +08:00
|
|
|
if (fp->nr_rates > MAX_NR_RATES) {
|
|
|
|
kfree(fp);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2010-03-05 02:46:13 +08:00
|
|
|
if (fp->nr_rates > 0) {
|
2011-11-11 02:38:43 +08:00
|
|
|
rate_table = kmemdup(fp->rate_table,
|
|
|
|
sizeof(int) * fp->nr_rates, GFP_KERNEL);
|
2010-03-05 02:46:13 +08:00
|
|
|
if (!rate_table) {
|
|
|
|
kfree(fp);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
fp->rate_table = rate_table;
|
|
|
|
}
|
|
|
|
|
|
|
|
stream = (fp->endpoint & USB_DIR_IN)
|
|
|
|
? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
|
2011-09-13 00:54:12 +08:00
|
|
|
err = snd_usb_add_audio_stream(chip, stream, fp);
|
2016-03-15 19:14:49 +08:00
|
|
|
if (err < 0)
|
|
|
|
goto error;
|
2010-03-05 02:46:13 +08:00
|
|
|
if (fp->iface != get_iface_desc(&iface->altsetting[0])->bInterfaceNumber ||
|
|
|
|
fp->altset_idx >= iface->num_altsetting) {
|
2016-03-15 19:14:49 +08:00
|
|
|
err = -EINVAL;
|
|
|
|
goto error;
|
2010-03-05 02:46:13 +08:00
|
|
|
}
|
|
|
|
alts = &iface->altsetting[fp->altset_idx];
|
2013-07-10 02:36:15 +08:00
|
|
|
altsd = get_iface_desc(alts);
|
2016-03-15 19:09:10 +08:00
|
|
|
if (altsd->bNumEndpoints < 1) {
|
2016-03-15 19:14:49 +08:00
|
|
|
err = -EINVAL;
|
|
|
|
goto error;
|
2016-03-15 19:09:10 +08:00
|
|
|
}
|
|
|
|
|
2013-07-10 02:36:15 +08:00
|
|
|
fp->protocol = altsd->bInterfaceProtocol;
|
|
|
|
|
2013-03-17 19:07:54 +08:00
|
|
|
if (fp->datainterval == 0)
|
|
|
|
fp->datainterval = snd_usb_parse_datainterval(chip, alts);
|
|
|
|
if (fp->maxpacksize == 0)
|
|
|
|
fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
|
2010-03-05 02:46:13 +08:00
|
|
|
usb_set_interface(chip->dev, fp->iface, 0);
|
2010-03-05 02:46:17 +08:00
|
|
|
snd_usb_init_pitch(chip, fp->iface, alts, fp);
|
|
|
|
snd_usb_init_sample_rate(chip, fp->iface, alts, fp, fp->rate_max);
|
2010-03-05 02:46:13 +08:00
|
|
|
return 0;
|
2016-03-15 19:14:49 +08:00
|
|
|
|
|
|
|
error:
|
2016-04-01 00:05:43 +08:00
|
|
|
list_del(&fp->list); /* unlink for avoiding double-free */
|
2016-03-15 19:14:49 +08:00
|
|
|
kfree(fp);
|
|
|
|
kfree(rate_table);
|
|
|
|
return err;
|
2010-03-05 02:46:13 +08:00
|
|
|
}
|
|
|
|
|
2013-04-01 05:43:12 +08:00
|
|
|
static int create_auto_pcm_quirk(struct snd_usb_audio *chip,
|
|
|
|
struct usb_interface *iface,
|
|
|
|
struct usb_driver *driver)
|
|
|
|
{
|
|
|
|
struct usb_host_interface *alts;
|
|
|
|
struct usb_interface_descriptor *altsd;
|
|
|
|
struct usb_endpoint_descriptor *epd;
|
|
|
|
struct uac1_as_header_descriptor *ashd;
|
|
|
|
struct uac_format_type_i_discrete_descriptor *fmtd;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Most Roland/Yamaha audio streaming interfaces have more or less
|
|
|
|
* standard descriptors, but older devices might lack descriptors, and
|
|
|
|
* future ones might change, so ensure that we fail silently if the
|
|
|
|
* interface doesn't look exactly right.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* must have a non-zero altsetting for streaming */
|
|
|
|
if (iface->num_altsetting < 2)
|
|
|
|
return -ENODEV;
|
|
|
|
alts = &iface->altsetting[1];
|
|
|
|
altsd = get_iface_desc(alts);
|
|
|
|
|
|
|
|
/* must have an isochronous endpoint for streaming */
|
|
|
|
if (altsd->bNumEndpoints < 1)
|
|
|
|
return -ENODEV;
|
|
|
|
epd = get_endpoint(alts, 0);
|
|
|
|
if (!usb_endpoint_xfer_isoc(epd))
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
/* must have format descriptors */
|
|
|
|
ashd = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL,
|
|
|
|
UAC_AS_GENERAL);
|
|
|
|
fmtd = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL,
|
|
|
|
UAC_FORMAT_TYPE);
|
|
|
|
if (!ashd || ashd->bLength < 7 ||
|
|
|
|
!fmtd || fmtd->bLength < 8)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
return create_standard_audio_quirk(chip, iface, driver, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int create_yamaha_midi_quirk(struct snd_usb_audio *chip,
|
|
|
|
struct usb_interface *iface,
|
|
|
|
struct usb_driver *driver,
|
|
|
|
struct usb_host_interface *alts)
|
|
|
|
{
|
|
|
|
static const struct snd_usb_audio_quirk yamaha_midi_quirk = {
|
|
|
|
.type = QUIRK_MIDI_YAMAHA
|
|
|
|
};
|
|
|
|
struct usb_midi_in_jack_descriptor *injd;
|
|
|
|
struct usb_midi_out_jack_descriptor *outjd;
|
|
|
|
|
|
|
|
/* must have some valid jack descriptors */
|
|
|
|
injd = snd_usb_find_csint_desc(alts->extra, alts->extralen,
|
|
|
|
NULL, USB_MS_MIDI_IN_JACK);
|
|
|
|
outjd = snd_usb_find_csint_desc(alts->extra, alts->extralen,
|
|
|
|
NULL, USB_MS_MIDI_OUT_JACK);
|
|
|
|
if (!injd && !outjd)
|
|
|
|
return -ENODEV;
|
2019-11-13 19:12:59 +08:00
|
|
|
if ((injd && !snd_usb_validate_midi_desc(injd)) ||
|
|
|
|
(outjd && !snd_usb_validate_midi_desc(outjd)))
|
2019-08-20 23:17:09 +08:00
|
|
|
return -ENODEV;
|
2013-04-01 05:43:12 +08:00
|
|
|
if (injd && (injd->bLength < 5 ||
|
|
|
|
(injd->bJackType != USB_MS_EMBEDDED &&
|
|
|
|
injd->bJackType != USB_MS_EXTERNAL)))
|
|
|
|
return -ENODEV;
|
|
|
|
if (outjd && (outjd->bLength < 6 ||
|
|
|
|
(outjd->bJackType != USB_MS_EMBEDDED &&
|
|
|
|
outjd->bJackType != USB_MS_EXTERNAL)))
|
|
|
|
return -ENODEV;
|
|
|
|
return create_any_midi_quirk(chip, iface, driver, &yamaha_midi_quirk);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int create_roland_midi_quirk(struct snd_usb_audio *chip,
|
|
|
|
struct usb_interface *iface,
|
|
|
|
struct usb_driver *driver,
|
|
|
|
struct usb_host_interface *alts)
|
|
|
|
{
|
|
|
|
static const struct snd_usb_audio_quirk roland_midi_quirk = {
|
|
|
|
.type = QUIRK_MIDI_ROLAND
|
|
|
|
};
|
|
|
|
u8 *roland_desc = NULL;
|
|
|
|
|
|
|
|
/* might have a vendor-specific descriptor <06 24 F1 02 ...> */
|
|
|
|
for (;;) {
|
|
|
|
roland_desc = snd_usb_find_csint_desc(alts->extra,
|
|
|
|
alts->extralen,
|
|
|
|
roland_desc, 0xf1);
|
|
|
|
if (!roland_desc)
|
|
|
|
return -ENODEV;
|
|
|
|
if (roland_desc[0] < 6 || roland_desc[3] != 2)
|
|
|
|
continue;
|
|
|
|
return create_any_midi_quirk(chip, iface, driver,
|
|
|
|
&roland_midi_quirk);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int create_std_midi_quirk(struct snd_usb_audio *chip,
|
|
|
|
struct usb_interface *iface,
|
|
|
|
struct usb_driver *driver,
|
|
|
|
struct usb_host_interface *alts)
|
|
|
|
{
|
|
|
|
struct usb_ms_header_descriptor *mshd;
|
|
|
|
struct usb_ms_endpoint_descriptor *msepd;
|
|
|
|
|
|
|
|
/* must have the MIDIStreaming interface header descriptor*/
|
|
|
|
mshd = (struct usb_ms_header_descriptor *)alts->extra;
|
|
|
|
if (alts->extralen < 7 ||
|
|
|
|
mshd->bLength < 7 ||
|
|
|
|
mshd->bDescriptorType != USB_DT_CS_INTERFACE ||
|
|
|
|
mshd->bDescriptorSubtype != USB_MS_HEADER)
|
|
|
|
return -ENODEV;
|
|
|
|
/* must have the MIDIStreaming endpoint descriptor*/
|
|
|
|
msepd = (struct usb_ms_endpoint_descriptor *)alts->endpoint[0].extra;
|
|
|
|
if (alts->endpoint[0].extralen < 4 ||
|
|
|
|
msepd->bLength < 4 ||
|
|
|
|
msepd->bDescriptorType != USB_DT_CS_ENDPOINT ||
|
|
|
|
msepd->bDescriptorSubtype != UAC_MS_GENERAL ||
|
|
|
|
msepd->bNumEmbMIDIJack < 1 ||
|
|
|
|
msepd->bNumEmbMIDIJack > 16)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
return create_any_midi_quirk(chip, iface, driver, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int create_auto_midi_quirk(struct snd_usb_audio *chip,
|
|
|
|
struct usb_interface *iface,
|
|
|
|
struct usb_driver *driver)
|
|
|
|
{
|
|
|
|
struct usb_host_interface *alts;
|
|
|
|
struct usb_interface_descriptor *altsd;
|
|
|
|
struct usb_endpoint_descriptor *epd;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
alts = &iface->altsetting[0];
|
|
|
|
altsd = get_iface_desc(alts);
|
|
|
|
|
|
|
|
/* must have at least one bulk/interrupt endpoint for streaming */
|
|
|
|
if (altsd->bNumEndpoints < 1)
|
|
|
|
return -ENODEV;
|
|
|
|
epd = get_endpoint(alts, 0);
|
2013-08-11 20:13:13 +08:00
|
|
|
if (!usb_endpoint_xfer_bulk(epd) &&
|
2013-04-01 05:43:12 +08:00
|
|
|
!usb_endpoint_xfer_int(epd))
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
switch (USB_ID_VENDOR(chip->usb_id)) {
|
|
|
|
case 0x0499: /* Yamaha */
|
|
|
|
err = create_yamaha_midi_quirk(chip, iface, driver, alts);
|
2013-08-11 20:13:13 +08:00
|
|
|
if (err != -ENODEV)
|
2013-04-01 05:43:12 +08:00
|
|
|
return err;
|
|
|
|
break;
|
|
|
|
case 0x0582: /* Roland */
|
|
|
|
err = create_roland_midi_quirk(chip, iface, driver, alts);
|
2013-08-11 20:13:13 +08:00
|
|
|
if (err != -ENODEV)
|
2013-04-01 05:43:12 +08:00
|
|
|
return err;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return create_std_midi_quirk(chip, iface, driver, alts);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int create_autodetect_quirk(struct snd_usb_audio *chip,
|
|
|
|
struct usb_interface *iface,
|
2013-04-05 03:43:57 +08:00
|
|
|
struct usb_driver *driver)
|
2013-04-01 05:43:12 +08:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = create_auto_pcm_quirk(chip, iface, driver);
|
|
|
|
if (err == -ENODEV)
|
|
|
|
err = create_auto_midi_quirk(chip, iface, driver);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2013-04-05 03:43:57 +08:00
|
|
|
static int create_autodetect_quirks(struct snd_usb_audio *chip,
|
|
|
|
struct usb_interface *iface,
|
|
|
|
struct usb_driver *driver,
|
|
|
|
const struct snd_usb_audio_quirk *quirk)
|
|
|
|
{
|
|
|
|
int probed_ifnum = get_iface_desc(iface->altsetting)->bInterfaceNumber;
|
|
|
|
int ifcount, ifnum, err;
|
|
|
|
|
|
|
|
err = create_autodetect_quirk(chip, iface, driver);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ALSA PCM playback/capture devices cannot be registered in two steps,
|
|
|
|
* so we have to claim the other corresponding interface here.
|
|
|
|
*/
|
|
|
|
ifcount = chip->dev->actconfig->desc.bNumInterfaces;
|
|
|
|
for (ifnum = 0; ifnum < ifcount; ifnum++) {
|
|
|
|
if (ifnum == probed_ifnum || quirk->ifnum >= 0)
|
|
|
|
continue;
|
|
|
|
iface = usb_ifnum_to_if(chip->dev, ifnum);
|
|
|
|
if (!iface ||
|
|
|
|
usb_interface_claimed(iface) ||
|
|
|
|
get_iface_desc(iface->altsetting)->bInterfaceClass !=
|
|
|
|
USB_CLASS_VENDOR_SPEC)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
err = create_autodetect_quirk(chip, iface, driver);
|
|
|
|
if (err >= 0)
|
|
|
|
usb_driver_claim_interface(driver, iface, (void *)-1L);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-03-05 02:46:13 +08:00
|
|
|
/*
|
|
|
|
* Create a stream for an Edirol UA-700/UA-25/UA-4FX interface.
|
|
|
|
* The only way to detect the sample rate is by looking at wMaxPacketSize.
|
|
|
|
*/
|
|
|
|
static int create_uaxx_quirk(struct snd_usb_audio *chip,
|
|
|
|
struct usb_interface *iface,
|
|
|
|
struct usb_driver *driver,
|
|
|
|
const struct snd_usb_audio_quirk *quirk)
|
|
|
|
{
|
|
|
|
static const struct audioformat ua_format = {
|
2010-03-05 02:46:15 +08:00
|
|
|
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
|
2010-03-05 02:46:13 +08:00
|
|
|
.channels = 2,
|
|
|
|
.fmt_type = UAC_FORMAT_TYPE_I,
|
|
|
|
.altsetting = 1,
|
|
|
|
.altset_idx = 1,
|
|
|
|
.rates = SNDRV_PCM_RATE_CONTINUOUS,
|
|
|
|
};
|
|
|
|
struct usb_host_interface *alts;
|
|
|
|
struct usb_interface_descriptor *altsd;
|
|
|
|
struct audioformat *fp;
|
|
|
|
int stream, err;
|
|
|
|
|
|
|
|
/* both PCM and MIDI interfaces have 2 or more altsettings */
|
|
|
|
if (iface->num_altsetting < 2)
|
|
|
|
return -ENXIO;
|
|
|
|
alts = &iface->altsetting[1];
|
|
|
|
altsd = get_iface_desc(alts);
|
|
|
|
|
|
|
|
if (altsd->bNumEndpoints == 2) {
|
|
|
|
static const struct snd_usb_midi_endpoint_info ua700_ep = {
|
|
|
|
.out_cables = 0x0003,
|
|
|
|
.in_cables = 0x0003
|
|
|
|
};
|
|
|
|
static const struct snd_usb_audio_quirk ua700_quirk = {
|
|
|
|
.type = QUIRK_MIDI_FIXED_ENDPOINT,
|
|
|
|
.data = &ua700_ep
|
|
|
|
};
|
|
|
|
static const struct snd_usb_midi_endpoint_info uaxx_ep = {
|
|
|
|
.out_cables = 0x0001,
|
|
|
|
.in_cables = 0x0001
|
|
|
|
};
|
|
|
|
static const struct snd_usb_audio_quirk uaxx_quirk = {
|
|
|
|
.type = QUIRK_MIDI_FIXED_ENDPOINT,
|
|
|
|
.data = &uaxx_ep
|
|
|
|
};
|
|
|
|
const struct snd_usb_audio_quirk *quirk =
|
|
|
|
chip->usb_id == USB_ID(0x0582, 0x002b)
|
|
|
|
? &ua700_quirk : &uaxx_quirk;
|
2016-01-11 18:33:34 +08:00
|
|
|
return __snd_usbmidi_create(chip->card, iface,
|
|
|
|
&chip->midi_list, quirk,
|
|
|
|
chip->usb_id);
|
2010-03-05 02:46:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (altsd->bNumEndpoints != 1)
|
|
|
|
return -ENXIO;
|
|
|
|
|
2011-11-11 02:38:43 +08:00
|
|
|
fp = kmemdup(&ua_format, sizeof(*fp), GFP_KERNEL);
|
2010-03-05 02:46:13 +08:00
|
|
|
if (!fp)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
fp->iface = altsd->bInterfaceNumber;
|
|
|
|
fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
|
|
|
|
fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
|
|
|
|
fp->datainterval = 0;
|
|
|
|
fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
|
2016-04-01 00:05:43 +08:00
|
|
|
INIT_LIST_HEAD(&fp->list);
|
2010-03-05 02:46:13 +08:00
|
|
|
|
|
|
|
switch (fp->maxpacksize) {
|
|
|
|
case 0x120:
|
|
|
|
fp->rate_max = fp->rate_min = 44100;
|
|
|
|
break;
|
|
|
|
case 0x138:
|
|
|
|
case 0x140:
|
|
|
|
fp->rate_max = fp->rate_min = 48000;
|
|
|
|
break;
|
|
|
|
case 0x258:
|
|
|
|
case 0x260:
|
|
|
|
fp->rate_max = fp->rate_min = 96000;
|
|
|
|
break;
|
|
|
|
default:
|
2014-02-26 20:02:17 +08:00
|
|
|
usb_audio_err(chip, "unknown sample rate\n");
|
2010-03-05 02:46:13 +08:00
|
|
|
kfree(fp);
|
|
|
|
return -ENXIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
stream = (fp->endpoint & USB_DIR_IN)
|
|
|
|
? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
|
2011-09-13 00:54:12 +08:00
|
|
|
err = snd_usb_add_audio_stream(chip, stream, fp);
|
2010-03-05 02:46:13 +08:00
|
|
|
if (err < 0) {
|
2016-04-01 00:05:43 +08:00
|
|
|
list_del(&fp->list); /* unlink for avoiding double-free */
|
2010-03-05 02:46:13 +08:00
|
|
|
kfree(fp);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
usb_set_interface(chip->dev, fp->iface, 0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-05-25 15:09:02 +08:00
|
|
|
/*
|
|
|
|
* Create a standard mixer for the specified interface.
|
|
|
|
*/
|
|
|
|
static int create_standard_mixer_quirk(struct snd_usb_audio *chip,
|
|
|
|
struct usb_interface *iface,
|
|
|
|
struct usb_driver *driver,
|
|
|
|
const struct snd_usb_audio_quirk *quirk)
|
|
|
|
{
|
|
|
|
if (quirk->ifnum < 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return snd_usb_create_mixer(chip, quirk->ifnum, 0);
|
|
|
|
}
|
|
|
|
|
2019-12-18 21:26:50 +08:00
|
|
|
|
|
|
|
static int setup_fmt_after_resume_quirk(struct snd_usb_audio *chip,
|
|
|
|
struct usb_interface *iface,
|
|
|
|
struct usb_driver *driver,
|
|
|
|
const struct snd_usb_audio_quirk *quirk)
|
|
|
|
{
|
|
|
|
chip->setup_fmt_after_resume_quirk = 1;
|
|
|
|
return 1; /* Continue with creating streams and mixer */
|
|
|
|
}
|
|
|
|
|
2010-03-05 02:46:13 +08:00
|
|
|
/*
|
|
|
|
* audio-interface quirks
|
|
|
|
*
|
|
|
|
* returns zero if no standard audio/MIDI parsing is needed.
|
2011-03-31 09:57:33 +08:00
|
|
|
* returns a positive value if standard audio/midi interfaces are parsed
|
2010-03-05 02:46:13 +08:00
|
|
|
* after this.
|
|
|
|
* returns a negative value at error.
|
|
|
|
*/
|
|
|
|
int snd_usb_create_quirk(struct snd_usb_audio *chip,
|
|
|
|
struct usb_interface *iface,
|
|
|
|
struct usb_driver *driver,
|
|
|
|
const struct snd_usb_audio_quirk *quirk)
|
|
|
|
{
|
|
|
|
typedef int (*quirk_func_t)(struct snd_usb_audio *,
|
|
|
|
struct usb_interface *,
|
|
|
|
struct usb_driver *,
|
|
|
|
const struct snd_usb_audio_quirk *);
|
|
|
|
static const quirk_func_t quirk_funcs[] = {
|
|
|
|
[QUIRK_IGNORE_INTERFACE] = ignore_interface_quirk,
|
|
|
|
[QUIRK_COMPOSITE] = create_composite_quirk,
|
2013-04-05 03:43:57 +08:00
|
|
|
[QUIRK_AUTODETECT] = create_autodetect_quirks,
|
2010-03-05 02:46:13 +08:00
|
|
|
[QUIRK_MIDI_STANDARD_INTERFACE] = create_any_midi_quirk,
|
|
|
|
[QUIRK_MIDI_FIXED_ENDPOINT] = create_any_midi_quirk,
|
|
|
|
[QUIRK_MIDI_YAMAHA] = create_any_midi_quirk,
|
2013-04-01 05:43:12 +08:00
|
|
|
[QUIRK_MIDI_ROLAND] = create_any_midi_quirk,
|
2010-03-05 02:46:13 +08:00
|
|
|
[QUIRK_MIDI_MIDIMAN] = create_any_midi_quirk,
|
|
|
|
[QUIRK_MIDI_NOVATION] = create_any_midi_quirk,
|
2010-10-23 00:20:48 +08:00
|
|
|
[QUIRK_MIDI_RAW_BYTES] = create_any_midi_quirk,
|
2010-03-05 02:46:13 +08:00
|
|
|
[QUIRK_MIDI_EMAGIC] = create_any_midi_quirk,
|
|
|
|
[QUIRK_MIDI_CME] = create_any_midi_quirk,
|
2010-05-21 03:31:10 +08:00
|
|
|
[QUIRK_MIDI_AKAI] = create_any_midi_quirk,
|
2011-08-26 19:19:49 +08:00
|
|
|
[QUIRK_MIDI_FTDI] = create_any_midi_quirk,
|
2015-11-16 05:38:29 +08:00
|
|
|
[QUIRK_MIDI_CH345] = create_any_midi_quirk,
|
2010-03-05 02:46:13 +08:00
|
|
|
[QUIRK_AUDIO_STANDARD_INTERFACE] = create_standard_audio_quirk,
|
|
|
|
[QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk,
|
|
|
|
[QUIRK_AUDIO_EDIROL_UAXX] = create_uaxx_quirk,
|
2011-05-25 15:09:02 +08:00
|
|
|
[QUIRK_AUDIO_ALIGN_TRANSFER] = create_align_transfer_quirk,
|
|
|
|
[QUIRK_AUDIO_STANDARD_MIXER] = create_standard_mixer_quirk,
|
2019-12-18 21:26:50 +08:00
|
|
|
[QUIRK_SETUP_FMT_AFTER_RESUME] = setup_fmt_after_resume_quirk,
|
2010-03-05 02:46:13 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
if (quirk->type < QUIRK_TYPE_COUNT) {
|
|
|
|
return quirk_funcs[quirk->type](chip, iface, driver, quirk);
|
|
|
|
} else {
|
2014-02-26 20:02:17 +08:00
|
|
|
usb_audio_err(chip, "invalid quirk type %d\n", quirk->type);
|
2010-03-05 02:46:13 +08:00
|
|
|
return -ENXIO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* boot quirks
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define EXTIGY_FIRMWARE_SIZE_OLD 794
|
|
|
|
#define EXTIGY_FIRMWARE_SIZE_NEW 483
|
|
|
|
|
|
|
|
static int snd_usb_extigy_boot_quirk(struct usb_device *dev, struct usb_interface *intf)
|
|
|
|
{
|
|
|
|
struct usb_host_config *config = dev->actconfig;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (le16_to_cpu(get_cfg_desc(config)->wTotalLength) == EXTIGY_FIRMWARE_SIZE_OLD ||
|
|
|
|
le16_to_cpu(get_cfg_desc(config)->wTotalLength) == EXTIGY_FIRMWARE_SIZE_NEW) {
|
2014-02-26 20:02:17 +08:00
|
|
|
dev_dbg(&dev->dev, "sending Extigy boot sequence...\n");
|
2010-03-05 02:46:13 +08:00
|
|
|
/* Send message to force it to reconnect with full interface. */
|
|
|
|
err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev,0),
|
2011-09-27 03:15:27 +08:00
|
|
|
0x10, 0x43, 0x0001, 0x000a, NULL, 0);
|
2014-02-26 20:02:17 +08:00
|
|
|
if (err < 0)
|
|
|
|
dev_dbg(&dev->dev, "error sending boot message: %d\n", err);
|
2010-03-05 02:46:13 +08:00
|
|
|
err = usb_get_descriptor(dev, USB_DT_DEVICE, 0,
|
|
|
|
&dev->descriptor, sizeof(dev->descriptor));
|
|
|
|
config = dev->actconfig;
|
2014-02-26 20:02:17 +08:00
|
|
|
if (err < 0)
|
|
|
|
dev_dbg(&dev->dev, "error usb_get_descriptor: %d\n", err);
|
2010-03-05 02:46:13 +08:00
|
|
|
err = usb_reset_configuration(dev);
|
2014-02-26 20:02:17 +08:00
|
|
|
if (err < 0)
|
|
|
|
dev_dbg(&dev->dev, "error usb_reset_configuration: %d\n", err);
|
|
|
|
dev_dbg(&dev->dev, "extigy_boot: new boot length = %d\n",
|
2010-03-05 02:46:13 +08:00
|
|
|
le16_to_cpu(get_cfg_desc(config)->wTotalLength));
|
|
|
|
return -ENODEV; /* quit this anyway */
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int snd_usb_audigy2nx_boot_quirk(struct usb_device *dev)
|
|
|
|
{
|
|
|
|
u8 buf = 1;
|
|
|
|
|
|
|
|
snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), 0x2a,
|
|
|
|
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER,
|
2011-09-27 03:15:27 +08:00
|
|
|
0, 0, &buf, 1);
|
2010-03-05 02:46:13 +08:00
|
|
|
if (buf == 0) {
|
|
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0x29,
|
|
|
|
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
|
2011-09-27 03:15:27 +08:00
|
|
|
1, 2000, NULL, 0);
|
2010-03-05 02:46:13 +08:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-07-13 00:13:46 +08:00
|
|
|
static int snd_usb_fasttrackpro_boot_quirk(struct usb_device *dev)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (dev->actconfig->desc.bConfigurationValue == 1) {
|
2014-02-26 20:02:17 +08:00
|
|
|
dev_info(&dev->dev,
|
2011-07-13 00:13:46 +08:00
|
|
|
"Fast Track Pro switching to config #2\n");
|
|
|
|
/* This function has to be available by the usb core module.
|
|
|
|
* if it is not avialable the boot quirk has to be left out
|
|
|
|
* and the configuration has to be set by udev or hotplug
|
|
|
|
* rules
|
|
|
|
*/
|
|
|
|
err = usb_driver_set_configuration(dev, 2);
|
2013-01-05 00:02:18 +08:00
|
|
|
if (err < 0)
|
2014-02-26 20:02:17 +08:00
|
|
|
dev_dbg(&dev->dev,
|
|
|
|
"error usb_driver_set_configuration: %d\n",
|
|
|
|
err);
|
2013-01-05 00:02:18 +08:00
|
|
|
/* Always return an error, so that we stop creating a device
|
|
|
|
that will just be destroyed and recreated with a new
|
|
|
|
configuration */
|
|
|
|
return -ENODEV;
|
2011-07-13 00:13:46 +08:00
|
|
|
} else
|
2014-02-26 20:02:17 +08:00
|
|
|
dev_info(&dev->dev, "Fast Track Pro config OK\n");
|
2011-07-13 00:13:46 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-03-05 02:46:13 +08:00
|
|
|
/*
|
|
|
|
* C-Media CM106/CM106+ have four 16-bit internal registers that are nicely
|
|
|
|
* documented in the device's data sheet.
|
|
|
|
*/
|
|
|
|
static int snd_usb_cm106_write_int_reg(struct usb_device *dev, int reg, u16 value)
|
|
|
|
{
|
|
|
|
u8 buf[4];
|
|
|
|
buf[0] = 0x20;
|
|
|
|
buf[1] = value & 0xff;
|
|
|
|
buf[2] = (value >> 8) & 0xff;
|
|
|
|
buf[3] = reg;
|
|
|
|
return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_CONFIGURATION,
|
|
|
|
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT,
|
2011-09-27 03:15:27 +08:00
|
|
|
0, 0, &buf, 4);
|
2010-03-05 02:46:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int snd_usb_cm106_boot_quirk(struct usb_device *dev)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Enable line-out driver mode, set headphone source to front
|
|
|
|
* channels, enable stereo mic.
|
|
|
|
*/
|
|
|
|
return snd_usb_cm106_write_int_reg(dev, 2, 0x8004);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2018-11-03 05:11:33 +08:00
|
|
|
* CM6206 registers from the CM6206 datasheet rev 2.1
|
2010-03-05 02:46:13 +08:00
|
|
|
*/
|
2018-11-03 05:11:33 +08:00
|
|
|
#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
|
|
|
|
|
2010-03-05 02:46:13 +08:00
|
|
|
static int snd_usb_cm6206_boot_quirk(struct usb_device *dev)
|
|
|
|
{
|
2011-08-06 06:23:18 +08:00
|
|
|
int err = 0, reg;
|
2018-11-03 05:11:33 +08:00
|
|
|
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 |
|
2019-01-09 04:03:11 +08:00
|
|
|
CM6206_REG1_SOFT_MUTE_EN,
|
2018-11-03 05:11:33 +08:00
|
|
|
/*
|
|
|
|
* 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 };
|
2010-03-05 02:46:13 +08:00
|
|
|
|
|
|
|
for (reg = 0; reg < ARRAY_SIZE(val); reg++) {
|
|
|
|
err = snd_usb_cm106_write_int_reg(dev, reg, val[reg]);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2013-12-19 21:32:53 +08:00
|
|
|
/* quirk for Plantronics GameCom 780 with CM6302 chip */
|
|
|
|
static int snd_usb_gamecon780_boot_quirk(struct usb_device *dev)
|
|
|
|
{
|
|
|
|
/* set the initial volume and don't change; other values are either
|
|
|
|
* too loud or silent due to firmware bug (bko#65251)
|
|
|
|
*/
|
2014-08-04 06:47:36 +08:00
|
|
|
u8 buf[2] = { 0x74, 0xe3 };
|
2013-12-19 21:32:53 +08:00
|
|
|
return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
|
|
|
|
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
|
|
|
|
UAC_FU_VOLUME << 8, 9 << 8, buf, 2);
|
|
|
|
}
|
|
|
|
|
2013-03-17 19:07:53 +08:00
|
|
|
/*
|
|
|
|
* Novation Twitch DJ controller
|
2013-12-20 16:06:58 +08:00
|
|
|
* Focusrite Novation Saffire 6 USB audio card
|
2013-03-17 19:07:53 +08:00
|
|
|
*/
|
2013-12-20 16:06:58 +08:00
|
|
|
static int snd_usb_novation_boot_quirk(struct usb_device *dev)
|
2013-03-17 19:07:53 +08:00
|
|
|
{
|
|
|
|
/* preemptively set up the device because otherwise the
|
|
|
|
* raw MIDI endpoints are not active */
|
|
|
|
usb_set_interface(dev, 0, 1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-03-05 02:46:13 +08:00
|
|
|
/*
|
|
|
|
* This call will put the synth in "USB send" mode, i.e it will send MIDI
|
|
|
|
* messages through USB (this is disabled at startup). The synth will
|
|
|
|
* acknowledge by sending a sysex on endpoint 0x85 and by displaying a USB
|
|
|
|
* sign on its LCD. Values here are chosen based on sniffing USB traffic
|
|
|
|
* under Windows.
|
|
|
|
*/
|
|
|
|
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 };
|
2019-06-24 21:08:28 +08:00
|
|
|
void *buf;
|
2010-03-05 02:46:13 +08:00
|
|
|
|
2019-06-24 21:08:28 +08:00
|
|
|
if (snd_usb_pipe_sanity_check(dev, usb_sndintpipe(dev, 0x05)))
|
|
|
|
return -EINVAL;
|
|
|
|
buf = kmemdup(seq, ARRAY_SIZE(seq), GFP_KERNEL);
|
2010-03-05 02:46:13 +08:00
|
|
|
if (!buf)
|
|
|
|
return -ENOMEM;
|
|
|
|
err = usb_interrupt_msg(dev, usb_sndintpipe(dev, 0x05), buf,
|
|
|
|
ARRAY_SIZE(seq), &actual_length, 1000);
|
|
|
|
kfree(buf);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-02-11 19:08:06 +08:00
|
|
|
/*
|
|
|
|
* Some sound cards from Native Instruments are in fact compliant to the USB
|
|
|
|
* audio standard of version 2 and other approved USB standards, even though
|
|
|
|
* they come up as vendor-specific device when first connected.
|
|
|
|
*
|
|
|
|
* However, they can be told to come up with a new set of descriptors
|
|
|
|
* upon their next enumeration, and the interfaces announced by the new
|
|
|
|
* descriptors will then be handled by the kernel's class drivers. As the
|
|
|
|
* product ID will also change, no further checks are required.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int snd_usb_nativeinstruments_boot_quirk(struct usb_device *dev)
|
|
|
|
{
|
2019-06-24 21:08:28 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (snd_usb_pipe_sanity_check(dev, usb_sndctrlpipe(dev, 0)))
|
|
|
|
return -EINVAL;
|
|
|
|
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
2011-02-11 19:08:06 +08:00
|
|
|
0xaf, USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
2013-04-06 02:49:46 +08:00
|
|
|
1, 0, NULL, 0, 1000);
|
2011-02-11 19:08:06 +08:00
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
usb_reset_device(dev);
|
|
|
|
|
|
|
|
/* return -EAGAIN, so the creation of an audio interface for this
|
|
|
|
* temporary device is aborted. The device will reconnect with a
|
|
|
|
* new product ID */
|
|
|
|
return -EAGAIN;
|
|
|
|
}
|
|
|
|
|
2012-12-19 18:27:22 +08:00
|
|
|
static void mbox2_setup_48_24_magic(struct usb_device *dev)
|
|
|
|
{
|
|
|
|
u8 srate[3];
|
|
|
|
u8 temp[12];
|
|
|
|
|
|
|
|
/* Choose 48000Hz permanently */
|
|
|
|
srate[0] = 0x80;
|
|
|
|
srate[1] = 0xbb;
|
|
|
|
srate[2] = 0x00;
|
|
|
|
|
|
|
|
/* Send the magic! */
|
|
|
|
snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
|
|
|
|
0x01, 0x22, 0x0100, 0x0085, &temp, 0x0003);
|
|
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
|
|
0x81, 0xa2, 0x0100, 0x0085, &srate, 0x0003);
|
|
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
|
|
0x81, 0xa2, 0x0100, 0x0086, &srate, 0x0003);
|
|
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
|
|
0x81, 0xa2, 0x0100, 0x0003, &srate, 0x0003);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Digidesign Mbox 2 needs to load firmware onboard
|
|
|
|
* and driver must wait a few seconds for initialisation.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define MBOX2_FIRMWARE_SIZE 646
|
|
|
|
#define MBOX2_BOOT_LOADING 0x01 /* Hard coded into the device */
|
|
|
|
#define MBOX2_BOOT_READY 0x02 /* Hard coded into the device */
|
|
|
|
|
2013-01-04 16:51:44 +08:00
|
|
|
static int snd_usb_mbox2_boot_quirk(struct usb_device *dev)
|
2012-12-19 18:27:22 +08:00
|
|
|
{
|
|
|
|
struct usb_host_config *config = dev->actconfig;
|
|
|
|
int err;
|
2013-02-17 21:33:04 +08:00
|
|
|
u8 bootresponse[0x12];
|
2012-12-19 18:27:22 +08:00
|
|
|
int fwsize;
|
|
|
|
int count;
|
|
|
|
|
|
|
|
fwsize = le16_to_cpu(get_cfg_desc(config)->wTotalLength);
|
|
|
|
|
|
|
|
if (fwsize != MBOX2_FIRMWARE_SIZE) {
|
2014-02-26 20:02:17 +08:00
|
|
|
dev_err(&dev->dev, "Invalid firmware size=%d.\n", fwsize);
|
2012-12-19 18:27:22 +08:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2014-02-26 20:02:17 +08:00
|
|
|
dev_dbg(&dev->dev, "Sending Digidesign Mbox 2 boot sequence...\n");
|
2012-12-19 18:27:22 +08:00
|
|
|
|
|
|
|
count = 0;
|
2013-01-04 16:51:44 +08:00
|
|
|
bootresponse[0] = MBOX2_BOOT_LOADING;
|
|
|
|
while ((bootresponse[0] == MBOX2_BOOT_LOADING) && (count < 10)) {
|
2012-12-19 18:27:22 +08:00
|
|
|
msleep(500); /* 0.5 second delay */
|
|
|
|
snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
|
|
|
|
/* Control magic - load onboard firmware */
|
|
|
|
0x85, 0xc0, 0x0001, 0x0000, &bootresponse, 0x0012);
|
2013-01-04 16:51:44 +08:00
|
|
|
if (bootresponse[0] == MBOX2_BOOT_READY)
|
2012-12-19 18:27:22 +08:00
|
|
|
break;
|
2014-02-26 20:02:17 +08:00
|
|
|
dev_dbg(&dev->dev, "device not ready, resending boot sequence...\n");
|
2012-12-19 18:27:22 +08:00
|
|
|
count++;
|
|
|
|
}
|
|
|
|
|
2013-01-04 16:51:44 +08:00
|
|
|
if (bootresponse[0] != MBOX2_BOOT_READY) {
|
2014-02-26 20:02:17 +08:00
|
|
|
dev_err(&dev->dev, "Unknown bootresponse=%d, or timed out, ignoring device.\n", bootresponse[0]);
|
2012-12-19 18:27:22 +08:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2014-02-26 20:02:17 +08:00
|
|
|
dev_dbg(&dev->dev, "device initialised!\n");
|
2012-12-19 18:27:22 +08:00
|
|
|
|
|
|
|
err = usb_get_descriptor(dev, USB_DT_DEVICE, 0,
|
|
|
|
&dev->descriptor, sizeof(dev->descriptor));
|
|
|
|
config = dev->actconfig;
|
|
|
|
if (err < 0)
|
2014-02-26 20:02:17 +08:00
|
|
|
dev_dbg(&dev->dev, "error usb_get_descriptor: %d\n", err);
|
2012-12-19 18:27:22 +08:00
|
|
|
|
|
|
|
err = usb_reset_configuration(dev);
|
|
|
|
if (err < 0)
|
2014-02-26 20:02:17 +08:00
|
|
|
dev_dbg(&dev->dev, "error usb_reset_configuration: %d\n", err);
|
|
|
|
dev_dbg(&dev->dev, "mbox2_boot: new boot length = %d\n",
|
2012-12-19 18:27:22 +08:00
|
|
|
le16_to_cpu(get_cfg_desc(config)->wTotalLength));
|
|
|
|
|
|
|
|
mbox2_setup_48_24_magic(dev);
|
|
|
|
|
2014-02-26 20:02:17 +08:00
|
|
|
dev_info(&dev->dev, "Digidesign Mbox 2: 24bit 48kHz");
|
2012-12-19 18:27:22 +08:00
|
|
|
|
|
|
|
return 0; /* Successful boot */
|
|
|
|
}
|
|
|
|
|
2018-05-09 06:14:13 +08:00
|
|
|
static int snd_usb_axefx3_boot_quirk(struct usb_device *dev)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
dev_dbg(&dev->dev, "Waiting for Axe-Fx III to boot up...\n");
|
|
|
|
|
2019-06-24 21:08:28 +08:00
|
|
|
if (snd_usb_pipe_sanity_check(dev, usb_sndctrlpipe(dev, 0)))
|
|
|
|
return -EINVAL;
|
2018-05-09 06:14:13 +08:00
|
|
|
/* If the Axe-Fx III has not fully booted, it will timeout when trying
|
|
|
|
* to enable the audio streaming interface. A more generous timeout is
|
|
|
|
* used here to detect when the Axe-Fx III has finished booting as the
|
|
|
|
* set interface message will be acked once it has
|
|
|
|
*/
|
|
|
|
err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
|
|
USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE,
|
|
|
|
1, 1, NULL, 0, 120000);
|
|
|
|
if (err < 0) {
|
|
|
|
dev_err(&dev->dev,
|
|
|
|
"failed waiting for Axe-Fx III to boot: %d\n", err);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev_dbg(&dev->dev, "Axe-Fx III is now ready\n");
|
|
|
|
|
|
|
|
err = usb_set_interface(dev, 1, 0);
|
|
|
|
if (err < 0)
|
|
|
|
dev_dbg(&dev->dev,
|
|
|
|
"error stopping Axe-Fx III interface: %d\n", err);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-03-01 03:34:04 +08:00
|
|
|
|
|
|
|
#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;
|
|
|
|
|
2019-06-24 21:08:28 +08:00
|
|
|
if (snd_usb_pipe_sanity_check(dev, usb_sndintpipe(dev, 0x01)))
|
|
|
|
return -EINVAL;
|
2019-03-01 03:34:04 +08:00
|
|
|
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);
|
|
|
|
|
2019-06-24 21:08:28 +08:00
|
|
|
if (snd_usb_pipe_sanity_check(dev, usb_rcvintpipe(dev, 0x82)))
|
|
|
|
return -EINVAL;
|
2019-03-01 03:34:04 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-01-12 18:23:58 +08:00
|
|
|
static int snd_usb_motu_m_series_boot_quirk(struct usb_device *dev)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (snd_usb_pipe_sanity_check(dev, usb_sndctrlpipe(dev, 0)))
|
|
|
|
return -EINVAL;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2010-03-05 02:46:13 +08:00
|
|
|
/*
|
|
|
|
* Setup quirks
|
|
|
|
*/
|
2011-07-13 00:13:46 +08:00
|
|
|
#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_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 */
|
|
|
|
|
|
|
|
static int quattro_skip_setting_quirk(struct snd_usb_audio *chip,
|
|
|
|
int iface, int altno)
|
|
|
|
{
|
|
|
|
/* Reset ALL ifaces to 0 altsetting.
|
|
|
|
* Call it for every possible altsetting of every interface.
|
|
|
|
*/
|
|
|
|
usb_set_interface(chip->dev, iface, 0);
|
|
|
|
if (chip->setup & MAUDIO_SET) {
|
|
|
|
if (chip->setup & MAUDIO_SET_COMPATIBLE) {
|
|
|
|
if (iface != 1 && iface != 2)
|
|
|
|
return 1; /* skip all interfaces but 1 and 2 */
|
|
|
|
} else {
|
|
|
|
unsigned int mask;
|
|
|
|
if (iface == 1 || iface == 2)
|
|
|
|
return 1; /* skip interfaces 1 and 2 */
|
|
|
|
if ((chip->setup & MAUDIO_SET_96K) && altno != 1)
|
|
|
|
return 1; /* skip this altsetting */
|
|
|
|
mask = chip->setup & MAUDIO_SET_MASK;
|
|
|
|
if (mask == MAUDIO_SET_24B_48K_DI && altno != 2)
|
|
|
|
return 1; /* skip this altsetting */
|
|
|
|
if (mask == MAUDIO_SET_24B_48K_NOTDI && altno != 3)
|
|
|
|
return 1; /* skip this altsetting */
|
|
|
|
if (mask == MAUDIO_SET_16B_48K_NOTDI && altno != 4)
|
|
|
|
return 1; /* skip this altsetting */
|
|
|
|
}
|
|
|
|
}
|
2014-02-26 20:02:17 +08:00
|
|
|
usb_audio_dbg(chip,
|
2011-07-13 00:13:46 +08:00
|
|
|
"using altsetting %d for interface %d config %d\n",
|
|
|
|
altno, iface, chip->setup);
|
|
|
|
return 0; /* keep this altsetting */
|
|
|
|
}
|
2010-03-05 02:46:13 +08:00
|
|
|
|
|
|
|
static int audiophile_skip_setting_quirk(struct snd_usb_audio *chip,
|
|
|
|
int iface,
|
|
|
|
int altno)
|
|
|
|
{
|
|
|
|
/* Reset ALL ifaces to 0 altsetting.
|
|
|
|
* Call it for every possible altsetting of every interface.
|
|
|
|
*/
|
|
|
|
usb_set_interface(chip->dev, iface, 0);
|
|
|
|
|
2011-07-13 00:13:46 +08:00
|
|
|
if (chip->setup & MAUDIO_SET) {
|
|
|
|
unsigned int mask;
|
|
|
|
if ((chip->setup & MAUDIO_SET_DTS) && altno != 6)
|
2010-03-05 02:46:13 +08:00
|
|
|
return 1; /* skip this altsetting */
|
2011-07-13 00:13:46 +08:00
|
|
|
if ((chip->setup & MAUDIO_SET_96K) && altno != 1)
|
2010-03-05 02:46:13 +08:00
|
|
|
return 1; /* skip this altsetting */
|
2011-07-13 00:13:46 +08:00
|
|
|
mask = chip->setup & MAUDIO_SET_MASK;
|
|
|
|
if (mask == MAUDIO_SET_24B_48K_DI && altno != 2)
|
2010-03-05 02:46:13 +08:00
|
|
|
return 1; /* skip this altsetting */
|
2011-07-13 00:13:46 +08:00
|
|
|
if (mask == MAUDIO_SET_24B_48K_NOTDI && altno != 3)
|
2010-03-05 02:46:13 +08:00
|
|
|
return 1; /* skip this altsetting */
|
2011-07-13 00:13:46 +08:00
|
|
|
if (mask == MAUDIO_SET_16B_48K_DI && altno != 4)
|
2010-03-05 02:46:13 +08:00
|
|
|
return 1; /* skip this altsetting */
|
2011-07-13 00:13:46 +08:00
|
|
|
if (mask == MAUDIO_SET_16B_48K_NOTDI && altno != 5)
|
2010-03-05 02:46:13 +08:00
|
|
|
return 1; /* skip this altsetting */
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0; /* keep this altsetting */
|
|
|
|
}
|
|
|
|
|
2011-07-13 00:13:46 +08:00
|
|
|
static int fasttrackpro_skip_setting_quirk(struct snd_usb_audio *chip,
|
|
|
|
int iface, int altno)
|
|
|
|
{
|
|
|
|
/* Reset ALL ifaces to 0 altsetting.
|
|
|
|
* Call it for every possible altsetting of every interface.
|
|
|
|
*/
|
|
|
|
usb_set_interface(chip->dev, iface, 0);
|
|
|
|
|
|
|
|
/* possible configuration where both inputs and only one output is
|
|
|
|
*used is not supported by the current setup
|
|
|
|
*/
|
|
|
|
if (chip->setup & (MAUDIO_SET | MAUDIO_SET_24B)) {
|
|
|
|
if (chip->setup & MAUDIO_SET_96K) {
|
|
|
|
if (altno != 3 && altno != 6)
|
|
|
|
return 1;
|
|
|
|
} else if (chip->setup & MAUDIO_SET_DI) {
|
|
|
|
if (iface == 4)
|
|
|
|
return 1; /* no analog input */
|
|
|
|
if (altno != 2 && altno != 5)
|
|
|
|
return 1; /* enable only altsets 2 and 5 */
|
|
|
|
} else {
|
|
|
|
if (iface == 5)
|
|
|
|
return 1; /* disable digialt input */
|
|
|
|
if (altno != 2 && altno != 5)
|
|
|
|
return 1; /* enalbe only altsets 2 and 5 */
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* keep only 16-Bit mode */
|
|
|
|
if (altno != 1)
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2014-02-26 20:02:17 +08:00
|
|
|
usb_audio_dbg(chip,
|
2011-07-13 00:13:46 +08:00
|
|
|
"using altsetting %d for interface %d config %d\n",
|
|
|
|
altno, iface, chip->setup);
|
|
|
|
return 0; /* keep this altsetting */
|
|
|
|
}
|
|
|
|
|
2020-02-15 09:23:35 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2010-03-05 02:46:13 +08:00
|
|
|
int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip,
|
|
|
|
int iface,
|
|
|
|
int altno)
|
|
|
|
{
|
|
|
|
/* audiophile usb: skip altsets incompatible with device_setup */
|
|
|
|
if (chip->usb_id == USB_ID(0x0763, 0x2003))
|
|
|
|
return audiophile_skip_setting_quirk(chip, iface, altno);
|
2011-07-13 00:13:46 +08:00
|
|
|
/* quattro usb: skip altsets incompatible with device_setup */
|
|
|
|
if (chip->usb_id == USB_ID(0x0763, 0x2001))
|
|
|
|
return quattro_skip_setting_quirk(chip, iface, altno);
|
|
|
|
/* fasttrackpro usb: skip altsets incompatible with device_setup */
|
|
|
|
if (chip->usb_id == USB_ID(0x0763, 0x2012))
|
|
|
|
return fasttrackpro_skip_setting_quirk(chip, iface, altno);
|
2020-02-15 09:23:35 +08:00
|
|
|
/* presonus studio 1810c: skip altsets incompatible with device_setup */
|
|
|
|
if (chip->usb_id == USB_ID(0x0194f, 0x010c))
|
|
|
|
return s1810c_skip_setting_quirk(chip, iface, altno);
|
|
|
|
|
2010-03-05 02:46:13 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int snd_usb_apply_boot_quirk(struct usb_device *dev,
|
|
|
|
struct usb_interface *intf,
|
2016-01-11 18:33:34 +08:00
|
|
|
const struct snd_usb_audio_quirk *quirk,
|
|
|
|
unsigned int id)
|
2010-03-05 02:46:13 +08:00
|
|
|
{
|
2011-02-11 19:34:12 +08:00
|
|
|
switch (id) {
|
|
|
|
case USB_ID(0x041e, 0x3000):
|
|
|
|
/* SB Extigy needs special boot-up sequence */
|
|
|
|
/* if more models come, this will go to the quirk list. */
|
2010-03-05 02:46:13 +08:00
|
|
|
return snd_usb_extigy_boot_quirk(dev, intf);
|
|
|
|
|
2011-02-11 19:34:12 +08:00
|
|
|
case USB_ID(0x041e, 0x3020):
|
|
|
|
/* SB Audigy 2 NX needs its own boot-up magic, too */
|
2010-03-05 02:46:13 +08:00
|
|
|
return snd_usb_audigy2nx_boot_quirk(dev);
|
|
|
|
|
2011-02-11 19:34:12 +08:00
|
|
|
case USB_ID(0x10f5, 0x0200):
|
|
|
|
/* C-Media CM106 / Turtle Beach Audio Advantage Roadie */
|
2010-03-05 02:46:13 +08:00
|
|
|
return snd_usb_cm106_boot_quirk(dev);
|
|
|
|
|
2011-02-11 19:34:12 +08:00
|
|
|
case USB_ID(0x0d8c, 0x0102):
|
|
|
|
/* C-Media CM6206 / CM106-Like Sound Device */
|
2011-04-28 22:18:40 +08:00
|
|
|
case USB_ID(0x0ccd, 0x00b1): /* Terratec Aureon 7.1 USB */
|
2010-03-05 02:46:13 +08:00
|
|
|
return snd_usb_cm6206_boot_quirk(dev);
|
|
|
|
|
2012-12-19 18:27:22 +08:00
|
|
|
case USB_ID(0x0dba, 0x3000):
|
|
|
|
/* Digidesign Mbox 2 */
|
|
|
|
return snd_usb_mbox2_boot_quirk(dev);
|
|
|
|
|
2013-12-20 16:06:58 +08:00
|
|
|
case USB_ID(0x1235, 0x0010): /* Focusrite Novation Saffire 6 USB */
|
|
|
|
case USB_ID(0x1235, 0x0018): /* Focusrite Novation Twitch */
|
|
|
|
return snd_usb_novation_boot_quirk(dev);
|
2013-03-17 19:07:53 +08:00
|
|
|
|
2011-02-11 19:34:12 +08:00
|
|
|
case USB_ID(0x133e, 0x0815):
|
|
|
|
/* Access Music VirusTI Desktop */
|
2010-03-05 02:46:13 +08:00
|
|
|
return snd_usb_accessmusic_boot_quirk(dev);
|
|
|
|
|
2011-05-18 17:28:41 +08:00
|
|
|
case USB_ID(0x17cc, 0x1000): /* Komplete Audio 6 */
|
2011-02-11 19:34:12 +08:00
|
|
|
case USB_ID(0x17cc, 0x1010): /* Traktor Audio 6 */
|
|
|
|
case USB_ID(0x17cc, 0x1020): /* Traktor Audio 10 */
|
2011-02-11 19:08:06 +08:00
|
|
|
return snd_usb_nativeinstruments_boot_quirk(dev);
|
2011-07-13 00:13:46 +08:00
|
|
|
case USB_ID(0x0763, 0x2012): /* M-Audio Fast Track Pro USB */
|
|
|
|
return snd_usb_fasttrackpro_boot_quirk(dev);
|
2013-12-19 21:32:53 +08:00
|
|
|
case USB_ID(0x047f, 0xc010): /* Plantronics Gamecom 780 */
|
|
|
|
return snd_usb_gamecon780_boot_quirk(dev);
|
2018-05-09 06:14:13 +08:00
|
|
|
case USB_ID(0x2466, 0x8010): /* Fractal Audio Axe-Fx 3 */
|
|
|
|
return snd_usb_axefx3_boot_quirk(dev);
|
2019-03-01 03:34:04 +08:00
|
|
|
case USB_ID(0x07fd, 0x0004): /* MOTU MicroBook II */
|
2020-02-29 23:18:15 +08:00
|
|
|
/*
|
|
|
|
* 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;
|
2011-02-11 19:34:12 +08:00
|
|
|
}
|
2011-02-11 19:08:06 +08:00
|
|
|
|
2010-03-05 02:46:13 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-01-12 18:23:58 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2010-03-05 02:46:13 +08:00
|
|
|
/*
|
|
|
|
* check if the device uses big-endian samples
|
|
|
|
*/
|
|
|
|
int snd_usb_is_big_endian_format(struct snd_usb_audio *chip, struct audioformat *fp)
|
|
|
|
{
|
2012-09-20 09:48:00 +08:00
|
|
|
/* it depends on altsetting whether the device is big-endian or not */
|
2010-03-05 02:46:13 +08:00
|
|
|
switch (chip->usb_id) {
|
|
|
|
case USB_ID(0x0763, 0x2001): /* M-Audio Quattro: captured data only */
|
2011-07-13 00:13:46 +08:00
|
|
|
if (fp->altsetting == 2 || fp->altsetting == 3 ||
|
|
|
|
fp->altsetting == 5 || fp->altsetting == 6)
|
2010-03-05 02:46:13 +08:00
|
|
|
return 1;
|
|
|
|
break;
|
|
|
|
case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
|
|
|
|
if (chip->setup == 0x00 ||
|
2011-07-13 00:13:46 +08:00
|
|
|
fp->altsetting == 1 || fp->altsetting == 2 ||
|
|
|
|
fp->altsetting == 3)
|
|
|
|
return 1;
|
|
|
|
break;
|
|
|
|
case USB_ID(0x0763, 0x2012): /* M-Audio Fast Track Pro */
|
|
|
|
if (fp->altsetting == 2 || fp->altsetting == 3 ||
|
|
|
|
fp->altsetting == 5 || fp->altsetting == 6)
|
2010-03-05 02:46:13 +08:00
|
|
|
return 1;
|
2011-07-13 00:13:46 +08:00
|
|
|
break;
|
2010-03-05 02:46:13 +08:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2011-02-08 14:22:36 +08:00
|
|
|
* For E-Mu 0404USB/0202USB/TrackerPre/0204 sample rate should be set for device,
|
2010-03-05 02:46:13 +08:00
|
|
|
* not for interface.
|
|
|
|
*/
|
|
|
|
|
|
|
|
enum {
|
|
|
|
EMU_QUIRK_SR_44100HZ = 0,
|
|
|
|
EMU_QUIRK_SR_48000HZ,
|
|
|
|
EMU_QUIRK_SR_88200HZ,
|
|
|
|
EMU_QUIRK_SR_96000HZ,
|
|
|
|
EMU_QUIRK_SR_176400HZ,
|
|
|
|
EMU_QUIRK_SR_192000HZ
|
|
|
|
};
|
|
|
|
|
|
|
|
static void set_format_emu_quirk(struct snd_usb_substream *subs,
|
|
|
|
struct audioformat *fmt)
|
|
|
|
{
|
|
|
|
unsigned char emu_samplerate_id = 0;
|
|
|
|
|
|
|
|
/* When capture is active
|
|
|
|
* sample rate shouldn't be changed
|
|
|
|
* by playback substream
|
|
|
|
*/
|
|
|
|
if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) {
|
|
|
|
if (subs->stream->substream[SNDRV_PCM_STREAM_CAPTURE].interface != -1)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (fmt->rate_min) {
|
|
|
|
case 48000:
|
|
|
|
emu_samplerate_id = EMU_QUIRK_SR_48000HZ;
|
|
|
|
break;
|
|
|
|
case 88200:
|
|
|
|
emu_samplerate_id = EMU_QUIRK_SR_88200HZ;
|
|
|
|
break;
|
|
|
|
case 96000:
|
|
|
|
emu_samplerate_id = EMU_QUIRK_SR_96000HZ;
|
|
|
|
break;
|
|
|
|
case 176400:
|
|
|
|
emu_samplerate_id = EMU_QUIRK_SR_176400HZ;
|
|
|
|
break;
|
|
|
|
case 192000:
|
|
|
|
emu_samplerate_id = EMU_QUIRK_SR_192000HZ;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
emu_samplerate_id = EMU_QUIRK_SR_44100HZ;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
snd_emuusb_set_samplerate(subs->stream->chip, emu_samplerate_id);
|
2013-04-13 11:33:59 +08:00
|
|
|
subs->pkt_offset_adj = (emu_samplerate_id >= EMU_QUIRK_SR_176400HZ) ? 4 : 0;
|
2010-03-05 02:46:13 +08:00
|
|
|
}
|
|
|
|
|
2020-06-01 18:22:24 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2010-03-05 02:46:13 +08:00
|
|
|
void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
|
|
|
|
struct audioformat *fmt)
|
|
|
|
{
|
|
|
|
switch (subs->stream->chip->usb_id) {
|
|
|
|
case USB_ID(0x041e, 0x3f02): /* E-Mu 0202 USB */
|
|
|
|
case USB_ID(0x041e, 0x3f04): /* E-Mu 0404 USB */
|
|
|
|
case USB_ID(0x041e, 0x3f0a): /* E-Mu Tracker Pre */
|
2011-02-08 14:22:36 +08:00
|
|
|
case USB_ID(0x041e, 0x3f19): /* E-Mu 0204 USB */
|
2010-03-05 02:46:13 +08:00
|
|
|
set_format_emu_quirk(subs, fmt);
|
|
|
|
break;
|
2020-06-01 18:22:24 +08:00
|
|
|
case USB_ID(0x2b73, 0x000a): /* Pioneer DJ DJM-900NXS2 */
|
|
|
|
pioneer_djm_set_format_quirk(subs);
|
|
|
|
break;
|
2010-03-05 02:46:13 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-17 04:44:33 +08:00
|
|
|
bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip)
|
|
|
|
{
|
2015-03-31 15:34:05 +08:00
|
|
|
/* devices which do not support reading the sample rate. */
|
|
|
|
switch (chip->usb_id) {
|
2020-01-05 16:19:00 +08:00
|
|
|
case USB_ID(0x041e, 0x4080): /* Creative Live Cam VF0610 */
|
|
|
|
case USB_ID(0x04d8, 0xfeea): /* Benchmark DAC1 Pre */
|
2016-05-11 23:48:00 +08:00
|
|
|
case USB_ID(0x0556, 0x0014): /* Phoenix Audio TMX320VC */
|
2020-01-05 16:19:00 +08:00
|
|
|
case USB_ID(0x05a3, 0x9420): /* ELP HD USB Camera */
|
2020-01-04 19:09:36 +08:00
|
|
|
case USB_ID(0x05a7, 0x1020): /* Bose Companion 5 */
|
2020-01-05 16:19:00 +08:00
|
|
|
case USB_ID(0x074d, 0x3553): /* Outlaw RR2150 (Micronas UAC3553B) */
|
2017-08-14 20:35:50 +08:00
|
|
|
case USB_ID(0x1395, 0x740a): /* Sennheiser DECT */
|
2016-08-13 02:08:47 +08:00
|
|
|
case USB_ID(0x1901, 0x0191): /* GE B850V3 CP2114 audio interface */
|
2020-01-05 16:19:00 +08:00
|
|
|
case USB_ID(0x21b4, 0x0081): /* AudioQuest DragonFly */
|
2020-02-12 00:22:35 +08:00
|
|
|
case USB_ID(0x2912, 0x30c8): /* Audioengine D1 */
|
2015-03-31 15:34:05 +08:00
|
|
|
return true;
|
|
|
|
}
|
2017-10-06 02:19:38 +08:00
|
|
|
|
|
|
|
/* devices of these vendors don't support reading rate, either */
|
|
|
|
switch (USB_ID_VENDOR(chip->usb_id)) {
|
2020-01-05 16:19:00 +08:00
|
|
|
case 0x045e: /* MS Lifecam */
|
|
|
|
case 0x047f: /* Plantronics */
|
2017-10-06 02:19:38 +08:00
|
|
|
case 0x1de7: /* Phoenix Audio */
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-03-31 15:34:05 +08:00
|
|
|
return false;
|
2015-02-17 04:44:33 +08:00
|
|
|
}
|
2014-11-29 00:32:54 +08:00
|
|
|
|
ALSA: usb-audio: Add native DSD support for Luxman DA-06
Add native DSD support quirk for Luxman DA-06 DAC, by adding the
PID/VID 1852:5065.
Rename "is_marantz_denon_dac()" function to "is_itf_usb_dsd_2alts_dac()"
to cover broader device family sharing the same USB audio
implementation(*).
For the same reason, rename "is_teac_dsd_dac()" function to
"is_itf_usb_dsd_3alts_dac()".
(*)
These devices have the same USB controller "ITF-USB DSD", supplied by
INTERFACE Co., Ltd.
"ITF-USB DSD" USB controller has two patterns,
Pattern 1. (2 altsets version)
- Altset 0: for control
- Altset 1: for stream (S32)
- Altset 2: for stream (S32, DSD_U32)
Pattern 2. (3 altsets version)
- Altset 0: for control
- Altset 1: for stream (S16)
- Altset 2: for stream (S32)
- Altset 3: for stream (S32, DSD_U32)
"is_itf_usb_dsd_2alts_dac()" returns true, if the DAC has "Pattern 1"
USB controller, and "is_itf_usb_dsd_3alts_dac()" returns true, if
"Pattern2".
Signed-off-by: Nobutaka Okabe <nob77413@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2018-03-23 18:18:22 +08:00
|
|
|
/* ITF-USB DSD based DACs need a vendor cmd to switch
|
2014-11-29 00:32:54 +08:00
|
|
|
* between PCM and native DSD mode
|
|
|
|
*/
|
2018-03-23 18:21:13 +08:00
|
|
|
static bool is_itf_usb_dsd_dac(unsigned int id)
|
2015-03-04 22:37:01 +08:00
|
|
|
{
|
2018-03-23 18:21:13 +08:00
|
|
|
switch (id) {
|
2020-06-13 19:40:06 +08:00
|
|
|
case USB_ID(0x154e, 0x1002): /* Denon DCD-1500RE */
|
2015-03-04 22:37:01 +08:00
|
|
|
case USB_ID(0x154e, 0x1003): /* Denon DA-300USB */
|
|
|
|
case USB_ID(0x154e, 0x3005): /* Marantz HD-DAC1 */
|
|
|
|
case USB_ID(0x154e, 0x3006): /* Marantz SA-14S1 */
|
ALSA: usb-audio: Add native DSD support for Luxman DA-06
Add native DSD support quirk for Luxman DA-06 DAC, by adding the
PID/VID 1852:5065.
Rename "is_marantz_denon_dac()" function to "is_itf_usb_dsd_2alts_dac()"
to cover broader device family sharing the same USB audio
implementation(*).
For the same reason, rename "is_teac_dsd_dac()" function to
"is_itf_usb_dsd_3alts_dac()".
(*)
These devices have the same USB controller "ITF-USB DSD", supplied by
INTERFACE Co., Ltd.
"ITF-USB DSD" USB controller has two patterns,
Pattern 1. (2 altsets version)
- Altset 0: for control
- Altset 1: for stream (S32)
- Altset 2: for stream (S32, DSD_U32)
Pattern 2. (3 altsets version)
- Altset 0: for control
- Altset 1: for stream (S16)
- Altset 2: for stream (S32)
- Altset 3: for stream (S32, DSD_U32)
"is_itf_usb_dsd_2alts_dac()" returns true, if the DAC has "Pattern 1"
USB controller, and "is_itf_usb_dsd_3alts_dac()" returns true, if
"Pattern2".
Signed-off-by: Nobutaka Okabe <nob77413@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2018-03-23 18:18:22 +08:00
|
|
|
case USB_ID(0x1852, 0x5065): /* Luxman DA-06 */
|
2018-03-23 18:21:13 +08:00
|
|
|
case USB_ID(0x0644, 0x8043): /* TEAC UD-501/UD-501V2/UD-503/NT-503 */
|
2017-12-07 18:58:33 +08:00
|
|
|
case USB_ID(0x0644, 0x8044): /* Esoteric D-05X */
|
2018-03-23 18:49:44 +08:00
|
|
|
case USB_ID(0x0644, 0x804a): /* TEAC UD-301 */
|
2016-12-13 00:24:08 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-11-29 00:32:54 +08:00
|
|
|
int snd_usb_select_mode_quirk(struct snd_usb_substream *subs,
|
|
|
|
struct audioformat *fmt)
|
|
|
|
{
|
|
|
|
struct usb_device *dev = subs->dev;
|
|
|
|
int err;
|
|
|
|
|
2018-03-23 18:21:13 +08:00
|
|
|
if (is_itf_usb_dsd_dac(subs->stream->chip->usb_id)) {
|
2014-11-29 00:32:54 +08:00
|
|
|
/* First switch to alt set 0, otherwise the mode switch cmd
|
|
|
|
* will not be accepted by the DAC
|
|
|
|
*/
|
|
|
|
err = usb_set_interface(dev, fmt->iface, 0);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
2018-07-27 16:55:28 +08:00
|
|
|
msleep(20); /* Delay needed after setting the interface */
|
2014-11-29 00:32:54 +08:00
|
|
|
|
2016-12-13 00:24:08 +08:00
|
|
|
/* Vendor mode switch cmd is required. */
|
2018-03-23 18:21:13 +08:00
|
|
|
if (fmt->formats & SNDRV_PCM_FMTBIT_DSD_U32_BE) {
|
|
|
|
/* DSD mode (DSD_U32) requested */
|
2016-12-13 00:24:08 +08:00
|
|
|
err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0,
|
|
|
|
USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
|
|
|
|
1, 1, NULL, 0);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
2018-03-23 18:21:13 +08:00
|
|
|
} else {
|
|
|
|
/* PCM or DOP mode (S32) requested */
|
|
|
|
/* PCM mode (S16) requested */
|
2016-12-13 00:24:08 +08:00
|
|
|
err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0,
|
|
|
|
USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
|
|
|
|
0, 1, NULL, 0);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
2018-03-23 18:21:13 +08:00
|
|
|
|
2016-12-13 00:24:08 +08:00
|
|
|
}
|
2018-07-27 16:55:28 +08:00
|
|
|
msleep(20);
|
2014-11-29 00:32:54 +08:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-09-04 16:23:07 +08:00
|
|
|
void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* "Playback Design" products send bogus feedback data at the start
|
|
|
|
* of the stream. Ignore them.
|
|
|
|
*/
|
2016-01-11 18:33:34 +08:00
|
|
|
if (USB_ID_VENDOR(ep->chip->usb_id) == 0x23ba &&
|
2012-09-04 16:23:07 +08:00
|
|
|
ep->type == SND_USB_ENDPOINT_TYPE_SYNC)
|
|
|
|
ep->skip_packets = 4;
|
2013-01-14 06:02:03 +08:00
|
|
|
|
|
|
|
/*
|
2013-02-10 01:56:35 +08:00
|
|
|
* M-Audio Fast Track C400/C600 - when packets are not skipped, real
|
|
|
|
* 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
|
2013-01-14 06:02:03 +08:00
|
|
|
* across power cycles).
|
|
|
|
*/
|
2013-02-10 01:56:35 +08:00
|
|
|
if ((ep->chip->usb_id == USB_ID(0x0763, 0x2030) ||
|
|
|
|
ep->chip->usb_id == USB_ID(0x0763, 0x2031)) &&
|
2013-01-14 06:02:03 +08:00
|
|
|
ep->type == SND_USB_ENDPOINT_TYPE_DATA)
|
|
|
|
ep->skip_packets = 16;
|
2016-08-22 14:53:36 +08:00
|
|
|
|
|
|
|
/* Work around devices that report unreasonable feedback data */
|
2016-08-22 14:53:37 +08:00
|
|
|
if ((ep->chip->usb_id == USB_ID(0x0644, 0x8038) || /* TEAC UD-H01 */
|
|
|
|
ep->chip->usb_id == USB_ID(0x1852, 0x5034)) && /* T+A Dac8 */
|
2016-08-22 14:53:36 +08:00
|
|
|
ep->syncmaxsize == 4)
|
2016-08-22 14:53:37 +08:00
|
|
|
ep->tenor_fb_quirk = 1;
|
2012-09-04 16:23:07 +08:00
|
|
|
}
|
|
|
|
|
2013-04-10 00:56:03 +08:00
|
|
|
void snd_usb_set_interface_quirk(struct usb_device *dev)
|
|
|
|
{
|
2016-01-11 18:33:34 +08:00
|
|
|
struct snd_usb_audio *chip = dev_get_drvdata(&dev->dev);
|
|
|
|
|
|
|
|
if (!chip)
|
|
|
|
return;
|
2013-04-10 00:56:03 +08:00
|
|
|
/*
|
|
|
|
* "Playback Design" products need a 50ms delay after setting the
|
|
|
|
* USB interface.
|
|
|
|
*/
|
2016-01-11 18:33:34 +08:00
|
|
|
switch (USB_ID_VENDOR(chip->usb_id)) {
|
2016-01-26 07:28:27 +08:00
|
|
|
case 0x23ba: /* Playback Design */
|
|
|
|
case 0x0644: /* TEAC Corp. */
|
2018-07-27 16:55:28 +08:00
|
|
|
msleep(50);
|
2016-01-26 07:28:27 +08:00
|
|
|
break;
|
|
|
|
}
|
2013-04-10 00:56:03 +08:00
|
|
|
}
|
|
|
|
|
2016-01-11 18:33:34 +08:00
|
|
|
/* quirk applied after snd_usb_ctl_msg(); not applied during boot quirks */
|
2012-09-04 16:23:07 +08:00
|
|
|
void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe,
|
|
|
|
__u8 request, __u8 requesttype, __u16 value,
|
|
|
|
__u16 index, void *data, __u16 size)
|
|
|
|
{
|
2016-01-11 18:33:34 +08:00
|
|
|
struct snd_usb_audio *chip = dev_get_drvdata(&dev->dev);
|
|
|
|
|
|
|
|
if (!chip)
|
|
|
|
return;
|
2012-09-04 16:23:07 +08:00
|
|
|
/*
|
|
|
|
* "Playback Design" products need a 20ms delay after each
|
|
|
|
* class compliant request
|
|
|
|
*/
|
2016-01-11 18:33:34 +08:00
|
|
|
if (USB_ID_VENDOR(chip->usb_id) == 0x23ba &&
|
2012-09-04 16:23:07 +08:00
|
|
|
(requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
|
2018-07-27 16:55:28 +08:00
|
|
|
msleep(20);
|
2014-11-15 21:01:21 +08:00
|
|
|
|
2016-01-26 07:28:27 +08:00
|
|
|
/*
|
|
|
|
* "TEAC Corp." products need a 20ms delay after each
|
|
|
|
* class compliant request
|
|
|
|
*/
|
2016-01-11 18:33:34 +08:00
|
|
|
if (USB_ID_VENDOR(chip->usb_id) == 0x0644 &&
|
2016-01-26 07:28:27 +08:00
|
|
|
(requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
|
2018-07-27 16:55:28 +08:00
|
|
|
msleep(20);
|
2016-01-26 07:28:27 +08:00
|
|
|
|
ALSA: usb-audio: Add native DSD support for Luxman DA-06
Add native DSD support quirk for Luxman DA-06 DAC, by adding the
PID/VID 1852:5065.
Rename "is_marantz_denon_dac()" function to "is_itf_usb_dsd_2alts_dac()"
to cover broader device family sharing the same USB audio
implementation(*).
For the same reason, rename "is_teac_dsd_dac()" function to
"is_itf_usb_dsd_3alts_dac()".
(*)
These devices have the same USB controller "ITF-USB DSD", supplied by
INTERFACE Co., Ltd.
"ITF-USB DSD" USB controller has two patterns,
Pattern 1. (2 altsets version)
- Altset 0: for control
- Altset 1: for stream (S32)
- Altset 2: for stream (S32, DSD_U32)
Pattern 2. (3 altsets version)
- Altset 0: for control
- Altset 1: for stream (S16)
- Altset 2: for stream (S32)
- Altset 3: for stream (S32, DSD_U32)
"is_itf_usb_dsd_2alts_dac()" returns true, if the DAC has "Pattern 1"
USB controller, and "is_itf_usb_dsd_3alts_dac()" returns true, if
"Pattern2".
Signed-off-by: Nobutaka Okabe <nob77413@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2018-03-23 18:18:22 +08:00
|
|
|
/* ITF-USB DSD based DACs functionality need a delay
|
2014-11-15 21:01:21 +08:00
|
|
|
* after each class compliant request
|
|
|
|
*/
|
2018-03-23 18:21:13 +08:00
|
|
|
if (is_itf_usb_dsd_dac(chip->usb_id)
|
2015-03-04 22:37:01 +08:00
|
|
|
&& (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
|
2018-07-27 16:55:28 +08:00
|
|
|
msleep(20);
|
2014-12-01 00:45:40 +08:00
|
|
|
|
2020-04-27 21:21:39 +08:00
|
|
|
/* Zoom R16/24, Logitech H650e, Jabra 550a, Kingston HyperX needs a tiny
|
|
|
|
* delay here, otherwise requests like get/set frequency return as
|
|
|
|
* failed despite actually succeeding.
|
2014-12-01 00:45:40 +08:00
|
|
|
*/
|
2017-08-22 14:33:53 +08:00
|
|
|
if ((chip->usb_id == USB_ID(0x1686, 0x00dd) ||
|
|
|
|
chip->usb_id == USB_ID(0x046d, 0x0a46) ||
|
2020-04-27 21:21:39 +08:00
|
|
|
chip->usb_id == USB_ID(0x0b0e, 0x0349) ||
|
|
|
|
chip->usb_id == USB_ID(0x0951, 0x16ad)) &&
|
2014-12-01 00:45:40 +08:00
|
|
|
(requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
|
2018-07-27 16:55:28 +08:00
|
|
|
usleep_range(1000, 2000);
|
2020-06-23 19:03:23 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Samsung USBC Headset (AKG) need a tiny delay after each
|
|
|
|
* class compliant request. (Model number: AAM625R or AAM627R)
|
|
|
|
*/
|
|
|
|
if (chip->usb_id == USB_ID(0x04e8, 0xa051) &&
|
|
|
|
(requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
|
|
|
|
usleep_range(5000, 6000);
|
2012-09-04 16:23:07 +08:00
|
|
|
}
|
|
|
|
|
2013-04-17 00:01:40 +08:00
|
|
|
/*
|
|
|
|
* snd_usb_interface_dsd_format_quirks() is called from format.c to
|
|
|
|
* augment the PCM format bit-field for DSD types. The UAC standards
|
|
|
|
* don't have a designated bit field to denote DSD-capable interfaces,
|
|
|
|
* hence all hardware that is known to support this format has to be
|
|
|
|
* listed here.
|
|
|
|
*/
|
|
|
|
u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
|
|
|
|
struct audioformat *fp,
|
|
|
|
unsigned int sample_bytes)
|
|
|
|
{
|
2018-03-23 18:21:13 +08:00
|
|
|
struct usb_interface *iface;
|
|
|
|
|
2013-04-17 00:01:40 +08:00
|
|
|
/* Playback Designs */
|
2019-08-28 05:08:46 +08:00
|
|
|
if (USB_ID_VENDOR(chip->usb_id) == 0x23ba &&
|
|
|
|
USB_ID_PRODUCT(chip->usb_id) < 0x0110) {
|
2013-04-17 00:01:40 +08:00
|
|
|
switch (fp->altsetting) {
|
|
|
|
case 1:
|
|
|
|
fp->dsd_dop = true;
|
|
|
|
return SNDRV_PCM_FMTBIT_DSD_U16_LE;
|
|
|
|
case 2:
|
|
|
|
fp->dsd_bitrev = true;
|
|
|
|
return SNDRV_PCM_FMTBIT_DSD_U8;
|
|
|
|
case 3:
|
|
|
|
fp->dsd_bitrev = true;
|
|
|
|
return SNDRV_PCM_FMTBIT_DSD_U16_LE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-06 00:14:46 +08:00
|
|
|
/* XMOS based USB DACs */
|
|
|
|
switch (chip->usb_id) {
|
2018-04-21 04:29:41 +08:00
|
|
|
case USB_ID(0x1511, 0x0037): /* AURALiC VEGA */
|
|
|
|
case USB_ID(0x2522, 0x0012): /* LH Labs VI DAC Infinity */
|
2017-10-15 17:41:32 +08:00
|
|
|
case USB_ID(0x2772, 0x0230): /* Pro-Ject Pre Box S2 Digital */
|
2014-09-06 00:14:46 +08:00
|
|
|
if (fp->altsetting == 2)
|
2014-11-21 22:04:46 +08:00
|
|
|
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
|
2014-09-06 00:14:46 +08:00
|
|
|
break;
|
2015-06-05 15:42:49 +08:00
|
|
|
|
2018-04-21 04:29:41 +08:00
|
|
|
case USB_ID(0x0d8c, 0x0316): /* Hegel HD12 DSD */
|
2019-01-29 06:47:01 +08:00
|
|
|
case USB_ID(0x10cb, 0x0103): /* The Bit Opus #3; with fp->dsd_raw */
|
2020-04-30 20:47:55 +08:00
|
|
|
case USB_ID(0x16d0, 0x06b2): /* NuPrime DAC-10 */
|
2019-01-29 06:47:01 +08:00
|
|
|
case USB_ID(0x16d0, 0x09dd): /* Encore mDSD */
|
2018-04-21 04:29:41 +08:00
|
|
|
case USB_ID(0x16d0, 0x0733): /* Furutech ADL Stratos */
|
|
|
|
case USB_ID(0x16d0, 0x09db): /* NuPrime Audio DAC-9 */
|
|
|
|
case USB_ID(0x1db5, 0x0003): /* Bryston BDA3 */
|
|
|
|
case USB_ID(0x22e1, 0xca01): /* HDTA Serenade DSD */
|
|
|
|
case USB_ID(0x249c, 0x9326): /* M2Tech Young MkIII */
|
2016-01-29 21:59:25 +08:00
|
|
|
case USB_ID(0x2616, 0x0106): /* PS Audio NuWave DAC */
|
2018-04-21 04:29:41 +08:00
|
|
|
case USB_ID(0x2622, 0x0041): /* Audiolab M-DAC+ */
|
|
|
|
case USB_ID(0x27f7, 0x3002): /* W4S DAC-2v2SE */
|
|
|
|
case USB_ID(0x29a2, 0x0086): /* Mutec MC3+ USB */
|
|
|
|
case USB_ID(0x6b42, 0x0042): /* MSB Technology */
|
2014-09-06 00:14:46 +08:00
|
|
|
if (fp->altsetting == 3)
|
2014-11-21 22:04:46 +08:00
|
|
|
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
|
2014-09-06 00:14:46 +08:00
|
|
|
break;
|
2016-12-18 05:51:41 +08:00
|
|
|
|
2018-02-11 21:50:27 +08:00
|
|
|
/* Amanero Combo384 USB based DACs with native DSD support */
|
|
|
|
case USB_ID(0x16d0, 0x071a): /* Amanero - Combo384 */
|
|
|
|
case USB_ID(0x2ab6, 0x0004): /* T+A DAC8DSD-V2.0, MP1000E-V2.0, MP2000R-V2.0, MP2500R-V2.0, MP3100HV-V2.0 */
|
|
|
|
case USB_ID(0x2ab6, 0x0005): /* T+A USB HD Audio 1 */
|
|
|
|
case USB_ID(0x2ab6, 0x0006): /* T+A USB HD Audio 2 */
|
2016-12-18 05:51:41 +08:00
|
|
|
if (fp->altsetting == 2) {
|
2017-05-12 20:34:37 +08:00
|
|
|
switch (le16_to_cpu(chip->dev->descriptor.bcdDevice)) {
|
2016-12-18 05:51:41 +08:00
|
|
|
case 0x199:
|
|
|
|
return SNDRV_PCM_FMTBIT_DSD_U32_LE;
|
|
|
|
case 0x19b:
|
2017-11-02 05:32:33 +08:00
|
|
|
case 0x203:
|
2016-12-18 05:51:41 +08:00
|
|
|
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2017-08-18 15:42:14 +08:00
|
|
|
case USB_ID(0x16d0, 0x0a23):
|
|
|
|
if (fp->altsetting == 2)
|
|
|
|
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
|
|
|
|
break;
|
2016-12-18 05:51:41 +08:00
|
|
|
|
2014-09-06 00:14:46 +08:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-03-23 18:21:13 +08:00
|
|
|
/* ITF-USB DSD based DACs */
|
|
|
|
if (is_itf_usb_dsd_dac(chip->usb_id)) {
|
|
|
|
iface = usb_ifnum_to_if(chip->dev, fp->iface);
|
2014-11-29 00:32:53 +08:00
|
|
|
|
2018-03-23 18:21:13 +08:00
|
|
|
/* Altsetting 2 support native DSD if the num of altsets is
|
|
|
|
* three (0-2),
|
|
|
|
* Altsetting 3 support native DSD if the num of altsets is
|
|
|
|
* four (0-3).
|
|
|
|
*/
|
|
|
|
if (fp->altsetting == iface->num_altsetting - 1)
|
2016-12-13 00:24:08 +08:00
|
|
|
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
|
|
|
|
}
|
|
|
|
|
2018-06-13 06:43:01 +08:00
|
|
|
/* Mostly generic method to detect many DSD-capable implementations -
|
|
|
|
* from XMOS/Thesycon
|
|
|
|
*/
|
|
|
|
switch (USB_ID_VENDOR(chip->usb_id)) {
|
2018-07-23 16:56:46 +08:00
|
|
|
case 0x152a: /* Thesycon devices */
|
2019-08-28 05:08:46 +08:00
|
|
|
case 0x20b1: /* XMOS based devices */
|
2019-08-28 05:08:47 +08:00
|
|
|
case 0x22d9: /* Oppo */
|
2019-08-28 05:08:46 +08:00
|
|
|
case 0x23ba: /* Playback Designs */
|
2018-06-13 06:43:01 +08:00
|
|
|
case 0x25ce: /* Mytek devices */
|
2019-08-28 05:08:47 +08:00
|
|
|
case 0x278b: /* Rotel? */
|
2019-10-24 18:27:14 +08:00
|
|
|
case 0x292b: /* Gustard/Ess based devices */
|
2019-02-06 00:20:47 +08:00
|
|
|
case 0x2ab6: /* T+A devices */
|
2019-09-24 15:11:43 +08:00
|
|
|
case 0x3842: /* EVGA */
|
2019-09-17 15:49:34 +08:00
|
|
|
case 0xc502: /* HiBy devices */
|
2018-06-13 06:43:01 +08:00
|
|
|
if (fp->dsd_raw)
|
|
|
|
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-04-17 00:01:40 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2018-03-19 09:46:02 +08:00
|
|
|
|
|
|
|
void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip,
|
|
|
|
struct audioformat *fp,
|
|
|
|
int stream)
|
|
|
|
{
|
|
|
|
switch (chip->usb_id) {
|
|
|
|
case USB_ID(0x0a92, 0x0053): /* AudioTrak Optoplay */
|
|
|
|
/* Optoplay sets the sample rate attribute although
|
|
|
|
* it seems not supporting it in fact.
|
|
|
|
*/
|
|
|
|
fp->attributes &= ~UAC_EP_CS_ATTR_SAMPLE_RATE;
|
|
|
|
break;
|
|
|
|
case USB_ID(0x041e, 0x3020): /* Creative SB Audigy 2 NX */
|
|
|
|
case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
|
|
|
|
/* doesn't set the sample rate attribute, but supports it */
|
|
|
|
fp->attributes |= UAC_EP_CS_ATTR_SAMPLE_RATE;
|
|
|
|
break;
|
|
|
|
case USB_ID(0x0763, 0x2001): /* M-Audio Quattro USB */
|
|
|
|
case USB_ID(0x0763, 0x2012): /* M-Audio Fast Track Pro USB */
|
|
|
|
case USB_ID(0x047f, 0x0ca1): /* plantronics headset */
|
|
|
|
case USB_ID(0x077d, 0x07af): /* Griffin iMic (note that there is
|
|
|
|
an older model 77d:223) */
|
|
|
|
/*
|
|
|
|
* plantronics headset and Griffin iMic have set adaptive-in
|
|
|
|
* although it's really not...
|
|
|
|
*/
|
|
|
|
fp->ep_attr &= ~USB_ENDPOINT_SYNCTYPE;
|
|
|
|
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
|
|
|
|
fp->ep_attr |= USB_ENDPOINT_SYNC_ADAPTIVE;
|
|
|
|
else
|
|
|
|
fp->ep_attr |= USB_ENDPOINT_SYNC_SYNC;
|
|
|
|
break;
|
2020-02-29 23:18:15 +08:00
|
|
|
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;
|
2018-03-19 09:46:02 +08:00
|
|
|
}
|
|
|
|
}
|
2020-03-15 00:54:49 +08:00
|
|
|
|
2020-03-25 18:33:19 +08:00
|
|
|
/*
|
|
|
|
* registration quirk:
|
|
|
|
* the registration is skipped if a device matches with the given ID,
|
|
|
|
* unless the interface reaches to the defined one. This is for delaying
|
|
|
|
* the registration until the last known interface, so that the card and
|
|
|
|
* devices appear at the same time.
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct registration_quirk {
|
|
|
|
unsigned int usb_id; /* composed via USB_ID() */
|
|
|
|
unsigned int interface; /* the interface to trigger register */
|
|
|
|
};
|
|
|
|
|
|
|
|
#define REG_QUIRK_ENTRY(vendor, product, iface) \
|
|
|
|
{ .usb_id = USB_ID(vendor, product), .interface = (iface) }
|
|
|
|
|
|
|
|
static const struct registration_quirk registration_quirks[] = {
|
|
|
|
REG_QUIRK_ENTRY(0x0951, 0x16d8, 2), /* Kingston HyperX AMP */
|
2020-04-04 23:38:43 +08:00
|
|
|
REG_QUIRK_ENTRY(0x0951, 0x16ed, 2), /* Kingston HyperX Cloud Alpha S */
|
2020-06-19 19:48:22 +08:00
|
|
|
REG_QUIRK_ENTRY(0x0951, 0x16ea, 2), /* Kingston HyperX Cloud Flight S */
|
2020-03-25 18:33:19 +08:00
|
|
|
{ 0 } /* terminator */
|
|
|
|
};
|
|
|
|
|
|
|
|
/* return true if skipping registration */
|
|
|
|
bool snd_usb_registration_quirk(struct snd_usb_audio *chip, int iface)
|
2020-03-15 00:54:49 +08:00
|
|
|
{
|
2020-03-25 18:33:19 +08:00
|
|
|
const struct registration_quirk *q;
|
|
|
|
|
|
|
|
for (q = registration_quirks; q->usb_id; q++)
|
|
|
|
if (chip->usb_id == q->usb_id)
|
|
|
|
return iface != q->interface;
|
|
|
|
|
2020-03-15 00:54:49 +08:00
|
|
|
/* Register as normal */
|
2020-03-25 18:33:19 +08:00
|
|
|
return false;
|
2020-03-15 00:54:49 +08:00
|
|
|
}
|