summaryrefslogtreecommitdiffstats
path: root/sound/usb
diff options
context:
space:
mode:
Diffstat (limited to 'sound/usb')
-rw-r--r--sound/usb/card.c4
-rw-r--r--sound/usb/clock.c4
-rw-r--r--sound/usb/helper.c1
-rw-r--r--sound/usb/midi.c1
-rw-r--r--sound/usb/mixer.c78
-rw-r--r--sound/usb/mixer_maps.c14
-rw-r--r--sound/usb/quirks.c5
-rw-r--r--sound/usb/usbaudio.h1
8 files changed, 106 insertions, 2 deletions
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 3fc63583a537..69860da473ea 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -350,6 +350,7 @@ static int snd_usb_audio_create(struct usb_interface *intf,
case USB_SPEED_HIGH:
case USB_SPEED_WIRELESS:
case USB_SPEED_SUPER:
+ case USB_SPEED_SUPER_PLUS:
break;
default:
dev_err(&dev->dev, "unknown device speed %d\n", snd_usb_get_speed(dev));
@@ -450,6 +451,9 @@ static int snd_usb_audio_create(struct usb_interface *intf,
case USB_SPEED_SUPER:
strlcat(card->longname, ", super speed", sizeof(card->longname));
break;
+ case USB_SPEED_SUPER_PLUS:
+ strlcat(card->longname, ", super speed plus", sizeof(card->longname));
+ break;
default:
break;
}
diff --git a/sound/usb/clock.c b/sound/usb/clock.c
index 7ccbcaf6a147..26dd5f20f149 100644
--- a/sound/usb/clock.c
+++ b/sound/usb/clock.c
@@ -309,6 +309,9 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
* support reading */
if (snd_usb_get_sample_rate_quirk(chip))
return 0;
+ /* the firmware is likely buggy, don't repeat to fail too many times */
+ if (chip->sample_rate_read_error > 2)
+ return 0;
if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN,
@@ -316,6 +319,7 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
data, sizeof(data))) < 0) {
dev_err(&dev->dev, "%d:%d: cannot get freq at ep %#x\n",
iface, fmt->altsetting, ep);
+ chip->sample_rate_read_error++;
return 0; /* some devices don't support reading */
}
diff --git a/sound/usb/helper.c b/sound/usb/helper.c
index 51ed1ac825fd..7712e2b84183 100644
--- a/sound/usb/helper.c
+++ b/sound/usb/helper.c
@@ -120,6 +120,7 @@ unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip,
case USB_SPEED_HIGH:
case USB_SPEED_WIRELESS:
case USB_SPEED_SUPER:
+ case USB_SPEED_SUPER_PLUS:
if (get_endpoint(alts, 0)->bInterval >= 1 &&
get_endpoint(alts, 0)->bInterval <= 4)
return get_endpoint(alts, 0)->bInterval - 1;
diff --git a/sound/usb/midi.c b/sound/usb/midi.c
index 47de8af42f16..7ba92921bf28 100644
--- a/sound/usb/midi.c
+++ b/sound/usb/midi.c
@@ -911,6 +911,7 @@ static void snd_usbmidi_us122l_output(struct snd_usb_midi_out_endpoint *ep,
switch (snd_usb_get_speed(ep->umidi->dev)) {
case USB_SPEED_HIGH:
case USB_SPEED_SUPER:
+ case USB_SPEED_SUPER_PLUS:
count = 1;
break;
default:
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 4f85757009b3..2f8c388ef84f 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -45,6 +45,7 @@
#include <linux/bitops.h>
#include <linux/init.h>
#include <linux/list.h>
+#include <linux/log2.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/usb.h>
@@ -1378,6 +1379,71 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
snd_usb_mixer_add_control(&cval->head, kctl);
}
+static int parse_clock_source_unit(struct mixer_build *state, int unitid,
+ void *_ftr)
+{
+ struct uac_clock_source_descriptor *hdr = _ftr;
+ struct usb_mixer_elem_info *cval;
+ struct snd_kcontrol *kctl;
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ int ret;
+
+ if (state->mixer->protocol != UAC_VERSION_2)
+ return -EINVAL;
+
+ if (hdr->bLength != sizeof(*hdr)) {
+ usb_audio_dbg(state->chip,
+ "Bogus clock source descriptor length of %d, ignoring.\n",
+ hdr->bLength);
+ return 0;
+ }
+
+ /*
+ * The only property of this unit we are interested in is the
+ * clock source validity. If that isn't readable, just bail out.
+ */
+ if (!uac2_control_is_readable(hdr->bmControls,
+ ilog2(UAC2_CS_CONTROL_CLOCK_VALID)))
+ return 0;
+
+ cval = kzalloc(sizeof(*cval), GFP_KERNEL);
+ if (!cval)
+ return -ENOMEM;
+
+ snd_usb_mixer_elem_init_std(&cval->head, state->mixer, hdr->bClockID);
+
+ cval->min = 0;
+ cval->max = 1;
+ cval->channels = 1;
+ cval->val_type = USB_MIXER_BOOLEAN;
+ cval->control = UAC2_CS_CONTROL_CLOCK_VALID;
+
+ if (uac2_control_is_writeable(hdr->bmControls,
+ ilog2(UAC2_CS_CONTROL_CLOCK_VALID)))
+ kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
+ else {
+ cval->master_readonly = 1;
+ kctl = snd_ctl_new1(&usb_feature_unit_ctl_ro, cval);
+ }
+
+ if (!kctl) {
+ kfree(cval);
+ return -ENOMEM;
+ }
+
+ kctl->private_free = snd_usb_mixer_elem_free;
+ ret = snd_usb_copy_string_desc(state, hdr->iClockSource,
+ name, sizeof(name));
+ if (ret > 0)
+ snprintf(kctl->id.name, sizeof(kctl->id.name),
+ "%s Validity", name);
+ else
+ snprintf(kctl->id.name, sizeof(kctl->id.name),
+ "Clock Source %d Validity", hdr->bClockID);
+
+ return snd_usb_mixer_add_control(&cval->head, kctl);
+}
+
/*
* parse a feature unit
*
@@ -2126,10 +2192,11 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
switch (p1[2]) {
case UAC_INPUT_TERMINAL:
- case UAC2_CLOCK_SOURCE:
return 0; /* NOP */
case UAC_MIXER_UNIT:
return parse_audio_mixer_unit(state, unitid, p1);
+ case UAC2_CLOCK_SOURCE:
+ return parse_clock_source_unit(state, unitid, p1);
case UAC_SELECTOR_UNIT:
case UAC2_CLOCK_SELECTOR:
return parse_audio_selector_unit(state, unitid, p1);
@@ -2307,6 +2374,7 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer,
__u8 unitid = (index >> 8) & 0xff;
__u8 control = (value >> 8) & 0xff;
__u8 channel = value & 0xff;
+ unsigned int count = 0;
if (channel >= MAX_CHANNELS) {
usb_audio_dbg(mixer->chip,
@@ -2315,6 +2383,12 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer,
return;
}
+ for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem)
+ count++;
+
+ if (count == 0)
+ return;
+
for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem) {
struct usb_mixer_elem_info *info;
@@ -2322,7 +2396,7 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer,
continue;
info = (struct usb_mixer_elem_info *)list;
- if (info->control != control)
+ if (count > 1 && info->control != control)
continue;
switch (attribute) {
diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c
index ddca6547399b..1f8fb0d904e0 100644
--- a/sound/usb/mixer_maps.c
+++ b/sound/usb/mixer_maps.c
@@ -349,6 +349,16 @@ static struct usbmix_name_map bose_companion5_map[] = {
};
/*
+ * Dell usb dock with ALC4020 codec had a firmware problem where it got
+ * screwed up when zero volume is passed; just skip it as a workaround
+ */
+static const struct usbmix_name_map dell_alc4020_map[] = {
+ { 16, NULL },
+ { 19, NULL },
+ { 0 }
+};
+
+/*
* Control map entries
*/
@@ -431,6 +441,10 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = {
.map = aureon_51_2_map,
},
{
+ .id = USB_ID(0x0bda, 0x4014),
+ .map = dell_alc4020_map,
+ },
+ {
.id = USB_ID(0x0dba, 0x1000),
.map = mbox1_map,
},
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 6178bb5d0731..6adde457b602 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -1134,9 +1134,14 @@ bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip)
case USB_ID(0x045E, 0x076F): /* MS Lifecam HD-6000 */
case USB_ID(0x045E, 0x0772): /* MS Lifecam Studio */
case USB_ID(0x045E, 0x0779): /* MS Lifecam HD-3000 */
+ case USB_ID(0x047F, 0x0415): /* Plantronics BT-300 */
case USB_ID(0x047F, 0xAA05): /* Plantronics DA45 */
case USB_ID(0x04D8, 0xFEEA): /* Benchmark DAC1 Pre */
+ case USB_ID(0x0556, 0x0014): /* Phoenix Audio TMX320VC */
case USB_ID(0x074D, 0x3553): /* Outlaw RR2150 (Micronas UAC3553B) */
+ case USB_ID(0x1de7, 0x0013): /* Phoenix Audio MT202exe */
+ case USB_ID(0x1de7, 0x0014): /* Phoenix Audio TMX320 */
+ case USB_ID(0x1de7, 0x0114): /* Phoenix Audio MT202pcs */
case USB_ID(0x21B4, 0x0081): /* AudioQuest DragonFly */
return true;
}
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index b665d85555cb..4d5c89a7ba2b 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -47,6 +47,7 @@ struct snd_usb_audio {
int num_interfaces;
int num_suspended_intf;
+ int sample_rate_read_error;
struct list_head pcm_list; /* list of pcm streams */
struct list_head ep_list; /* list of audio-related endpoints */
OpenPOWER on IntegriCloud