summary refs log tree commit diff
path: root/sound/usb
diff options
context:
space:
mode:
Diffstat (limited to 'sound/usb')
-rw-r--r--sound/usb/card.c7
-rw-r--r--sound/usb/card.h2
-rw-r--r--sound/usb/clock.c228
-rw-r--r--sound/usb/clock.h4
-rw-r--r--sound/usb/format.c91
-rw-r--r--sound/usb/format.h6
-rw-r--r--sound/usb/mixer.c337
-rw-r--r--sound/usb/stream.c365
8 files changed, 848 insertions, 192 deletions
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 8018d56cfecc..4a1c6bb3dfa0 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -7,6 +7,7 @@
  *	    Alan Cox (alan@lxorguk.ukuu.org.uk)
  *	    Thomas Sailer (sailer@ife.ee.ethz.ch)
  *
+ *   Audio Class 3.0 support by Ruslan Bilovol <ruslan.bilovol@gmail.com>
  *
  *   This program is free software; you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -44,6 +45,7 @@
 #include <linux/mutex.h>
 #include <linux/usb/audio.h>
 #include <linux/usb/audio-v2.h>
+#include <linux/usb/audio-v3.h>
 #include <linux/module.h>
 
 #include <sound/control.h>
@@ -281,7 +283,8 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
 		break;
 	}
 
-	case UAC_VERSION_2: {
+	case UAC_VERSION_2:
+	case UAC_VERSION_3: {
 		struct usb_interface_assoc_descriptor *assoc =
 			usb_ifnum_to_if(dev, ctrlif)->intf_assoc;
 
@@ -301,7 +304,7 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
 		}
 
 		if (!assoc) {
-			dev_err(&dev->dev, "Audio class v2 interfaces need an interface association\n");
+			dev_err(&dev->dev, "Audio class v2/v3 interfaces need an interface association\n");
 			return -EINVAL;
 		}
 
diff --git a/sound/usb/card.h b/sound/usb/card.h
index ed87cc83eb47..1406292d50ec 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -22,7 +22,7 @@ struct audioformat {
 	unsigned char endpoint;		/* endpoint */
 	unsigned char ep_attr;		/* endpoint attributes */
 	unsigned char datainterval;	/* log_2 of data packet interval */
-	unsigned char protocol;		/* UAC_VERSION_1/2 */
+	unsigned char protocol;		/* UAC_VERSION_1/2/3 */
 	unsigned int maxpacksize;	/* max. packet size */
 	unsigned int rates;		/* rate bitmasks */
 	unsigned int rate_min, rate_max;	/* min/max rates */
diff --git a/sound/usb/clock.c b/sound/usb/clock.c
index eb3396ffba4c..25de7fe285d9 100644
--- a/sound/usb/clock.c
+++ b/sound/usb/clock.c
@@ -23,6 +23,7 @@
 #include <linux/usb.h>
 #include <linux/usb/audio.h>
 #include <linux/usb/audio-v2.h>
+#include <linux/usb/audio-v3.h>
 
 #include <sound/core.h>
 #include <sound/info.h>
@@ -50,6 +51,22 @@ static struct uac_clock_source_descriptor *
 	return NULL;
 }
 
+static struct uac3_clock_source_descriptor *
+	snd_usb_find_clock_source_v3(struct usb_host_interface *ctrl_iface,
+				  int clock_id)
+{
+	struct uac3_clock_source_descriptor *cs = NULL;
+
+	while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra,
+					     ctrl_iface->extralen,
+					     cs, UAC3_CLOCK_SOURCE))) {
+		if (cs->bClockID == clock_id)
+			return cs;
+	}
+
+	return NULL;
+}
+
 static struct uac_clock_selector_descriptor *
 	snd_usb_find_clock_selector(struct usb_host_interface *ctrl_iface,
 				    int clock_id)
@@ -69,6 +86,22 @@ static struct uac_clock_selector_descriptor *
 	return NULL;
 }
 
+static struct uac3_clock_selector_descriptor *
+	snd_usb_find_clock_selector_v3(struct usb_host_interface *ctrl_iface,
+				    int clock_id)
+{
+	struct uac3_clock_selector_descriptor *cs = NULL;
+
+	while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra,
+					     ctrl_iface->extralen,
+					     cs, UAC3_CLOCK_SELECTOR))) {
+		if (cs->bClockID == clock_id)
+			return cs;
+	}
+
+	return NULL;
+}
+
 static struct uac_clock_multiplier_descriptor *
 	snd_usb_find_clock_multiplier(struct usb_host_interface *ctrl_iface,
 				      int clock_id)
@@ -85,6 +118,22 @@ static struct uac_clock_multiplier_descriptor *
 	return NULL;
 }
 
+static struct uac3_clock_multiplier_descriptor *
+	snd_usb_find_clock_multiplier_v3(struct usb_host_interface *ctrl_iface,
+				      int clock_id)
+{
+	struct uac3_clock_multiplier_descriptor *cs = NULL;
+
+	while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra,
+					     ctrl_iface->extralen,
+					     cs, UAC3_CLOCK_MULTIPLIER))) {
+		if (cs->bClockID == clock_id)
+			return cs;
+	}
+
+	return NULL;
+}
+
 static int uac_clock_selector_get_val(struct snd_usb_audio *chip, int selector_id)
 {
 	unsigned char buf;
@@ -138,19 +187,33 @@ static int uac_clock_selector_set_val(struct snd_usb_audio *chip, int selector_i
 	return ret;
 }
 
-static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id)
+static bool uac_clock_source_is_valid(struct snd_usb_audio *chip,
+				      int protocol,
+				      int source_id)
 {
 	int err;
 	unsigned char data;
 	struct usb_device *dev = chip->dev;
-	struct uac_clock_source_descriptor *cs_desc =
-		snd_usb_find_clock_source(chip->ctrl_intf, source_id);
-
-	if (!cs_desc)
-		return 0;
+	u32 bmControls;
+
+	if (protocol == UAC_VERSION_3) {
+		struct uac3_clock_source_descriptor *cs_desc =
+			snd_usb_find_clock_source_v3(chip->ctrl_intf, source_id);
+
+		if (!cs_desc)
+			return 0;
+		bmControls = le32_to_cpu(cs_desc->bmControls);
+	} else { /* UAC_VERSION_1/2 */
+		struct uac_clock_source_descriptor *cs_desc =
+			snd_usb_find_clock_source(chip->ctrl_intf, source_id);
+
+		if (!cs_desc)
+			return 0;
+		bmControls = cs_desc->bmControls;
+	}
 
 	/* If a clock source can't tell us whether it's valid, we assume it is */
-	if (!uac2_control_is_readable(cs_desc->bmControls,
+	if (!uac_v2v3_control_is_readable(bmControls,
 				      UAC2_CS_CONTROL_CLOCK_VALID - 1))
 		return 1;
 
@@ -170,9 +233,8 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id)
 	return !!data;
 }
 
-static int __uac_clock_find_source(struct snd_usb_audio *chip,
-				   int entity_id, unsigned long *visited,
-				   bool validate)
+static int __uac_clock_find_source(struct snd_usb_audio *chip, int entity_id,
+				   unsigned long *visited, bool validate)
 {
 	struct uac_clock_source_descriptor *source;
 	struct uac_clock_selector_descriptor *selector;
@@ -191,7 +253,8 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip,
 	source = snd_usb_find_clock_source(chip->ctrl_intf, entity_id);
 	if (source) {
 		entity_id = source->bClockID;
-		if (validate && !uac_clock_source_is_valid(chip, entity_id)) {
+		if (validate && !uac_clock_source_is_valid(chip, UAC_VERSION_2,
+								entity_id)) {
 			usb_audio_err(chip,
 				"clock source %d is not valid, cannot use\n",
 				entity_id);
@@ -260,6 +323,97 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip,
 	return -EINVAL;
 }
 
+static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id,
+				   unsigned long *visited, bool validate)
+{
+	struct uac3_clock_source_descriptor *source;
+	struct uac3_clock_selector_descriptor *selector;
+	struct uac3_clock_multiplier_descriptor *multiplier;
+
+	entity_id &= 0xff;
+
+	if (test_and_set_bit(entity_id, visited)) {
+		usb_audio_warn(chip,
+			 "%s(): recursive clock topology detected, id %d.\n",
+			 __func__, entity_id);
+		return -EINVAL;
+	}
+
+	/* first, see if the ID we're looking for is a clock source already */
+	source = snd_usb_find_clock_source_v3(chip->ctrl_intf, entity_id);
+	if (source) {
+		entity_id = source->bClockID;
+		if (validate && !uac_clock_source_is_valid(chip, UAC_VERSION_3,
+								entity_id)) {
+			usb_audio_err(chip,
+				"clock source %d is not valid, cannot use\n",
+				entity_id);
+			return -ENXIO;
+		}
+		return entity_id;
+	}
+
+	selector = snd_usb_find_clock_selector_v3(chip->ctrl_intf, entity_id);
+	if (selector) {
+		int ret, i, cur;
+
+		/* the entity ID we are looking for is a selector.
+		 * find out what it currently selects */
+		ret = uac_clock_selector_get_val(chip, selector->bClockID);
+		if (ret < 0)
+			return ret;
+
+		/* Selector values are one-based */
+
+		if (ret > selector->bNrInPins || ret < 1) {
+			usb_audio_err(chip,
+				"%s(): selector reported illegal value, id %d, ret %d\n",
+				__func__, selector->bClockID, ret);
+
+			return -EINVAL;
+		}
+
+		cur = ret;
+		ret = __uac3_clock_find_source(chip, selector->baCSourceID[ret - 1],
+					       visited, validate);
+		if (!validate || ret > 0 || !chip->autoclock)
+			return ret;
+
+		/* The current clock source is invalid, try others. */
+		for (i = 1; i <= selector->bNrInPins; i++) {
+			int err;
+
+			if (i == cur)
+				continue;
+
+			ret = __uac3_clock_find_source(chip, selector->baCSourceID[i - 1],
+				visited, true);
+			if (ret < 0)
+				continue;
+
+			err = uac_clock_selector_set_val(chip, entity_id, i);
+			if (err < 0)
+				continue;
+
+			usb_audio_info(chip,
+				 "found and selected valid clock source %d\n",
+				 ret);
+			return ret;
+		}
+
+		return -ENXIO;
+	}
+
+	/* FIXME: multipliers only act as pass-thru element for now */
+	multiplier = snd_usb_find_clock_multiplier_v3(chip->ctrl_intf,
+						      entity_id);
+	if (multiplier)
+		return __uac3_clock_find_source(chip, multiplier->bCSourceID,
+						visited, validate);
+
+	return -EINVAL;
+}
+
 /*
  * For all kinds of sample rate settings and other device queries,
  * the clock source (end-leaf) must be used. However, clock selectors,
@@ -271,12 +425,22 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip,
  *
  * Returns the clock source UnitID (>=0) on success, or an error.
  */
-int snd_usb_clock_find_source(struct snd_usb_audio *chip, int entity_id,
-			      bool validate)
+int snd_usb_clock_find_source(struct snd_usb_audio *chip, int protocol,
+			      int entity_id, bool validate)
 {
 	DECLARE_BITMAP(visited, 256);
 	memset(visited, 0, sizeof(visited));
-	return __uac_clock_find_source(chip, entity_id, visited, validate);
+
+	switch (protocol) {
+	case UAC_VERSION_2:
+		return __uac_clock_find_source(chip, entity_id, visited,
+					       validate);
+	case UAC_VERSION_3:
+		return __uac3_clock_find_source(chip, entity_id, visited,
+					       validate);
+	default:
+		return -EINVAL;
+	}
 }
 
 static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
@@ -335,7 +499,7 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
 	return 0;
 }
 
-static int get_sample_rate_v2(struct snd_usb_audio *chip, int iface,
+static int get_sample_rate_v2v3(struct snd_usb_audio *chip, int iface,
 			      int altsetting, int clock)
 {
 	struct usb_device *dev = chip->dev;
@@ -348,7 +512,7 @@ static int get_sample_rate_v2(struct snd_usb_audio *chip, int iface,
 			      snd_usb_ctrl_intf(chip) | (clock << 8),
 			      &data, sizeof(data));
 	if (err < 0) {
-		dev_warn(&dev->dev, "%d:%d: cannot get freq (v2): err %d\n",
+		dev_warn(&dev->dev, "%d:%d: cannot get freq (v2/v3): err %d\n",
 			 iface, altsetting, err);
 		return 0;
 	}
@@ -356,7 +520,7 @@ static int get_sample_rate_v2(struct snd_usb_audio *chip, int iface,
 	return le32_to_cpu(data);
 }
 
-static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
+static int set_sample_rate_v2v3(struct snd_usb_audio *chip, int iface,
 			      struct usb_host_interface *alts,
 			      struct audioformat *fmt, int rate)
 {
@@ -365,18 +529,30 @@ static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
 	int err, cur_rate, prev_rate;
 	int clock;
 	bool writeable;
-	struct uac_clock_source_descriptor *cs_desc;
+	u32 bmControls;
 
-	clock = snd_usb_clock_find_source(chip, fmt->clock, true);
+	clock = snd_usb_clock_find_source(chip, fmt->protocol,
+					  fmt->clock, true);
 	if (clock < 0)
 		return clock;
 
-	prev_rate = get_sample_rate_v2(chip, iface, fmt->altsetting, clock);
+	prev_rate = get_sample_rate_v2v3(chip, iface, fmt->altsetting, clock);
 	if (prev_rate == rate)
 		return 0;
 
-	cs_desc = snd_usb_find_clock_source(chip->ctrl_intf, clock);
-	writeable = uac2_control_is_writeable(cs_desc->bmControls, UAC2_CS_CONTROL_SAM_FREQ - 1);
+	if (fmt->protocol == UAC_VERSION_3) {
+		struct uac3_clock_source_descriptor *cs_desc;
+
+		cs_desc = snd_usb_find_clock_source_v3(chip->ctrl_intf, clock);
+		bmControls = le32_to_cpu(cs_desc->bmControls);
+	} else {
+		struct uac_clock_source_descriptor *cs_desc;
+
+		cs_desc = snd_usb_find_clock_source(chip->ctrl_intf, clock);
+		bmControls = cs_desc->bmControls;
+	}
+
+	writeable = uac_v2v3_control_is_writeable(bmControls, UAC2_CS_CONTROL_SAM_FREQ - 1);
 	if (writeable) {
 		data = cpu_to_le32(rate);
 		err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR,
@@ -386,12 +562,13 @@ static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
 				      &data, sizeof(data));
 		if (err < 0) {
 			usb_audio_err(chip,
-				"%d:%d: cannot set freq %d (v2): err %d\n",
+				"%d:%d: cannot set freq %d (v2/v3): err %d\n",
 				iface, fmt->altsetting, rate, err);
 			return err;
 		}
 
-		cur_rate = get_sample_rate_v2(chip, iface, fmt->altsetting, clock);
+		cur_rate = get_sample_rate_v2v3(chip, iface,
+						fmt->altsetting, clock);
 	} else {
 		cur_rate = prev_rate;
 	}
@@ -430,7 +607,8 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
 		return set_sample_rate_v1(chip, iface, alts, fmt, rate);
 
 	case UAC_VERSION_2:
-		return set_sample_rate_v2(chip, iface, alts, fmt, rate);
+	case UAC_VERSION_3:
+		return set_sample_rate_v2v3(chip, iface, alts, fmt, rate);
 	}
 }
 
diff --git a/sound/usb/clock.h b/sound/usb/clock.h
index 87557cae1a0b..076e31b79ee0 100644
--- a/sound/usb/clock.h
+++ b/sound/usb/clock.h
@@ -6,7 +6,7 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
 			     struct usb_host_interface *alts,
 			     struct audioformat *fmt, int rate);
 
-int snd_usb_clock_find_source(struct snd_usb_audio *chip, int entity_id,
-			     bool validate);
+int snd_usb_clock_find_source(struct snd_usb_audio *chip, int protocol,
+			     int entity_id, bool validate);
 
 #endif /* __USBAUDIO_CLOCK_H */
diff --git a/sound/usb/format.c b/sound/usb/format.c
index 2c44386e5569..edbe67eeddfa 100644
--- a/sound/usb/format.c
+++ b/sound/usb/format.c
@@ -20,6 +20,7 @@
 #include <linux/usb.h>
 #include <linux/usb/audio.h>
 #include <linux/usb/audio-v2.h>
+#include <linux/usb/audio-v3.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -39,11 +40,11 @@
  * @dev: usb device
  * @fp: audioformat record
  * @format: the format tag (wFormatTag)
- * @fmt: the format type descriptor
+ * @fmt: the format type descriptor (v1/v2) or AudioStreaming descriptor (v3)
  */
 static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
 				     struct audioformat *fp,
-				     unsigned int format, void *_fmt)
+				     u64 format, void *_fmt)
 {
 	int sample_width, sample_bytes;
 	u64 pcm_formats = 0;
@@ -69,6 +70,18 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
 		format <<= 1;
 		break;
 	}
+	case UAC_VERSION_3: {
+		struct uac3_as_header_descriptor *as = _fmt;
+
+		sample_width = as->bBitResolution;
+		sample_bytes = as->bSubslotSize;
+
+		if (format & UAC3_FORMAT_TYPE_I_RAW_DATA)
+			pcm_formats |= SNDRV_PCM_FMTBIT_SPECIAL;
+
+		format <<= 1;
+		break;
+	}
 	}
 
 	if ((pcm_formats == 0) &&
@@ -137,7 +150,7 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
 	}
 	if (format & ~0x3f) {
 		usb_audio_info(chip,
-			 "%u:%d : unsupported format bits %#x\n",
+			 "%u:%d : unsupported format bits %#llx\n",
 			 fp->iface, fp->altsetting, format);
 	}
 
@@ -281,15 +294,16 @@ static int parse_uac2_sample_rate_range(struct snd_usb_audio *chip,
 
 /*
  * parse the format descriptor and stores the possible sample rates
- * on the audioformat table (audio class v2).
+ * on the audioformat table (audio class v2 and v3).
  */
-static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
+static int parse_audio_format_rates_v2v3(struct snd_usb_audio *chip,
 				       struct audioformat *fp)
 {
 	struct usb_device *dev = chip->dev;
 	unsigned char tmp[2], *data;
 	int nr_triplets, data_size, ret = 0;
-	int clock = snd_usb_clock_find_source(chip, fp->clock, false);
+	int clock = snd_usb_clock_find_source(chip, fp->protocol,
+					      fp->clock, false);
 
 	if (clock < 0) {
 		dev_err(&dev->dev,
@@ -368,13 +382,30 @@ err:
  * parse the format type I and III descriptors
  */
 static int parse_audio_format_i(struct snd_usb_audio *chip,
-				struct audioformat *fp, unsigned int format,
-				struct uac_format_type_i_continuous_descriptor *fmt)
+				struct audioformat *fp, u64 format,
+				void *_fmt)
 {
 	snd_pcm_format_t pcm_format;
+	unsigned int fmt_type;
 	int ret;
 
-	if (fmt->bFormatType == UAC_FORMAT_TYPE_III) {
+	switch (fp->protocol) {
+	default:
+	case UAC_VERSION_1:
+	case UAC_VERSION_2: {
+		struct uac_format_type_i_continuous_descriptor *fmt = _fmt;
+
+		fmt_type = fmt->bFormatType;
+		break;
+	}
+	case UAC_VERSION_3: {
+		/* fp->fmt_type is already set in this case */
+		fmt_type = fp->fmt_type;
+		break;
+	}
+	}
+
+	if (fmt_type == UAC_FORMAT_TYPE_III) {
 		/* FIXME: the format type is really IECxxx
 		 *        but we give normal PCM format to get the existing
 		 *        apps working...
@@ -393,7 +424,7 @@ static int parse_audio_format_i(struct snd_usb_audio *chip,
 		}
 		fp->formats = pcm_format_to_bits(pcm_format);
 	} else {
-		fp->formats = parse_audio_format_i_type(chip, fp, format, fmt);
+		fp->formats = parse_audio_format_i_type(chip, fp, format, _fmt);
 		if (!fp->formats)
 			return -EINVAL;
 	}
@@ -405,15 +436,20 @@ static int parse_audio_format_i(struct snd_usb_audio *chip,
 	 */
 	switch (fp->protocol) {
 	default:
-	case UAC_VERSION_1:
+	case UAC_VERSION_1: {
+		struct uac_format_type_i_continuous_descriptor *fmt = _fmt;
+
 		fp->channels = fmt->bNrChannels;
 		ret = parse_audio_format_rates_v1(chip, fp, (unsigned char *) fmt, 7);
 		break;
+	}
 	case UAC_VERSION_2:
+	case UAC_VERSION_3: {
 		/* fp->channels is already set in this case */
-		ret = parse_audio_format_rates_v2(chip, fp);
+		ret = parse_audio_format_rates_v2v3(chip, fp);
 		break;
 	}
+	}
 
 	if (fp->channels < 1) {
 		usb_audio_err(chip,
@@ -430,7 +466,7 @@ static int parse_audio_format_i(struct snd_usb_audio *chip,
  */
 static int parse_audio_format_ii(struct snd_usb_audio *chip,
 				 struct audioformat *fp,
-				 int format, void *_fmt)
+				 u64 format, void *_fmt)
 {
 	int brate, framesize, ret;
 
@@ -445,7 +481,7 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip,
 		break;
 	default:
 		usb_audio_info(chip,
-			 "%u:%d : unknown format tag %#x is detected.  processed as MPEG.\n",
+			 "%u:%d : unknown format tag %#llx is detected.  processed as MPEG.\n",
 			 fp->iface, fp->altsetting, format);
 		fp->formats = SNDRV_PCM_FMTBIT_MPEG;
 		break;
@@ -470,7 +506,7 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip,
 		framesize = le16_to_cpu(fmt->wSamplesPerFrame);
 		usb_audio_info(chip, "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize);
 		fp->frame_size = framesize;
-		ret = parse_audio_format_rates_v2(chip, fp);
+		ret = parse_audio_format_rates_v2v3(chip, fp);
 		break;
 	}
 	}
@@ -479,7 +515,7 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip,
 }
 
 int snd_usb_parse_audio_format(struct snd_usb_audio *chip,
-			       struct audioformat *fp, unsigned int format,
+			       struct audioformat *fp, u64 format,
 			       struct uac_format_type_i_continuous_descriptor *fmt,
 			       int stream)
 {
@@ -520,3 +556,26 @@ int snd_usb_parse_audio_format(struct snd_usb_audio *chip,
 	return 0;
 }
 
+int snd_usb_parse_audio_format_v3(struct snd_usb_audio *chip,
+			       struct audioformat *fp,
+			       struct uac3_as_header_descriptor *as,
+			       int stream)
+{
+	u64 format = le64_to_cpu(as->bmFormats);
+	int err;
+
+	/*
+	 * Type I format bits are D0..D6
+	 * This test works because type IV is not supported
+	 */
+	if (format & 0x7f)
+		fp->fmt_type = UAC_FORMAT_TYPE_I;
+	else
+		fp->fmt_type = UAC_FORMAT_TYPE_III;
+
+	err = parse_audio_format_i(chip, fp, format, as);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
diff --git a/sound/usb/format.h b/sound/usb/format.h
index 8c3ff9ce0824..e70171892f32 100644
--- a/sound/usb/format.h
+++ b/sound/usb/format.h
@@ -3,8 +3,12 @@
 #define __USBAUDIO_FORMAT_H
 
 int snd_usb_parse_audio_format(struct snd_usb_audio *chip,
-			       struct audioformat *fp, unsigned int format,
+			       struct audioformat *fp, u64 format,
 			       struct uac_format_type_i_continuous_descriptor *fmt,
 			       int stream);
 
+int snd_usb_parse_audio_format_v3(struct snd_usb_audio *chip,
+			       struct audioformat *fp,
+			       struct uac3_as_header_descriptor *as,
+			       int stream);
 #endif /*  __USBAUDIO_FORMAT_H */
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 06b22624ab7a..1c02f7373eda 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -51,6 +51,7 @@
 #include <linux/usb.h>
 #include <linux/usb/audio.h>
 #include <linux/usb/audio-v2.h>
+#include <linux/usb/audio-v3.h>
 
 #include <sound/core.h>
 #include <sound/control.h>
@@ -189,7 +190,7 @@ static void *find_audio_control_unit(struct mixer_build *state,
 					USB_DT_CS_INTERFACE)) != NULL) {
 		if (hdr->bLength >= 4 &&
 		    hdr->bDescriptorSubtype >= UAC_INPUT_TERMINAL &&
-		    hdr->bDescriptorSubtype <= UAC2_SAMPLE_RATE_CONVERTER &&
+		    hdr->bDescriptorSubtype <= UAC3_SAMPLE_RATE_CONVERTER &&
 		    hdr->bUnitID == unit)
 			return hdr;
 	}
@@ -468,9 +469,10 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
 
 	validx += cval->idx_off;
 
+
 	if (cval->head.mixer->protocol == UAC_VERSION_1) {
 		val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
-	} else { /* UAC_VERSION_2 */
+	} else { /* UAC_VERSION_2/3 */
 		val_len = uac2_ctl_value_size(cval->val_type);
 
 		/* FIXME */
@@ -723,6 +725,7 @@ static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm
 static int check_input_term(struct mixer_build *state, int id,
 			    struct usb_audio_term *term)
 {
+	int protocol = state->mixer->protocol;
 	int err;
 	void *p1;
 
@@ -730,16 +733,104 @@ static int check_input_term(struct mixer_build *state, int id,
 	while ((p1 = find_audio_control_unit(state, id)) != NULL) {
 		unsigned char *hdr = p1;
 		term->id = id;
-		switch (hdr[2]) {
-		case UAC_INPUT_TERMINAL:
-			if (state->mixer->protocol == UAC_VERSION_1) {
-				struct uac_input_terminal_descriptor *d = p1;
-				term->type = le16_to_cpu(d->wTerminalType);
-				term->channels = d->bNrChannels;
-				term->chconfig = le16_to_cpu(d->wChannelConfig);
-				term->name = d->iTerminal;
-			} else { /* UAC_VERSION_2 */
-				struct uac2_input_terminal_descriptor *d = p1;
+
+		if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) {
+			switch (hdr[2]) {
+			case UAC_INPUT_TERMINAL:
+				if (protocol == UAC_VERSION_1) {
+					struct uac_input_terminal_descriptor *d = p1;
+
+					term->type = le16_to_cpu(d->wTerminalType);
+					term->channels = d->bNrChannels;
+					term->chconfig = le16_to_cpu(d->wChannelConfig);
+					term->name = d->iTerminal;
+				} else { /* UAC_VERSION_2 */
+					struct uac2_input_terminal_descriptor *d = p1;
+
+					/* call recursively to verify that the
+					 * referenced clock entity is valid */
+					err = check_input_term(state, d->bCSourceID, term);
+					if (err < 0)
+						return err;
+
+					/* save input term properties after recursion,
+					 * to ensure they are not overriden by the
+					 * recursion calls */
+					term->id = id;
+					term->type = le16_to_cpu(d->wTerminalType);
+					term->channels = d->bNrChannels;
+					term->chconfig = le32_to_cpu(d->bmChannelConfig);
+					term->name = d->iTerminal;
+				}
+				return 0;
+			case UAC_FEATURE_UNIT: {
+				/* the header is the same for v1 and v2 */
+				struct uac_feature_unit_descriptor *d = p1;
+
+				id = d->bSourceID;
+				break; /* continue to parse */
+			}
+			case UAC_MIXER_UNIT: {
+				struct uac_mixer_unit_descriptor *d = p1;
+
+				term->type = d->bDescriptorSubtype << 16; /* virtual type */
+				term->channels = uac_mixer_unit_bNrChannels(d);
+				term->chconfig = uac_mixer_unit_wChannelConfig(d, protocol);
+				term->name = uac_mixer_unit_iMixer(d);
+				return 0;
+			}
+			case UAC_SELECTOR_UNIT:
+			case UAC2_CLOCK_SELECTOR: {
+				struct uac_selector_unit_descriptor *d = p1;
+				/* call recursively to retrieve the channel info */
+				err = check_input_term(state, d->baSourceID[0], term);
+				if (err < 0)
+					return err;
+				term->type = d->bDescriptorSubtype << 16; /* virtual type */
+				term->id = id;
+				term->name = uac_selector_unit_iSelector(d);
+				return 0;
+			}
+			case UAC1_PROCESSING_UNIT:
+			case UAC1_EXTENSION_UNIT:
+			/* UAC2_PROCESSING_UNIT_V2 */
+			/* UAC2_EFFECT_UNIT */
+			case UAC2_EXTENSION_UNIT_V2: {
+				struct uac_processing_unit_descriptor *d = p1;
+
+				if (protocol == UAC_VERSION_2 &&
+					hdr[2] == UAC2_EFFECT_UNIT) {
+					/* UAC2/UAC1 unit IDs overlap here in an
+					 * uncompatible way. Ignore this unit for now.
+					 */
+					return 0;
+				}
+
+				if (d->bNrInPins) {
+					id = d->baSourceID[0];
+					break; /* continue to parse */
+				}
+				term->type = d->bDescriptorSubtype << 16; /* virtual type */
+				term->channels = uac_processing_unit_bNrChannels(d);
+				term->chconfig = uac_processing_unit_wChannelConfig(d, protocol);
+				term->name = uac_processing_unit_iProcessing(d, protocol);
+				return 0;
+			}
+			case UAC2_CLOCK_SOURCE: {
+				struct uac_clock_source_descriptor *d = p1;
+
+				term->type = d->bDescriptorSubtype << 16; /* virtual type */
+				term->id = id;
+				term->name = d->iClockSource;
+				return 0;
+			}
+			default:
+				return -ENODEV;
+			}
+		} else { /* UAC_VERSION_3 */
+			switch (hdr[2]) {
+			case UAC_INPUT_TERMINAL: {
+				struct uac3_input_terminal_descriptor *d = p1;
 
 				/* call recursively to verify that the
 				 * referenced clock entity is valid */
@@ -752,71 +843,31 @@ static int check_input_term(struct mixer_build *state, int id,
 				 * recursion calls */
 				term->id = id;
 				term->type = le16_to_cpu(d->wTerminalType);
-				term->channels = d->bNrChannels;
-				term->chconfig = le32_to_cpu(d->bmChannelConfig);
-				term->name = d->iTerminal;
-			}
-			return 0;
-		case UAC_FEATURE_UNIT: {
-			/* the header is the same for v1 and v2 */
-			struct uac_feature_unit_descriptor *d = p1;
-			id = d->bSourceID;
-			break; /* continue to parse */
-		}
-		case UAC_MIXER_UNIT: {
-			struct uac_mixer_unit_descriptor *d = p1;
-			term->type = d->bDescriptorSubtype << 16; /* virtual type */
-			term->channels = uac_mixer_unit_bNrChannels(d);
-			term->chconfig = uac_mixer_unit_wChannelConfig(d, state->mixer->protocol);
-			term->name = uac_mixer_unit_iMixer(d);
-			return 0;
-		}
-		case UAC_SELECTOR_UNIT:
-		case UAC2_CLOCK_SELECTOR: {
-			struct uac_selector_unit_descriptor *d = p1;
-			/* call recursively to retrieve the channel info */
-			err = check_input_term(state, d->baSourceID[0], term);
-			if (err < 0)
-				return err;
-			term->type = d->bDescriptorSubtype << 16; /* virtual type */
-			term->id = id;
-			term->name = uac_selector_unit_iSelector(d);
-			return 0;
-		}
-		case UAC1_PROCESSING_UNIT:
-		case UAC1_EXTENSION_UNIT:
-		/* UAC2_PROCESSING_UNIT_V2 */
-		/* UAC2_EFFECT_UNIT */
-		case UAC2_EXTENSION_UNIT_V2: {
-			struct uac_processing_unit_descriptor *d = p1;
-
-			if (state->mixer->protocol == UAC_VERSION_2 &&
-				hdr[2] == UAC2_EFFECT_UNIT) {
-				/* UAC2/UAC1 unit IDs overlap here in an
-				 * uncompatible way. Ignore this unit for now.
-				 */
+
+				/* REVISIT: UAC3 IT doesn't have channels/cfg */
+				term->channels = 0;
+				term->chconfig = 0;
+
+				term->name = le16_to_cpu(d->wTerminalDescrStr);
 				return 0;
 			}
+			case UAC3_FEATURE_UNIT: {
+				struct uac3_feature_unit_descriptor *d = p1;
 
-			if (d->bNrInPins) {
-				id = d->baSourceID[0];
+				id = d->bSourceID;
 				break; /* continue to parse */
 			}
-			term->type = d->bDescriptorSubtype << 16; /* virtual type */
-			term->channels = uac_processing_unit_bNrChannels(d);
-			term->chconfig = uac_processing_unit_wChannelConfig(d, state->mixer->protocol);
-			term->name = uac_processing_unit_iProcessing(d, state->mixer->protocol);
-			return 0;
-		}
-		case UAC2_CLOCK_SOURCE: {
-			struct uac_clock_source_descriptor *d = p1;
-			term->type = d->bDescriptorSubtype << 16; /* virtual type */
-			term->id = id;
-			term->name = d->iClockSource;
-			return 0;
-		}
-		default:
-			return -ENODEV;
+			case UAC3_CLOCK_SOURCE: {
+				struct uac3_clock_source_descriptor *d = p1;
+
+				term->type = d->bDescriptorSubtype << 16; /* virtual type */
+				term->id = id;
+				term->name = le16_to_cpu(d->wClockSourceStr);
+				return 0;
+			}
+			default:
+				return -ENODEV;
+			}
 		}
 	}
 	return -ENODEV;
@@ -1423,7 +1474,7 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid,
 	 * 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,
+	if (!uac_v2v3_control_is_readable(hdr->bmControls,
 				      ilog2(UAC2_CS_CONTROL_CLOCK_VALID)))
 		return 0;
 
@@ -1439,7 +1490,7 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid,
 	cval->val_type = USB_MIXER_BOOLEAN;
 	cval->control = UAC2_CS_CONTROL_CLOCK_VALID;
 
-	if (uac2_control_is_writeable(hdr->bmControls,
+	if (uac_v2v3_control_is_writeable(hdr->bmControls,
 				      ilog2(UAC2_CS_CONTROL_CLOCK_VALID)))
 		kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
 	else {
@@ -1502,7 +1553,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
 				      unitid);
 			return -EINVAL;
 		}
-	} else {
+	} else if (state->mixer->protocol == UAC_VERSION_2) {
 		struct uac2_feature_unit_descriptor *ftr = _ftr;
 		if (hdr->bLength < 6) {
 			usb_audio_err(state->chip,
@@ -1519,6 +1570,24 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
 				      unitid);
 			return -EINVAL;
 		}
+	} else { /* UAC_VERSION_3 */
+		struct uac3_feature_unit_descriptor *ftr = _ftr;
+
+		if (hdr->bLength < 7) {
+			usb_audio_err(state->chip,
+				      "unit %u: invalid UAC3_FEATURE_UNIT descriptor\n",
+				      unitid);
+			return -EINVAL;
+		}
+		csize = 4;
+		channels = (ftr->bLength - 7) / 4 - 1;
+		bmaControls = ftr->bmaControls;
+		if (hdr->bLength < 7 + csize) {
+			usb_audio_err(state->chip,
+				      "unit %u: invalid UAC3_FEATURE_UNIT descriptor\n",
+				      unitid);
+			return -EINVAL;
+		}
 	}
 
 	/* parse the source unit */
@@ -1577,7 +1646,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
 				build_feature_ctl(state, _ftr, 0, i, &iterm,
 						  unitid, 0);
 		}
-	} else { /* UAC_VERSION_2 */
+	} else { /* UAC_VERSION_2/3 */
 		for (i = 0; i < ARRAY_SIZE(audio_feature_info); i++) {
 			unsigned int ch_bits = 0;
 			unsigned int ch_read_only = 0;
@@ -1587,9 +1656,9 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
 
 				mask = snd_usb_combine_bytes(bmaControls +
 							     csize * (j+1), csize);
-				if (uac2_control_is_readable(mask, i)) {
+				if (uac_v2v3_control_is_readable(mask, i)) {
 					ch_bits |= (1 << j);
-					if (!uac2_control_is_writeable(mask, i))
+					if (!uac_v2v3_control_is_writeable(mask, i))
 						ch_read_only |= (1 << j);
 				}
 			}
@@ -1610,9 +1679,9 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
 			if (ch_bits & 1)
 				build_feature_ctl(state, _ftr, ch_bits, i,
 						  &iterm, unitid, ch_read_only);
-			if (uac2_control_is_readable(master_bits, i))
+			if (uac_v2v3_control_is_readable(master_bits, i))
 				build_feature_ctl(state, _ftr, 0, i, &iterm, unitid,
-						  !uac2_control_is_writeable(master_bits, i));
+						  !uac_v2v3_control_is_writeable(master_bits, i));
 		}
 	}
 
@@ -2220,6 +2289,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
 static int parse_audio_unit(struct mixer_build *state, int unitid)
 {
 	unsigned char *p1;
+	int protocol = state->mixer->protocol;
 
 	if (test_and_set_bit(unitid, state->unitbitmap))
 		return 0; /* the unit already visited */
@@ -2230,36 +2300,61 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
 		return -EINVAL;
 	}
 
-	switch (p1[2]) {
-	case UAC_INPUT_TERMINAL:
-		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);
-	case UAC_FEATURE_UNIT:
-		return parse_audio_feature_unit(state, unitid, p1);
-	case UAC1_PROCESSING_UNIT:
-	/*   UAC2_EFFECT_UNIT has the same value */
-		if (state->mixer->protocol == UAC_VERSION_1)
-			return parse_audio_processing_unit(state, unitid, p1);
-		else
-			return 0; /* FIXME - effect units not implemented yet */
-	case UAC1_EXTENSION_UNIT:
-	/*   UAC2_PROCESSING_UNIT_V2 has the same value */
-		if (state->mixer->protocol == UAC_VERSION_1)
+	if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) {
+		switch (p1[2]) {
+		case UAC_INPUT_TERMINAL:
+			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);
+		case UAC_FEATURE_UNIT:
+			return parse_audio_feature_unit(state, unitid, p1);
+		case UAC1_PROCESSING_UNIT:
+		/*   UAC2_EFFECT_UNIT has the same value */
+			if (protocol == UAC_VERSION_1)
+				return parse_audio_processing_unit(state, unitid, p1);
+			else
+				return 0; /* FIXME - effect units not implemented yet */
+		case UAC1_EXTENSION_UNIT:
+		/*   UAC2_PROCESSING_UNIT_V2 has the same value */
+			if (protocol == UAC_VERSION_1)
+				return parse_audio_extension_unit(state, unitid, p1);
+			else /* UAC_VERSION_2 */
+				return parse_audio_processing_unit(state, unitid, p1);
+		case UAC2_EXTENSION_UNIT_V2:
 			return parse_audio_extension_unit(state, unitid, p1);
-		else /* UAC_VERSION_2 */
+		default:
+			usb_audio_err(state->chip,
+				"unit %u: unexpected type 0x%02x\n", unitid, p1[2]);
+			return -EINVAL;
+		}
+	} else { /* UAC_VERSION_3 */
+		switch (p1[2]) {
+		case UAC_INPUT_TERMINAL:
+			return 0; /* NOP */
+		case UAC3_MIXER_UNIT:
+			return parse_audio_mixer_unit(state, unitid, p1);
+		case UAC3_CLOCK_SOURCE:
+			return parse_clock_source_unit(state, unitid, p1);
+		case UAC3_CLOCK_SELECTOR:
+			return parse_audio_selector_unit(state, unitid, p1);
+		case UAC3_FEATURE_UNIT:
+			return parse_audio_feature_unit(state, unitid, p1);
+		case UAC3_EFFECT_UNIT:
+			return 0; /* FIXME - effect units not implemented yet */
+		case UAC3_PROCESSING_UNIT:
 			return parse_audio_processing_unit(state, unitid, p1);
-	case UAC2_EXTENSION_UNIT_V2:
-		return parse_audio_extension_unit(state, unitid, p1);
-	default:
-		usb_audio_err(state->chip,
-			"unit %u: unexpected type 0x%02x\n", unitid, p1[2]);
-		return -EINVAL;
+		case UAC3_EXTENSION_UNIT:
+			return parse_audio_extension_unit(state, unitid, p1);
+		default:
+			usb_audio_err(state->chip,
+				"unit %u: unexpected type 0x%02x\n", unitid, p1[2]);
+			return -EINVAL;
+		}
 	}
 }
 
@@ -2330,7 +2425,7 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
 			err = parse_audio_unit(&state, desc->bSourceID);
 			if (err < 0 && err != -EINVAL)
 				return err;
-		} else { /* UAC_VERSION_2 */
+		} else if (mixer->protocol == UAC_VERSION_2) {
 			struct uac2_output_terminal_descriptor *desc = p;
 
 			if (desc->bLength < sizeof(*desc))
@@ -2351,6 +2446,27 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
 			err = parse_audio_unit(&state, desc->bCSourceID);
 			if (err < 0 && err != -EINVAL)
 				return err;
+		} else {  /* UAC_VERSION_3 */
+			struct uac3_output_terminal_descriptor *desc = p;
+
+			if (desc->bLength < sizeof(*desc))
+				continue; /* invalid descriptor? */
+			/* mark terminal ID as visited */
+			set_bit(desc->bTerminalID, state.unitbitmap);
+			state.oterm.id = desc->bTerminalID;
+			state.oterm.type = le16_to_cpu(desc->wTerminalType);
+			state.oterm.name = le16_to_cpu(desc->wTerminalDescrStr);
+			err = parse_audio_unit(&state, desc->bSourceID);
+			if (err < 0 && err != -EINVAL)
+				return err;
+
+			/*
+			 * For UAC3, use the same approach to also add the
+			 * clock selectors
+			 */
+			err = parse_audio_unit(&state, desc->bCSourceID);
+			if (err < 0 && err != -EINVAL)
+				return err;
 		}
 	}
 
@@ -2597,6 +2713,9 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
 	case UAC_VERSION_2:
 		mixer->protocol = UAC_VERSION_2;
 		break;
+	case UAC_VERSION_3:
+		mixer->protocol = UAC_VERSION_3;
+		break;
 	}
 
 	if ((err = snd_usb_mixer_controls(mixer)) < 0 ||
diff --git a/sound/usb/stream.c b/sound/usb/stream.c
index dbbe854745ab..6a8f5843334e 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.c
@@ -20,6 +20,7 @@
 #include <linux/usb.h>
 #include <linux/usb/audio.h>
 #include <linux/usb/audio-v2.h>
+#include <linux/usb/audio-v3.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -311,6 +312,153 @@ static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits,
 	return chmap;
 }
 
+/* UAC3 device stores channels information in Cluster Descriptors */
+static struct
+snd_pcm_chmap_elem *convert_chmap_v3(struct uac3_cluster_header_descriptor
+								*cluster)
+{
+	unsigned int channels = cluster->bNrChannels;
+	struct snd_pcm_chmap_elem *chmap;
+	void *p = cluster;
+	int len, c;
+
+	if (channels > ARRAY_SIZE(chmap->map))
+		return NULL;
+
+	chmap = kzalloc(sizeof(*chmap), GFP_KERNEL);
+	if (!chmap)
+		return NULL;
+
+	len = le16_to_cpu(cluster->wLength);
+	c = 0;
+	p += sizeof(struct uac3_cluster_header_descriptor);
+
+	while (((p - (void *)cluster) < len) && (c < channels)) {
+		struct uac3_cluster_segment_descriptor *cs_desc = p;
+		u16 cs_len;
+		u8 cs_type;
+
+		cs_len = le16_to_cpu(cs_desc->wLength);
+		cs_type = cs_desc->bSegmentType;
+
+		if (cs_type == UAC3_CHANNEL_INFORMATION) {
+			struct uac3_cluster_information_segment_descriptor *is = p;
+			unsigned char map;
+
+			/*
+			 * TODO: this conversion is not complete, update it
+			 * after adding UAC3 values to asound.h
+			 */
+			switch (is->bChPurpose) {
+			case UAC3_CH_MONO:
+				map = SNDRV_CHMAP_MONO;
+				break;
+			case UAC3_CH_LEFT:
+			case UAC3_CH_FRONT_LEFT:
+			case UAC3_CH_HEADPHONE_LEFT:
+				map = SNDRV_CHMAP_FL;
+				break;
+			case UAC3_CH_RIGHT:
+			case UAC3_CH_FRONT_RIGHT:
+			case UAC3_CH_HEADPHONE_RIGHT:
+				map = SNDRV_CHMAP_FR;
+				break;
+			case UAC3_CH_FRONT_CENTER:
+				map = SNDRV_CHMAP_FC;
+				break;
+			case UAC3_CH_FRONT_LEFT_OF_CENTER:
+				map = SNDRV_CHMAP_FLC;
+				break;
+			case UAC3_CH_FRONT_RIGHT_OF_CENTER:
+				map = SNDRV_CHMAP_FRC;
+				break;
+			case UAC3_CH_SIDE_LEFT:
+				map = SNDRV_CHMAP_SL;
+				break;
+			case UAC3_CH_SIDE_RIGHT:
+				map = SNDRV_CHMAP_SR;
+				break;
+			case UAC3_CH_BACK_LEFT:
+				map = SNDRV_CHMAP_RL;
+				break;
+			case UAC3_CH_BACK_RIGHT:
+				map = SNDRV_CHMAP_RR;
+				break;
+			case UAC3_CH_BACK_CENTER:
+				map = SNDRV_CHMAP_RC;
+				break;
+			case UAC3_CH_BACK_LEFT_OF_CENTER:
+				map = SNDRV_CHMAP_RLC;
+				break;
+			case UAC3_CH_BACK_RIGHT_OF_CENTER:
+				map = SNDRV_CHMAP_RRC;
+				break;
+			case UAC3_CH_TOP_CENTER:
+				map = SNDRV_CHMAP_TC;
+				break;
+			case UAC3_CH_TOP_FRONT_LEFT:
+				map = SNDRV_CHMAP_TFL;
+				break;
+			case UAC3_CH_TOP_FRONT_RIGHT:
+				map = SNDRV_CHMAP_TFR;
+				break;
+			case UAC3_CH_TOP_FRONT_CENTER:
+				map = SNDRV_CHMAP_TFC;
+				break;
+			case UAC3_CH_TOP_FRONT_LOC:
+				map = SNDRV_CHMAP_TFLC;
+				break;
+			case UAC3_CH_TOP_FRONT_ROC:
+				map = SNDRV_CHMAP_TFRC;
+				break;
+			case UAC3_CH_TOP_SIDE_LEFT:
+				map = SNDRV_CHMAP_TSL;
+				break;
+			case UAC3_CH_TOP_SIDE_RIGHT:
+				map = SNDRV_CHMAP_TSR;
+				break;
+			case UAC3_CH_TOP_BACK_LEFT:
+				map = SNDRV_CHMAP_TRL;
+				break;
+			case UAC3_CH_TOP_BACK_RIGHT:
+				map = SNDRV_CHMAP_TRR;
+				break;
+			case UAC3_CH_TOP_BACK_CENTER:
+				map = SNDRV_CHMAP_TRC;
+				break;
+			case UAC3_CH_BOTTOM_CENTER:
+				map = SNDRV_CHMAP_BC;
+				break;
+			case UAC3_CH_LOW_FREQUENCY_EFFECTS:
+				map = SNDRV_CHMAP_LFE;
+				break;
+			case UAC3_CH_LFE_LEFT:
+				map = SNDRV_CHMAP_LLFE;
+				break;
+			case UAC3_CH_LFE_RIGHT:
+				map = SNDRV_CHMAP_RLFE;
+				break;
+			case UAC3_CH_RELATIONSHIP_UNDEFINED:
+			default:
+				map = SNDRV_CHMAP_UNKNOWN;
+				break;
+			}
+			chmap->map[c++] = map;
+		}
+		p += cs_len;
+	}
+
+	if (channels < c)
+		pr_err("%s: channel number mismatch\n", __func__);
+
+	chmap->channels = channels;
+
+	for (; c < channels; c++)
+		chmap->map[c] = SNDRV_CHMAP_UNKNOWN;
+
+	return chmap;
+}
+
 /*
  * add this endpoint to the chip instance.
  * if a stream with the same endpoint already exists, append to it.
@@ -461,10 +609,11 @@ snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface,
 	return NULL;
 }
 
-static struct uac2_output_terminal_descriptor *
-	snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface,
-						int terminal_id)
+static void *
+snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface,
+					int terminal_id)
 {
+	/* OK to use with both UAC2 and UAC3 */
 	struct uac2_output_terminal_descriptor *term = NULL;
 
 	while ((term = snd_usb_find_csint_desc(ctrl_iface->extra,
@@ -484,10 +633,12 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
 	struct usb_host_interface *alts;
 	struct usb_interface_descriptor *altsd;
 	int i, altno, err, stream;
-	unsigned int format = 0, num_channels = 0;
+	u64 format = 0;
+	unsigned int num_channels = 0;
 	struct audioformat *fp = NULL;
 	int num, protocol, clock = 0;
-	struct uac_format_type_i_continuous_descriptor *fmt;
+	struct uac_format_type_i_continuous_descriptor *fmt = NULL;
+	struct snd_pcm_chmap_elem *chmap_v3 = NULL;
 	unsigned int chconfig;
 
 	dev = chip->dev;
@@ -624,38 +775,158 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
 				iface_no, altno, as->bTerminalLink);
 			continue;
 		}
-		}
 
-		/* get format type */
-		fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_FORMAT_TYPE);
-		if (!fmt) {
+		case UAC_VERSION_3: {
+			struct uac3_input_terminal_descriptor *input_term;
+			struct uac3_output_terminal_descriptor *output_term;
+			struct uac3_as_header_descriptor *as;
+			struct uac3_cluster_header_descriptor *cluster;
+			struct uac3_hc_descriptor_header hc_header;
+			u16 cluster_id, wLength;
+
+			as = snd_usb_find_csint_desc(alts->extra,
+							alts->extralen,
+							NULL, UAC_AS_GENERAL);
+
+			if (!as) {
+				dev_err(&dev->dev,
+					"%u:%d : UAC_AS_GENERAL descriptor not found\n",
+					iface_no, altno);
+				continue;
+			}
+
+			if (as->bLength < sizeof(*as)) {
+				dev_err(&dev->dev,
+					"%u:%d : invalid UAC_AS_GENERAL desc\n",
+					iface_no, altno);
+				continue;
+			}
+
+			cluster_id = le16_to_cpu(as->wClusterDescrID);
+			if (!cluster_id) {
+				dev_err(&dev->dev,
+					"%u:%d : no cluster descriptor\n",
+					iface_no, altno);
+				continue;
+			}
+
+			/*
+			 * Get number of channels and channel map through
+			 * High Capability Cluster Descriptor
+			 *
+			 * First step: get High Capability header and
+			 * read size of Cluster Descriptor
+			 */
+			err = snd_usb_ctl_msg(chip->dev,
+					usb_rcvctrlpipe(chip->dev, 0),
+					UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR,
+					USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+					cluster_id,
+					snd_usb_ctrl_intf(chip),
+					&hc_header, sizeof(hc_header));
+			if (err < 0)
+				return err;
+			else if (err != sizeof(hc_header)) {
+				dev_err(&dev->dev,
+					"%u:%d : can't get High Capability descriptor\n",
+					iface_no, altno);
+				return -EIO;
+			}
+
+			/*
+			 * Second step: allocate needed amount of memory
+			 * and request Cluster Descriptor
+			 */
+			wLength = le16_to_cpu(hc_header.wLength);
+			cluster = kzalloc(wLength, GFP_KERNEL);
+			if (!cluster)
+				return -ENOMEM;
+			err = snd_usb_ctl_msg(chip->dev,
+					usb_rcvctrlpipe(chip->dev, 0),
+					UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR,
+					USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+					cluster_id,
+					snd_usb_ctrl_intf(chip),
+					cluster, wLength);
+			if (err < 0) {
+				kfree(cluster);
+				return err;
+			} else if (err != wLength) {
+				dev_err(&dev->dev,
+					"%u:%d : can't get Cluster Descriptor\n",
+					iface_no, altno);
+				kfree(cluster);
+				return -EIO;
+			}
+
+			num_channels = cluster->bNrChannels;
+			chmap_v3 = convert_chmap_v3(cluster);
+
+			kfree(cluster);
+
+			format = le64_to_cpu(as->bmFormats);
+
+			/* lookup the terminal associated to this interface
+			 * to extract the clock */
+			input_term = snd_usb_find_input_terminal_descriptor(
+							chip->ctrl_intf,
+							as->bTerminalLink);
+
+			if (input_term) {
+				clock = input_term->bCSourceID;
+				break;
+			}
+
+			output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf,
+									      as->bTerminalLink);
+			if (output_term) {
+				clock = output_term->bCSourceID;
+				break;
+			}
+
 			dev_err(&dev->dev,
-				"%u:%d : no UAC_FORMAT_TYPE desc\n",
-				iface_no, altno);
+				"%u:%d : bogus bTerminalLink %d\n",
+				iface_no, altno, as->bTerminalLink);
 			continue;
 		}
-		if (((protocol == UAC_VERSION_1) && (fmt->bLength < 8)) ||
-		    ((protocol == UAC_VERSION_2) && (fmt->bLength < 6))) {
-			dev_err(&dev->dev,
-				"%u:%d : invalid UAC_FORMAT_TYPE desc\n",
-				iface_no, altno);
-			continue;
 		}
 
-		/*
-		 * Blue Microphones workaround: The last altsetting is identical
-		 * with the previous one, except for a larger packet size, but
-		 * is actually a mislabeled two-channel setting; ignore it.
-		 */
-		if (fmt->bNrChannels == 1 &&
-		    fmt->bSubframeSize == 2 &&
-		    altno == 2 && num == 3 &&
-		    fp && fp->altsetting == 1 && fp->channels == 1 &&
-		    fp->formats == SNDRV_PCM_FMTBIT_S16_LE &&
-		    protocol == UAC_VERSION_1 &&
-		    le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) ==
+		if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) {
+			/* get format type */
+			fmt = snd_usb_find_csint_desc(alts->extra,
+						      alts->extralen,
+						      NULL, UAC_FORMAT_TYPE);
+			if (!fmt) {
+				dev_err(&dev->dev,
+					"%u:%d : no UAC_FORMAT_TYPE desc\n",
+					iface_no, altno);
+				continue;
+			}
+			if (((protocol == UAC_VERSION_1) && (fmt->bLength < 8))
+					|| ((protocol == UAC_VERSION_2) &&
+							(fmt->bLength < 6))) {
+				dev_err(&dev->dev,
+					"%u:%d : invalid UAC_FORMAT_TYPE desc\n",
+					iface_no, altno);
+				continue;
+			}
+
+			/*
+			 * Blue Microphones workaround: The last altsetting is
+			 * identical with the previous one, except for a larger
+			 * packet size, but is actually a mislabeled two-channel
+			 * setting; ignore it.
+			 */
+			if (fmt->bNrChannels == 1 &&
+			    fmt->bSubframeSize == 2 &&
+			    altno == 2 && num == 3 &&
+			    fp && fp->altsetting == 1 && fp->channels == 1 &&
+			    fp->formats == SNDRV_PCM_FMTBIT_S16_LE &&
+			    protocol == UAC_VERSION_1 &&
+			    le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) ==
 							fp->maxpacksize * 2)
-			continue;
+				continue;
+		}
 
 		fp = kzalloc(sizeof(*fp), GFP_KERNEL);
 		if (!fp)
@@ -681,17 +952,39 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
 		snd_usb_audioformat_attributes_quirk(chip, fp, stream);
 
 		/* ok, let's parse further... */
-		if (snd_usb_parse_audio_format(chip, fp, format, fmt, stream) < 0) {
-			kfree(fp->rate_table);
-			kfree(fp);
-			fp = NULL;
-			continue;
+		if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) {
+			if (snd_usb_parse_audio_format(chip, fp, format,
+							fmt, stream) < 0) {
+				kfree(fp->rate_table);
+				kfree(fp);
+				fp = NULL;
+				continue;
+			}
+		} else {
+			struct uac3_as_header_descriptor *as;
+
+			as = snd_usb_find_csint_desc(alts->extra,
+						     alts->extralen,
+						     NULL, UAC_AS_GENERAL);
+
+			if (snd_usb_parse_audio_format_v3(chip, fp, as,
+								stream) < 0) {
+				kfree(fp->rate_table);
+				kfree(fp);
+				fp = NULL;
+				continue;
+			}
 		}
 
 		/* Create chmap */
 		if (fp->channels != num_channels)
 			chconfig = 0;
-		fp->chmap = convert_chmap(fp->channels, chconfig, protocol);
+
+		if (protocol == UAC_VERSION_3)
+			fp->chmap = chmap_v3;
+		else
+			fp->chmap = convert_chmap(fp->channels, chconfig,
+						  protocol);
 
 		dev_dbg(&dev->dev, "%u:%d: add audio endpoint %#x\n", iface_no, altno, fp->endpoint);
 		err = snd_usb_add_audio_stream(chip, stream, fp);