diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 54702888855d..c30fbd062793 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -35,8 +35,8 @@
  */
 #define USB_OUT_CLK_ID	(out_clk_src_desc.bClockID)
 #define USB_IN_CLK_ID	(in_clk_src_desc.bClockID)
-#define USB_OUT_FU_ID	(out_feature_unit_desc->bUnitID)
-#define USB_IN_FU_ID	(in_feature_unit_desc->bUnitID)
+#define USB_OUT_FU_ID(_opts)	(_opts->c_alt_1_opts.fu_id)
+#define USB_IN_FU_ID(_opts)	(_opts->p_alt_1_opts.fu_id)
 
 #define CONTROL_ABSENT	0
 #define CONTROL_RDONLY	1
@@ -54,15 +54,36 @@
 #define UNFLW_CTRL	8
 #define OVFLW_CTRL	10
 
-#define EPIN_EN(_opts) ((_opts)->p_chmask != 0)
-#define EPOUT_EN(_opts) ((_opts)->c_chmask != 0)
-#define FUIN_EN(_opts) (EPIN_EN(_opts) \
+
+#define EP_EN(_alt_opts) ((_alt_opts) && ((_alt_opts)->chmask != 0))
+#define FUIN_EN(_opts) (EP_EN(&_opts->p_alt_1_opts) \
 				&& ((_opts)->p_mute_present \
 				|| (_opts)->p_volume_present))
-#define FUOUT_EN(_opts) (EPOUT_EN(_opts) \
+#define FUOUT_EN(_opts) (EP_EN(&_opts->c_alt_1_opts) \
 				&& ((_opts)->c_mute_present \
 				|| (_opts)->c_volume_present))
-#define EPOUT_FBACK_IN_EN(_opts) ((_opts)->c_sync == USB_ENDPOINT_SYNC_ASYNC)
+#define EPOUT_FBACK_IN_EN(_alt_opts) ((_alt_opts)->sync == USB_ENDPOINT_SYNC_ASYNC)
+
+/* Check if any alt mode has option enabled */
+#define EN_ANY(single, fn, cp)						\
+static int fn(struct f_uac2_opts *opts)					\
+{									\
+	struct f_uac2_alt_opts *alt_opts;				\
+									\
+	if (single(&opts->cp##_alt_1_opts))				\
+		return 1;						\
+									\
+	list_for_each_entry(alt_opts, &opts->cp##_alt_opts, list) {	\
+		if (single(alt_opts))					\
+			return 1;					\
+	}								\
+									\
+	return 0;							\
+}
+
+EN_ANY(EP_EN, epout_en_any, c)
+EN_ANY(EP_EN, epin_en_any, p)
+EN_ANY(EPOUT_FBACK_IN_EN, epout_fback_in_en_any, p)
 
 struct f_uac2 {
 	struct g_audio g_audio;
@@ -94,7 +115,7 @@ static int afunc_notify(struct g_audio *agdev, int unit_id, int cs);
 /* --------- USB Function Interface ------------- */
 
 static struct usb_interface_assoc_descriptor iad_desc = {
-	.bLength = sizeof iad_desc,
+	.bLength = sizeof(iad_desc),
 	.bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
 
 	.bFirstInterface = 0,
@@ -140,65 +161,6 @@ static struct uac_clock_source_descriptor out_clk_src_desc = {
 	.bAssocTerminal = 0,
 };
 
-/* Input Terminal for USB_OUT */
-static struct uac2_input_terminal_descriptor usb_out_it_desc = {
-	.bLength = sizeof usb_out_it_desc,
-	.bDescriptorType = USB_DT_CS_INTERFACE,
-
-	.bDescriptorSubtype = UAC_INPUT_TERMINAL,
-	/* .bTerminalID = DYNAMIC */
-	.wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING),
-	.bAssocTerminal = 0,
-	/* .bCSourceID = DYNAMIC */
-	.iChannelNames = 0,
-	.bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL),
-};
-
-/* Input Terminal for I/O-In */
-static struct uac2_input_terminal_descriptor io_in_it_desc = {
-	.bLength = sizeof io_in_it_desc,
-	.bDescriptorType = USB_DT_CS_INTERFACE,
-
-	.bDescriptorSubtype = UAC_INPUT_TERMINAL,
-	/* .bTerminalID = DYNAMIC */
-	/* .wTerminalType = DYNAMIC */
-	.bAssocTerminal = 0,
-	/* .bCSourceID = DYNAMIC */
-	.iChannelNames = 0,
-	.bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL),
-};
-
-/* Ouput Terminal for USB_IN */
-static struct uac2_output_terminal_descriptor usb_in_ot_desc = {
-	.bLength = sizeof usb_in_ot_desc,
-	.bDescriptorType = USB_DT_CS_INTERFACE,
-
-	.bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
-	/* .bTerminalID = DYNAMIC */
-	.wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING),
-	.bAssocTerminal = 0,
-	/* .bSourceID = DYNAMIC */
-	/* .bCSourceID = DYNAMIC */
-	.bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL),
-};
-
-/* Ouput Terminal for I/O-Out */
-static struct uac2_output_terminal_descriptor io_out_ot_desc = {
-	.bLength = sizeof io_out_ot_desc,
-	.bDescriptorType = USB_DT_CS_INTERFACE,
-
-	.bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
-	/* .bTerminalID = DYNAMIC */
-	/* .wTerminalType = DYNAMIC */
-	.bAssocTerminal = 0,
-	/* .bSourceID = DYNAMIC */
-	/* .bCSourceID = DYNAMIC */
-	.bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL),
-};
-
-static struct uac2_feature_unit_descriptor *in_feature_unit_desc;
-static struct uac2_feature_unit_descriptor *out_feature_unit_desc;
-
 static struct uac2_ac_header_descriptor ac_hdr_desc = {
 	.bLength = sizeof ac_hdr_desc,
 	.bDescriptorType = USB_DT_CS_INTERFACE,
@@ -246,89 +208,6 @@ static struct usb_ss_ep_comp_descriptor ss_ep_int_desc_comp = {
 	.wBytesPerInterval = cpu_to_le16(6),
 };
 
-/* Audio Streaming OUT Interface - Alt0 */
-static struct usb_interface_descriptor std_as_out_if0_desc = {
-	.bLength = sizeof std_as_out_if0_desc,
-	.bDescriptorType = USB_DT_INTERFACE,
-
-	.bAlternateSetting = 0,
-	.bNumEndpoints = 0,
-	.bInterfaceClass = USB_CLASS_AUDIO,
-	.bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
-	.bInterfaceProtocol = UAC_VERSION_2,
-};
-
-/* Audio Streaming OUT Interface - Alt1 */
-static struct usb_interface_descriptor std_as_out_if1_desc = {
-	.bLength = sizeof std_as_out_if1_desc,
-	.bDescriptorType = USB_DT_INTERFACE,
-
-	.bAlternateSetting = 1,
-	.bNumEndpoints = 1,
-	.bInterfaceClass = USB_CLASS_AUDIO,
-	.bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
-	.bInterfaceProtocol = UAC_VERSION_2,
-};
-
-/* Audio Stream OUT Intface Desc */
-static struct uac2_as_header_descriptor as_out_hdr_desc = {
-	.bLength = sizeof as_out_hdr_desc,
-	.bDescriptorType = USB_DT_CS_INTERFACE,
-
-	.bDescriptorSubtype = UAC_AS_GENERAL,
-	/* .bTerminalLink = DYNAMIC */
-	.bmControls = 0,
-	.bFormatType = UAC_FORMAT_TYPE_I,
-	.bmFormats = cpu_to_le32(UAC_FORMAT_TYPE_I_PCM),
-	.iChannelNames = 0,
-};
-
-/* Audio USB_OUT Format */
-static struct uac2_format_type_i_descriptor as_out_fmt1_desc = {
-	.bLength = sizeof as_out_fmt1_desc,
-	.bDescriptorType = USB_DT_CS_INTERFACE,
-	.bDescriptorSubtype = UAC_FORMAT_TYPE,
-	.bFormatType = UAC_FORMAT_TYPE_I,
-};
-
-/* STD AS ISO OUT Endpoint */
-static struct usb_endpoint_descriptor fs_epout_desc = {
-	.bLength = USB_DT_ENDPOINT_SIZE,
-	.bDescriptorType = USB_DT_ENDPOINT,
-
-	.bEndpointAddress = USB_DIR_OUT,
-	/* .bmAttributes = DYNAMIC */
-	/* .wMaxPacketSize = DYNAMIC */
-	.bInterval = 1,
-};
-
-static struct usb_endpoint_descriptor hs_epout_desc = {
-	.bLength = USB_DT_ENDPOINT_SIZE,
-	.bDescriptorType = USB_DT_ENDPOINT,
-
-	/* .bmAttributes = DYNAMIC */
-	/* .wMaxPacketSize = DYNAMIC */
-	/* .bInterval = DYNAMIC */
-};
-
-static struct usb_endpoint_descriptor ss_epout_desc = {
-	.bLength = USB_DT_ENDPOINT_SIZE,
-	.bDescriptorType = USB_DT_ENDPOINT,
-
-	.bEndpointAddress = USB_DIR_OUT,
-	/* .bmAttributes = DYNAMIC */
-	/* .wMaxPacketSize = DYNAMIC */
-	/* .bInterval = DYNAMIC */
-};
-
-static struct usb_ss_ep_comp_descriptor ss_epout_desc_comp = {
-	.bLength		= sizeof(ss_epout_desc_comp),
-	.bDescriptorType	= USB_DT_SS_ENDPOINT_COMP,
-	.bMaxBurst		= 0,
-	.bmAttributes		= 0,
-	/* wBytesPerInterval = DYNAMIC */
-};
-
 /* CS AS ISO OUT Endpoint */
 static struct uac2_iso_endpoint_descriptor as_iso_out_desc = {
 	.bLength = sizeof as_iso_out_desc,
@@ -379,90 +258,6 @@ static struct usb_ss_ep_comp_descriptor ss_epin_fback_desc_comp = {
 	.wBytesPerInterval	= cpu_to_le16(4),
 };
 
-
-/* Audio Streaming IN Interface - Alt0 */
-static struct usb_interface_descriptor std_as_in_if0_desc = {
-	.bLength = sizeof std_as_in_if0_desc,
-	.bDescriptorType = USB_DT_INTERFACE,
-
-	.bAlternateSetting = 0,
-	.bNumEndpoints = 0,
-	.bInterfaceClass = USB_CLASS_AUDIO,
-	.bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
-	.bInterfaceProtocol = UAC_VERSION_2,
-};
-
-/* Audio Streaming IN Interface - Alt1 */
-static struct usb_interface_descriptor std_as_in_if1_desc = {
-	.bLength = sizeof std_as_in_if1_desc,
-	.bDescriptorType = USB_DT_INTERFACE,
-
-	.bAlternateSetting = 1,
-	.bNumEndpoints = 1,
-	.bInterfaceClass = USB_CLASS_AUDIO,
-	.bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
-	.bInterfaceProtocol = UAC_VERSION_2,
-};
-
-/* Audio Stream IN Intface Desc */
-static struct uac2_as_header_descriptor as_in_hdr_desc = {
-	.bLength = sizeof as_in_hdr_desc,
-	.bDescriptorType = USB_DT_CS_INTERFACE,
-
-	.bDescriptorSubtype = UAC_AS_GENERAL,
-	/* .bTerminalLink = DYNAMIC */
-	.bmControls = 0,
-	.bFormatType = UAC_FORMAT_TYPE_I,
-	.bmFormats = cpu_to_le32(UAC_FORMAT_TYPE_I_PCM),
-	.iChannelNames = 0,
-};
-
-/* Audio USB_IN Format */
-static struct uac2_format_type_i_descriptor as_in_fmt1_desc = {
-	.bLength = sizeof as_in_fmt1_desc,
-	.bDescriptorType = USB_DT_CS_INTERFACE,
-	.bDescriptorSubtype = UAC_FORMAT_TYPE,
-	.bFormatType = UAC_FORMAT_TYPE_I,
-};
-
-/* STD AS ISO IN Endpoint */
-static struct usb_endpoint_descriptor fs_epin_desc = {
-	.bLength = USB_DT_ENDPOINT_SIZE,
-	.bDescriptorType = USB_DT_ENDPOINT,
-
-	.bEndpointAddress = USB_DIR_IN,
-	.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
-	/* .wMaxPacketSize = DYNAMIC */
-	.bInterval = 1,
-};
-
-static struct usb_endpoint_descriptor hs_epin_desc = {
-	.bLength = USB_DT_ENDPOINT_SIZE,
-	.bDescriptorType = USB_DT_ENDPOINT,
-
-	.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
-	/* .wMaxPacketSize = DYNAMIC */
-	/* .bInterval = DYNAMIC */
-};
-
-static struct usb_endpoint_descriptor ss_epin_desc = {
-	.bLength = USB_DT_ENDPOINT_SIZE,
-	.bDescriptorType = USB_DT_ENDPOINT,
-
-	.bEndpointAddress = USB_DIR_IN,
-	.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
-	/* .wMaxPacketSize = DYNAMIC */
-	/* .bInterval = DYNAMIC */
-};
-
-static struct usb_ss_ep_comp_descriptor ss_epin_desc_comp = {
-	.bLength		= sizeof(ss_epin_desc_comp),
-	.bDescriptorType	= USB_DT_SS_ENDPOINT_COMP,
-	.bMaxBurst		= 0,
-	.bmAttributes		= 0,
-	/* wBytesPerInterval = DYNAMIC */
-};
-
 /* CS AS ISO IN Endpoint */
 static struct uac2_iso_endpoint_descriptor as_iso_in_desc = {
 	.bLength = sizeof as_iso_in_desc,
@@ -475,115 +270,6 @@ static struct uac2_iso_endpoint_descriptor as_iso_in_desc = {
 	.wLockDelay = 0,
 };
 
-static struct usb_descriptor_header *fs_audio_desc[] = {
-	(struct usb_descriptor_header *)&iad_desc,
-	(struct usb_descriptor_header *)&std_ac_if_desc,
-
-	(struct usb_descriptor_header *)&ac_hdr_desc,
-	(struct usb_descriptor_header *)&in_clk_src_desc,
-	(struct usb_descriptor_header *)&out_clk_src_desc,
-	(struct usb_descriptor_header *)&usb_out_it_desc,
-	(struct usb_descriptor_header *)&out_feature_unit_desc,
-	(struct usb_descriptor_header *)&io_in_it_desc,
-	(struct usb_descriptor_header *)&usb_in_ot_desc,
-	(struct usb_descriptor_header *)&in_feature_unit_desc,
-	(struct usb_descriptor_header *)&io_out_ot_desc,
-
-	(struct usb_descriptor_header *)&fs_ep_int_desc,
-
-	(struct usb_descriptor_header *)&std_as_out_if0_desc,
-	(struct usb_descriptor_header *)&std_as_out_if1_desc,
-
-	(struct usb_descriptor_header *)&as_out_hdr_desc,
-	(struct usb_descriptor_header *)&as_out_fmt1_desc,
-	(struct usb_descriptor_header *)&fs_epout_desc,
-	(struct usb_descriptor_header *)&as_iso_out_desc,
-	(struct usb_descriptor_header *)&fs_epin_fback_desc,
-
-	(struct usb_descriptor_header *)&std_as_in_if0_desc,
-	(struct usb_descriptor_header *)&std_as_in_if1_desc,
-
-	(struct usb_descriptor_header *)&as_in_hdr_desc,
-	(struct usb_descriptor_header *)&as_in_fmt1_desc,
-	(struct usb_descriptor_header *)&fs_epin_desc,
-	(struct usb_descriptor_header *)&as_iso_in_desc,
-	NULL,
-};
-
-static struct usb_descriptor_header *hs_audio_desc[] = {
-	(struct usb_descriptor_header *)&iad_desc,
-	(struct usb_descriptor_header *)&std_ac_if_desc,
-
-	(struct usb_descriptor_header *)&ac_hdr_desc,
-	(struct usb_descriptor_header *)&in_clk_src_desc,
-	(struct usb_descriptor_header *)&out_clk_src_desc,
-	(struct usb_descriptor_header *)&usb_out_it_desc,
-	(struct usb_descriptor_header *)&out_feature_unit_desc,
-	(struct usb_descriptor_header *)&io_in_it_desc,
-	(struct usb_descriptor_header *)&usb_in_ot_desc,
-	(struct usb_descriptor_header *)&in_feature_unit_desc,
-	(struct usb_descriptor_header *)&io_out_ot_desc,
-
-	(struct usb_descriptor_header *)&hs_ep_int_desc,
-
-	(struct usb_descriptor_header *)&std_as_out_if0_desc,
-	(struct usb_descriptor_header *)&std_as_out_if1_desc,
-
-	(struct usb_descriptor_header *)&as_out_hdr_desc,
-	(struct usb_descriptor_header *)&as_out_fmt1_desc,
-	(struct usb_descriptor_header *)&hs_epout_desc,
-	(struct usb_descriptor_header *)&as_iso_out_desc,
-	(struct usb_descriptor_header *)&hs_epin_fback_desc,
-
-	(struct usb_descriptor_header *)&std_as_in_if0_desc,
-	(struct usb_descriptor_header *)&std_as_in_if1_desc,
-
-	(struct usb_descriptor_header *)&as_in_hdr_desc,
-	(struct usb_descriptor_header *)&as_in_fmt1_desc,
-	(struct usb_descriptor_header *)&hs_epin_desc,
-	(struct usb_descriptor_header *)&as_iso_in_desc,
-	NULL,
-};
-
-static struct usb_descriptor_header *ss_audio_desc[] = {
-	(struct usb_descriptor_header *)&iad_desc,
-	(struct usb_descriptor_header *)&std_ac_if_desc,
-
-	(struct usb_descriptor_header *)&ac_hdr_desc,
-	(struct usb_descriptor_header *)&in_clk_src_desc,
-	(struct usb_descriptor_header *)&out_clk_src_desc,
-	(struct usb_descriptor_header *)&usb_out_it_desc,
-  (struct usb_descriptor_header *)&out_feature_unit_desc,
-	(struct usb_descriptor_header *)&io_in_it_desc,
-	(struct usb_descriptor_header *)&usb_in_ot_desc,
-	(struct usb_descriptor_header *)&in_feature_unit_desc,
-	(struct usb_descriptor_header *)&io_out_ot_desc,
-
-	(struct usb_descriptor_header *)&ss_ep_int_desc,
-	(struct usb_descriptor_header *)&ss_ep_int_desc_comp,
-
-	(struct usb_descriptor_header *)&std_as_out_if0_desc,
-	(struct usb_descriptor_header *)&std_as_out_if1_desc,
-
-	(struct usb_descriptor_header *)&as_out_hdr_desc,
-	(struct usb_descriptor_header *)&as_out_fmt1_desc,
-	(struct usb_descriptor_header *)&ss_epout_desc,
-	(struct usb_descriptor_header *)&ss_epout_desc_comp,
-	(struct usb_descriptor_header *)&as_iso_out_desc,
-	(struct usb_descriptor_header *)&ss_epin_fback_desc,
-	(struct usb_descriptor_header *)&ss_epin_fback_desc_comp,
-
-	(struct usb_descriptor_header *)&std_as_in_if0_desc,
-	(struct usb_descriptor_header *)&std_as_in_if1_desc,
-
-	(struct usb_descriptor_header *)&as_in_hdr_desc,
-	(struct usb_descriptor_header *)&as_in_fmt1_desc,
-	(struct usb_descriptor_header *)&ss_epin_desc,
-	(struct usb_descriptor_header *)&ss_epin_desc_comp,
-	(struct usb_descriptor_header *)&as_iso_in_desc,
-	NULL,
-};
-
 struct cntrl_cur_lay2 {
 	__le16	wCUR;
 };
@@ -720,11 +406,65 @@ static int set_ep_max_packet_size_bint(struct device *dev, const struct f_uac2_a
 		alt_opts->ssize, alt_opts->sync, alt_opts->c.opts->fb_max);
 }
 
-static struct uac2_feature_unit_descriptor *build_fu_desc(int chmask)
+struct path_params {
+	int dir;
+	int id;
+	struct f_uac2_opts *opts;
+	struct usb_string *strings;
+};
+
+/* Audio20 4.7.2.4 Input Terminal Descriptor */
+static void init_it_desc(struct uac2_input_terminal_descriptor *it_desc,
+			 struct f_uac2_alt_opts *alt_opts,
+			 struct path_params *params)
+{
+	it_desc->bLength =		sizeof(*it_desc);
+	it_desc->bDescriptorType =	USB_DT_CS_INTERFACE;
+	it_desc->bDescriptorSubtype =	UAC_INPUT_TERMINAL;
+	it_desc->bTerminalID =		params->id++;
+	it_desc->wTerminalType =	cpu_to_le16((params->dir == HOST_TO_DEVICE) ?
+							UAC_TERMINAL_STREAMING :
+							alt_opts->terminal_type);
+	it_desc->bAssocTerminal =	0;
+	it_desc->bCSourceID =		(params->dir == HOST_TO_DEVICE) ? out_clk_src_desc.bClockID
+									: in_clk_src_desc.bClockID;
+	it_desc->bNrChannels =		num_channels(alt_opts->chmask);
+	it_desc->bmChannelConfig =	cpu_to_le32(alt_opts->chmask);
+	it_desc->iChannelNames =	add_string(params->strings, alt_opts->it_ch_name);
+	it_desc->bmControls =		cpu_to_le16(CONTROL_RDWR << COPY_CTRL);
+	it_desc->iTerminal =		add_string(params->strings, alt_opts->it_name);
+}
+
+/* Audio20 4.7.2.5 Output Terminal Descriptor */
+static void init_ot_desc(struct uac2_output_terminal_descriptor *ot_desc,
+			 struct f_uac2_alt_opts *alt_opts,
+			 struct path_params *params, int src_id)
+{
+	ot_desc->bLength =		sizeof(*ot_desc);
+	ot_desc->bDescriptorType =	USB_DT_CS_INTERFACE;
+	ot_desc->bDescriptorSubtype =	UAC_OUTPUT_TERMINAL;
+	ot_desc->bTerminalID =		params->id++;
+	ot_desc->wTerminalType =	cpu_to_le16((params->dir == HOST_TO_DEVICE) ?
+							alt_opts->terminal_type :
+							UAC_TERMINAL_STREAMING);
+	ot_desc->bAssocTerminal =	0;
+	ot_desc->bSourceID =		src_id;
+	ot_desc->bCSourceID =		(params->dir == HOST_TO_DEVICE) ? out_clk_src_desc.bClockID
+									: in_clk_src_desc.bClockID;
+	ot_desc->bmControls =		cpu_to_le16(CONTROL_RDWR << COPY_CTRL);
+	ot_desc->iTerminal =		add_string(params->strings, alt_opts->ot_name);
+}
+
+/* Audio20 4.7.2.8 Feature Unit Descriptor */
+static struct uac2_feature_unit_descriptor *build_fu_desc(struct f_uac2_alt_opts *alt_opts,
+							  struct path_params *params, int src_id)
 {
 	struct uac2_feature_unit_descriptor *fu_desc;
-	int channels = num_channels(chmask);
+	int channels = num_channels(alt_opts->chmask);
 	int fu_desc_size = UAC2_DT_FEATURE_UNIT_SIZE(channels);
+	__le32 *bma;
+	u32 control = 0;
+	u8 *i_feature;
 
 	fu_desc = kzalloc(fu_desc_size, GFP_KERNEL);
 	if (!fu_desc)
@@ -732,218 +472,520 @@ static struct uac2_feature_unit_descriptor *build_fu_desc(int chmask)
 
 	fu_desc->bLength = fu_desc_size;
 	fu_desc->bDescriptorType = USB_DT_CS_INTERFACE;
-
 	fu_desc->bDescriptorSubtype = UAC_FEATURE_UNIT;
+	fu_desc->bUnitID = params->id++;
+	fu_desc->bSourceID = src_id;
 
 	/* bUnitID, bSourceID and bmaControls will be defined later */
+	if (params->dir == HOST_TO_DEVICE) {
+		if (params->opts->c_mute_present)
+			control |= CONTROL_RDWR << FU_MUTE_CTRL;
+		if (params->opts->c_volume_present)
+			control |= CONTROL_RDWR << FU_VOL_CTRL;
+	}
+
+	if (params->dir == DEVICE_TO_HOST) {
+		if (params->opts->p_mute_present)
+			control |= CONTROL_RDWR << FU_MUTE_CTRL;
+		if (params->opts->p_volume_present)
+			control |= CONTROL_RDWR << FU_VOL_CTRL;
+	}
+
+	/* Only master volume/mute is supported. Per-channel controls are all zero. */
+	bma = (__le32 *)&fu_desc->bmaControls[0];
+	*bma = cpu_to_le32(control);
+
+	/* iFeature is located after all channel controls */
+	i_feature = (u8 *)fu_desc + fu_desc->bLength - 1;
+	*i_feature = add_string(params->strings, alt_opts->fu_vol_name);
 
 	return fu_desc;
 }
 
+/* Audio20 4.9.1  Standard AS Interface Descriptor */
+static void init_as_interface_desc(struct usb_interface_descriptor *iface_desc,
+				   u8 ifnum, u8 alt, u8 endpoints, const char *name,
+				   struct usb_string *strings)
+{
+	iface_desc->bLength =			sizeof(*iface_desc);
+	iface_desc->bDescriptorType =		USB_DT_INTERFACE;
+	iface_desc->bInterfaceNumber =		ifnum;
+	iface_desc->bAlternateSetting =		alt;
+	iface_desc->bNumEndpoints =		endpoints;
+	iface_desc->bInterfaceClass =		USB_CLASS_AUDIO;
+	iface_desc->bInterfaceSubClass =	USB_SUBCLASS_AUDIOSTREAMING;
+	iface_desc->bInterfaceProtocol =	UAC_VERSION_2;
+	iface_desc->iInterface =		add_string(strings, name);
+}
+
+/* Audio20 4.9.2  Class-Specific AS Interface Descriptor */
+static void init_as_header_desc(struct f_uac2_alt_opts *alt_opts, int terminalId)
+{
+	alt_opts->as_header_desc.bLength =		sizeof(alt_opts->as_header_desc);
+	alt_opts->as_header_desc.bDescriptorType =	USB_DT_CS_INTERFACE;
+	alt_opts->as_header_desc.bDescriptorSubtype =	UAC_AS_GENERAL;
+	alt_opts->as_header_desc.bTerminalLink =	terminalId;
+	alt_opts->as_header_desc.bmControls =		0;
+	alt_opts->as_header_desc.bFormatType =		UAC_FORMAT_TYPE_I;
+	alt_opts->as_header_desc.bmFormats =		cpu_to_le32(UAC_FORMAT_TYPE_I_PCM);
+	alt_opts->as_header_desc.bNrChannels =		num_channels(alt_opts->chmask);
+	alt_opts->as_header_desc.bmChannelConfig =	cpu_to_le32(alt_opts->chmask);
+	alt_opts->as_header_desc.iChannelNames =	0;
+}
+
+/* Audio20 4.9.3 Class-Specific AS Format Type Descriptor */
+static void init_uac_format_type_i_discrete_desc(struct f_uac2_alt_opts *alt_opts)
+{
+	alt_opts->fmt_desc.bLength =		sizeof(alt_opts->fmt_desc);
+	alt_opts->fmt_desc.bDescriptorType =	USB_DT_CS_INTERFACE;
+	alt_opts->fmt_desc.bDescriptorSubtype =	UAC_FORMAT_TYPE;
+	alt_opts->fmt_desc.bFormatType =	UAC_FORMAT_TYPE_I;
+	alt_opts->fmt_desc.bSubslotSize =	alt_opts->ssize;
+	alt_opts->fmt_desc.bBitResolution =	alt_opts->ssize * 8;
+}
+
+static int init_isoc_ep_descriptor(struct device *dev, struct usb_endpoint_descriptor *ep_desc,
+				   struct f_uac2_alt_opts *alt_opts, int dir,
+				   enum usb_device_speed speed, u8 addr)
+{
+	ep_desc->bLength =		USB_DT_ENDPOINT_SIZE;
+	ep_desc->bDescriptorType =	USB_DT_ENDPOINT;
+	ep_desc->bEndpointAddress =	addr;
+	ep_desc->bmAttributes =		USB_ENDPOINT_XFER_ISOC |
+					(((dir == HOST_TO_DEVICE) && !EPOUT_FBACK_IN_EN(alt_opts))
+						? USB_ENDPOINT_SYNC_ADAPTIVE
+						: USB_ENDPOINT_SYNC_ASYNC);
+	ep_desc->bInterval =		1; /* For FS. For HS/SS, this is set later from hs_bint. */
+
+	return set_ep_max_packet_size_bint(dev, alt_opts, ep_desc, speed, (dir == DEVICE_TO_HOST));
+}
+
+static void init_isoc_ep_descriptor_comp(struct usb_ss_ep_comp_descriptor *ep_desc_comp,
+					 struct usb_endpoint_descriptor *ep_desc)
+{
+	ep_desc_comp->bLength		= sizeof(*ep_desc_comp),
+	ep_desc_comp->bDescriptorType	= USB_DT_SS_ENDPOINT_COMP,
+	ep_desc_comp->bMaxBurst		= 0,
+	ep_desc_comp->bmAttributes	= 0,
+	ep_desc_comp->wBytesPerInterval = ep_desc->wMaxPacketSize;
+}
+
+static int init_alt_descriptors(struct device *dev, struct f_uac2_alt_opts *alt_opts, int ifnum,
+				u8 epaddr, int endpoints, int terminalID, int dir,
+				struct usb_string *strings)
+{
+	int status = 0;
+
+	init_as_header_desc(alt_opts, terminalID);
+	init_as_interface_desc(&alt_opts->intf_desc, ifnum, alt_opts->c.alt_num,
+			       endpoints, alt_opts->name, strings);
+	init_uac_format_type_i_discrete_desc(alt_opts);
+
+	status = init_isoc_ep_descriptor(dev, &alt_opts->fs_iso_ep_desc, alt_opts,
+					 dir, USB_SPEED_FULL, epaddr);
+	if (!status)
+		status = init_isoc_ep_descriptor(dev, &alt_opts->hs_iso_ep_desc, alt_opts,
+						 dir, USB_SPEED_HIGH, epaddr);
+	if (!status)
+		status = init_isoc_ep_descriptor(dev, &alt_opts->ss_iso_ep_desc, alt_opts,
+						 dir, USB_SPEED_SUPER, epaddr);
+
+	init_isoc_ep_descriptor_comp(&alt_opts->ss_iso_ep_desc_comp, &alt_opts->ss_iso_ep_desc);
+
+	return status;
+}
+
+static struct f_uac2_path_descriptors *build_path_descriptors(struct path_params *params,
+							      struct f_uac2_alt_opts *alt_opts)
+{
+	struct f_uac2_path_descriptors *path_descs;
+	u8 srcId;
+
+	path_descs = kzalloc(sizeof(*path_descs), GFP_KERNEL);
+	if (!path_descs)
+		return NULL;
+
+	path_descs->dir = params->dir;
+	path_descs->alt_opts = alt_opts;
+
+	init_it_desc(&path_descs->it_desc, alt_opts, params);
+	srcId = path_descs->it_desc.bTerminalID;
+
+	if (((params->dir == HOST_TO_DEVICE) && FUOUT_EN(params->opts)) ||
+	    ((params->dir == DEVICE_TO_HOST) && FUIN_EN(params->opts))) {
+		path_descs->fu_desc = build_fu_desc(alt_opts, params,
+						    path_descs->it_desc.bTerminalID);
+		if (!path_descs->fu_desc) {
+			kfree(path_descs);
+			return NULL;
+		}
+		srcId = path_descs->fu_desc->bUnitID;
+	}
+
+	init_ot_desc(&path_descs->ot_desc, alt_opts, params, srcId);
+
+	return path_descs;
+}
+
+static void free_path_descriptors(struct f_uac2_path_descriptors *path_descs)
+{
+	kfree(path_descs->fu_desc);
+	kfree(path_descs);
+}
+
+static struct f_uac2_path_descriptors *find_path_descriptors(struct list_head *list,
+							   struct f_uac2_alt_opts *alt_opts,
+							   int dir)
+{
+	struct f_uac2_path_descriptors *path_descs;
+
+	list_for_each_entry(path_descs, list, list) {
+		/* Check that all options used in the path descriptors are the same */
+		if ((path_descs->dir == dir) &&
+		    (!strncmp(path_descs->alt_opts->name, alt_opts->name,
+			      sizeof(alt_opts->name))) &&
+		    (!strncmp(path_descs->alt_opts->it_name, alt_opts->it_name,
+			      sizeof(alt_opts->it_name))) &&
+		    (!strncmp(path_descs->alt_opts->it_ch_name, alt_opts->it_ch_name,
+			      sizeof(alt_opts->it_ch_name))) &&
+		    (!strncmp(path_descs->alt_opts->ot_name, alt_opts->ot_name,
+			      sizeof(alt_opts->ot_name))) &&
+		    (path_descs->alt_opts->chmask == alt_opts->chmask) &&
+		    (path_descs->alt_opts->terminal_type == alt_opts->terminal_type))
+			return path_descs;
+	}
+	return NULL;
+}
+
+static int add_path_descriptors(struct list_head *list, struct path_params *params,
+				struct f_uac2_alt_opts *alt_opts)
+{
+	int len = 0;
+	struct f_uac2_path_descriptors *path_descs;
+
+	if (!EP_EN(alt_opts))
+		return 0;
+
+	path_descs = find_path_descriptors(list, alt_opts, params->dir);
+
+	if (!path_descs) {
+		path_descs = build_path_descriptors(params, alt_opts);
+		if (path_descs) {
+			list_add_tail(&path_descs->list, list);
+			len += sizeof(path_descs->it_desc);
+			len += sizeof(path_descs->ot_desc);
+			if (path_descs->fu_desc)
+				len += path_descs->fu_desc->bLength;
+		}
+	}
+
+	if (path_descs) {
+		alt_opts->as_header_desc.bTerminalLink =
+			(params->dir == HOST_TO_DEVICE) ? path_descs->it_desc.bTerminalID
+							: path_descs->ot_desc.bTerminalID;
+		alt_opts->it_id = path_descs->it_desc.bTerminalID;
+		alt_opts->fu_id = path_descs->fu_desc ? path_descs->fu_desc->bUnitID : 0;
+		alt_opts->ot_id = path_descs->ot_desc.bTerminalID;
+	}
+
+	return len;
+}
+
 /* Use macro to overcome line length limitation */
-#define USBDHDR(p) (struct usb_descriptor_header *)(p)
+#define USBDHDR(p) ((struct usb_descriptor_header *)(p))
 
-static void setup_headers(struct f_uac2_opts *opts,
-			  struct usb_descriptor_header **headers,
-			  enum usb_device_speed speed)
+static inline void add_descriptor(int i, struct usb_descriptor_header **desc_list,
+				  struct usb_descriptor_header *desc)
+{
+	if (desc_list)
+		desc_list[i] = desc;
+}
+
+static int add_alt_descriptors(int i, struct usb_descriptor_header **desc_list,
+			       struct f_uac2_alt_opts *alt_opts, enum usb_device_speed speed)
+{
+	add_descriptor(i++, desc_list, USBDHDR(&alt_opts->intf_desc));
+	add_descriptor(i++, desc_list, USBDHDR(&alt_opts->as_header_desc));
+	add_descriptor(i++, desc_list, USBDHDR(&alt_opts->fmt_desc));
+	if (speed == USB_SPEED_FULL)
+		add_descriptor(i++, desc_list, USBDHDR(&alt_opts->fs_iso_ep_desc));
+	else if (speed == USB_SPEED_HIGH)
+		add_descriptor(i++, desc_list, USBDHDR(&alt_opts->hs_iso_ep_desc));
+	else if (speed == USB_SPEED_SUPER || speed == USB_SPEED_SUPER_PLUS) {
+		add_descriptor(i++, desc_list, USBDHDR(&alt_opts->ss_iso_ep_desc));
+		add_descriptor(i++, desc_list, USBDHDR(&alt_opts->ss_iso_ep_desc_comp));
+	}
+
+	return i;
+}
+
+
+static int setup_headers(struct f_uac2_opts *opts,
+			 struct usb_descriptor_header **headers,
+			 struct list_head *path_descs,
+			 enum usb_device_speed speed)
 {
-	struct usb_ss_ep_comp_descriptor *epout_desc_comp = NULL;
-	struct usb_ss_ep_comp_descriptor *epin_desc_comp = NULL;
 	struct usb_ss_ep_comp_descriptor *epin_fback_desc_comp = NULL;
 	struct usb_ss_ep_comp_descriptor *ep_int_desc_comp = NULL;
-	struct usb_endpoint_descriptor *epout_desc;
-	struct usb_endpoint_descriptor *epin_desc;
 	struct usb_endpoint_descriptor *epin_fback_desc;
 	struct usb_endpoint_descriptor *ep_int_desc;
-	int i;
+
+	int i = 0;
+	struct list_head *pos;
 
 	switch (speed) {
 	case USB_SPEED_FULL:
-		epout_desc = &fs_epout_desc;
-		epin_desc = &fs_epin_desc;
 		epin_fback_desc = &fs_epin_fback_desc;
 		ep_int_desc = &fs_ep_int_desc;
 		break;
 	case USB_SPEED_HIGH:
-		epout_desc = &hs_epout_desc;
-		epin_desc = &hs_epin_desc;
 		epin_fback_desc = &hs_epin_fback_desc;
 		ep_int_desc = &hs_ep_int_desc;
 		break;
 	default:
-		epout_desc = &ss_epout_desc;
-		epin_desc = &ss_epin_desc;
-		epout_desc_comp = &ss_epout_desc_comp;
-		epin_desc_comp = &ss_epin_desc_comp;
 		epin_fback_desc = &ss_epin_fback_desc;
 		epin_fback_desc_comp = &ss_epin_fback_desc_comp;
 		ep_int_desc = &ss_ep_int_desc;
 		ep_int_desc_comp = &ss_ep_int_desc_comp;
 	}
 
-	i = 0;
-	headers[i++] = USBDHDR(&iad_desc);
-	headers[i++] = USBDHDR(&std_ac_if_desc);
-	headers[i++] = USBDHDR(&ac_hdr_desc);
-	if (EPIN_EN(opts))
-		headers[i++] = USBDHDR(&in_clk_src_desc);
-	if (EPOUT_EN(opts)) {
-		headers[i++] = USBDHDR(&out_clk_src_desc);
-		headers[i++] = USBDHDR(&usb_out_it_desc);
-
-		if (FUOUT_EN(opts))
-			headers[i++] = USBDHDR(out_feature_unit_desc);
+	add_descriptor(i++, headers, USBDHDR(&iad_desc));
+	add_descriptor(i++, headers, USBDHDR(&std_ac_if_desc));
+	add_descriptor(i++, headers, USBDHDR(&ac_hdr_desc));
+	if (epin_en_any(opts))
+		add_descriptor(i++, headers, USBDHDR(&in_clk_src_desc));
+	if (epout_en_any(opts))
+		add_descriptor(i++, headers, USBDHDR(&out_clk_src_desc));
+
+	list_for_each(pos, path_descs) {
+		struct f_uac2_path_descriptors *path_desc =
+			container_of(pos, struct f_uac2_path_descriptors, list);
+		add_descriptor(i++, headers, USBDHDR(&path_desc->it_desc));
+		if (path_desc->fu_desc)
+			add_descriptor(i++, headers, USBDHDR(path_desc->fu_desc));
+		add_descriptor(i++, headers, USBDHDR(&path_desc->ot_desc));
 	}
 
-	if (EPIN_EN(opts)) {
-		headers[i++] = USBDHDR(&io_in_it_desc);
+	// If any FU exists, add the interrupt endpoint descriptor
+	if (FUOUT_EN(opts) || FUIN_EN(opts)) {
+		add_descriptor(i++, headers, USBDHDR(ep_int_desc));
+		if (ep_int_desc_comp)
+			add_descriptor(i++, headers, USBDHDR(ep_int_desc_comp));
+	}
 
-		if (FUIN_EN(opts))
-			headers[i++] = USBDHDR(in_feature_unit_desc);
+	// If any capture interface is active
+	if (epout_en_any(opts)) {
+		struct f_uac2_alt_opts *alt_opts;
 
-		headers[i++] = USBDHDR(&usb_in_ot_desc);
-	}
+		add_descriptor(i++, headers, USBDHDR(&opts->c_alt_0_opts.intf_desc));
 
-	if (EPOUT_EN(opts))
-		headers[i++] = USBDHDR(&io_out_ot_desc);
+		if (EP_EN(&opts->c_alt_1_opts)) {
+			i = add_alt_descriptors(i, headers, &opts->c_alt_1_opts, speed);
 
-	if (FUOUT_EN(opts) || FUIN_EN(opts)) {
-		headers[i++] = USBDHDR(ep_int_desc);
-		if (ep_int_desc_comp)
-			headers[i++] = USBDHDR(ep_int_desc_comp);
-	}
+			add_descriptor(i++, headers, USBDHDR(&as_iso_out_desc));
+			if (EPOUT_FBACK_IN_EN(&opts->c_alt_1_opts)) {
+				add_descriptor(i++, headers, USBDHDR(epin_fback_desc));
+				if (epin_fback_desc_comp)
+					add_descriptor(i++, headers, USBDHDR(epin_fback_desc_comp));
+			}
+		}
 
-	if (EPOUT_EN(opts)) {
-		headers[i++] = USBDHDR(&std_as_out_if0_desc);
-		headers[i++] = USBDHDR(&std_as_out_if1_desc);
-		headers[i++] = USBDHDR(&as_out_hdr_desc);
-		headers[i++] = USBDHDR(&as_out_fmt1_desc);
-		headers[i++] = USBDHDR(epout_desc);
-		if (epout_desc_comp)
-			headers[i++] = USBDHDR(epout_desc_comp);
-
-		headers[i++] = USBDHDR(&as_iso_out_desc);
-
-		if (EPOUT_FBACK_IN_EN(opts)) {
-			headers[i++] = USBDHDR(epin_fback_desc);
-			if (epin_fback_desc_comp)
-				headers[i++] = USBDHDR(epin_fback_desc_comp);
+		list_for_each_entry(alt_opts, &opts->c_alt_opts, list) {
+			if (EP_EN(alt_opts)) {
+				i = add_alt_descriptors(i, headers, alt_opts, speed);
+
+				add_descriptor(i++, headers, USBDHDR(&as_iso_out_desc));
+				if (EPOUT_FBACK_IN_EN(alt_opts)) {
+					add_descriptor(i++, headers, USBDHDR(epin_fback_desc));
+					if (epin_fback_desc_comp)
+						add_descriptor(i++, headers,
+							       USBDHDR(epin_fback_desc_comp));
+				}
+			}
 		}
 	}
 
-	if (EPIN_EN(opts)) {
-		headers[i++] = USBDHDR(&std_as_in_if0_desc);
-		headers[i++] = USBDHDR(&std_as_in_if1_desc);
-		headers[i++] = USBDHDR(&as_in_hdr_desc);
-		headers[i++] = USBDHDR(&as_in_fmt1_desc);
-		headers[i++] = USBDHDR(epin_desc);
-		if (epin_desc_comp)
-			headers[i++] = USBDHDR(epin_desc_comp);
+	// If any playback interface is active
+	if (epin_en_any(opts)) {
+		struct f_uac2_alt_opts *alt_opts;
+
+		add_descriptor(i++, headers, USBDHDR(&opts->p_alt_0_opts.intf_desc));
+
+		if (EP_EN(&opts->p_alt_1_opts)) {
+			i = add_alt_descriptors(i, headers, &opts->p_alt_1_opts, speed);
 
-		headers[i++] = USBDHDR(&as_iso_in_desc);
+			add_descriptor(i++, headers, USBDHDR(&as_iso_in_desc));
+		}
+
+		list_for_each_entry(alt_opts, &opts->p_alt_opts, list) {
+			if (EP_EN(alt_opts)) {
+				i = add_alt_descriptors(i, headers, alt_opts, speed);
+
+				add_descriptor(i++, headers, USBDHDR(&as_iso_in_desc));
+			}
+		}
 	}
-	headers[i] = NULL;
+
+	add_descriptor(i++, headers, NULL);
+
+	return i;
 }
 
-static void setup_descriptor(struct f_uac2_opts *opts)
+static int setup_descriptor(struct device *dev, struct f_uac2 *uac2, struct f_uac2_opts *opts,
+			    struct usb_string *strings)
 {
+	int status;
+	struct usb_descriptor_header **fs_desc_list, **hs_desc_list, **ss_ssp_desc_list;
+
 	/* patch descriptors */
-	int i = 1; /* ID's start with 1 */
-
-	if (EPOUT_EN(opts))
-		usb_out_it_desc.bTerminalID = i++;
-	if (EPIN_EN(opts))
-		io_in_it_desc.bTerminalID = i++;
-	if (EPOUT_EN(opts))
-		io_out_ot_desc.bTerminalID = i++;
-	if (EPIN_EN(opts))
-		usb_in_ot_desc.bTerminalID = i++;
-	if (FUOUT_EN(opts))
-		out_feature_unit_desc->bUnitID = i++;
-	if (FUIN_EN(opts))
-		in_feature_unit_desc->bUnitID = i++;
-	if (EPOUT_EN(opts))
-		out_clk_src_desc.bClockID = i++;
-	if (EPIN_EN(opts))
-		in_clk_src_desc.bClockID = i++;
-
-	usb_out_it_desc.bCSourceID = out_clk_src_desc.bClockID;
-
-	if (FUIN_EN(opts)) {
-		usb_in_ot_desc.bSourceID = in_feature_unit_desc->bUnitID;
-		in_feature_unit_desc->bSourceID = io_in_it_desc.bTerminalID;
-	} else {
-		usb_in_ot_desc.bSourceID = io_in_it_desc.bTerminalID;
-	}
+	int len;
+	struct list_head path_descs = LIST_HEAD_INIT(path_descs);
+	int fs_num, hs_num, ss_ssp_num;
+	struct f_uac2_alt_opts *alt_opts;
+	struct list_head *path_desc, *tmp;
+	struct path_params params;
 
-	usb_in_ot_desc.bCSourceID = in_clk_src_desc.bClockID;
-	io_in_it_desc.bCSourceID = in_clk_src_desc.bClockID;
-	io_out_ot_desc.bCSourceID = out_clk_src_desc.bClockID;
+	params.id = 1; /* ID's start with 1 */
+	params.opts = opts;
+	params.strings = strings;
 
-	if (FUOUT_EN(opts)) {
-		io_out_ot_desc.bSourceID = out_feature_unit_desc->bUnitID;
-		out_feature_unit_desc->bSourceID = usb_out_it_desc.bTerminalID;
-	} else {
-		io_out_ot_desc.bSourceID = usb_out_it_desc.bTerminalID;
-	}
+	len = sizeof(ac_hdr_desc);
 
-	as_out_hdr_desc.bTerminalLink = usb_out_it_desc.bTerminalID;
-	as_in_hdr_desc.bTerminalLink = usb_in_ot_desc.bTerminalID;
+	if (uac2->g_audio.out_ep) {
+		params.dir = HOST_TO_DEVICE;
+		out_clk_src_desc.bClockID = params.id++;
+		len += sizeof(out_clk_src_desc);
 
-	iad_desc.bInterfaceCount = 1;
-	ac_hdr_desc.wTotalLength = cpu_to_le16(sizeof(ac_hdr_desc));
+		init_as_interface_desc(&opts->c_alt_0_opts.intf_desc, uac2->as_out_intf, 0, 0,
+				       opts->c_alt_0_opts.name, strings);
 
-	if (EPIN_EN(opts)) {
-		u16 len = le16_to_cpu(ac_hdr_desc.wTotalLength);
+		/* Audio path descriptors (input terminal -> <feature unit> -> output terminal) */
+		len += add_path_descriptors(&path_descs, &params, &opts->c_alt_1_opts);
 
+		status = init_alt_descriptors(dev, &opts->c_alt_1_opts, uac2->as_out_intf,
+					     uac2->g_audio.out_ep->address,
+					     EPOUT_FBACK_IN_EN(&opts->c_alt_1_opts) ? 2 : 1,
+					     opts->c_alt_1_opts.it_id, HOST_TO_DEVICE, strings);
+		if (status) {
+			dev_err(dev, "Failed to init alt descs for capture alt %d (%d)\n",
+				1, status);
+			goto cleanup;
+		}
+
+		list_for_each_entry(alt_opts, &opts->c_alt_opts, list) {
+			len += add_path_descriptors(&path_descs, &params, alt_opts);
+
+			status = init_alt_descriptors(dev, alt_opts, uac2->as_out_intf,
+						      uac2->g_audio.out_ep->address,
+						      EPOUT_FBACK_IN_EN(alt_opts) ? 2 : 1,
+						      alt_opts->it_id, HOST_TO_DEVICE, strings);
+			if (status) {
+				dev_err(dev, "Failed to init alt descs for capture alt %d (%d)\n",
+					alt_opts->c.alt_num, status);
+				goto cleanup;
+			}
+		}
+	}
+
+	if (uac2->g_audio.in_ep) {
+		params.dir = DEVICE_TO_HOST;
+		in_clk_src_desc.bClockID = params.id++;
 		len += sizeof(in_clk_src_desc);
-		len += sizeof(usb_in_ot_desc);
 
-		if (FUIN_EN(opts))
-			len += in_feature_unit_desc->bLength;
+		init_as_interface_desc(&opts->p_alt_0_opts.intf_desc, uac2->as_in_intf, 0, 0,
+				       opts->p_alt_0_opts.name, strings);
 
-		len += sizeof(io_in_it_desc);
-		ac_hdr_desc.wTotalLength = cpu_to_le16(len);
-		iad_desc.bInterfaceCount++;
+		/* Audio path descriptors (input terminal -> <feature unit> -> output terminal) */
+		len += add_path_descriptors(&path_descs, &params, &opts->p_alt_1_opts);
+
+		status = init_alt_descriptors(dev, &opts->p_alt_1_opts, uac2->as_in_intf,
+					      uac2->g_audio.in_ep->address, 1,
+					      opts->p_alt_1_opts.ot_id, DEVICE_TO_HOST, strings);
+		if (status) {
+			dev_err(dev, "Failed to init alt descs for playback alt %d (%d)\n",
+				1, status);
+			goto cleanup;
+		}
+
+		list_for_each_entry(alt_opts, &opts->p_alt_opts, list) {
+			len += add_path_descriptors(&path_descs, &params, alt_opts);
+
+			status = init_alt_descriptors(dev, alt_opts, uac2->as_in_intf,
+						      uac2->g_audio.in_ep->address, 1,
+						      alt_opts->ot_id, DEVICE_TO_HOST, strings);
+			if (status) {
+				dev_err(dev, "Failed to init alt descs for playback alt %d (%d)\n",
+					alt_opts->c.alt_num, status);
+				goto cleanup;
+			}
+		}
 	}
-	if (EPOUT_EN(opts)) {
-		u16 len = le16_to_cpu(ac_hdr_desc.wTotalLength);
 
-		len += sizeof(out_clk_src_desc);
-		len += sizeof(usb_out_it_desc);
+	ac_hdr_desc.wTotalLength = cpu_to_le16(len);
 
-		if (FUOUT_EN(opts))
-			len += out_feature_unit_desc->bLength;
+	/* Count how many descriptors we have and then allocate and populate */
+	fs_num = setup_headers(opts, NULL, &path_descs, USB_SPEED_FULL);
+	hs_num = setup_headers(opts, NULL, &path_descs, USB_SPEED_HIGH);
+	ss_ssp_num = setup_headers(opts, NULL, &path_descs, USB_SPEED_SUPER);
 
-		len += sizeof(io_out_ot_desc);
-		ac_hdr_desc.wTotalLength = cpu_to_le16(len);
-		iad_desc.bInterfaceCount++;
+	fs_desc_list = kzalloc((fs_num + hs_num + ss_ssp_num) * sizeof(*fs_desc_list), GFP_KERNEL);
+	if (!fs_desc_list) {
+		status = -ENOMEM;
+		goto cleanup;
 	}
+	hs_desc_list = fs_desc_list + fs_num;
+	ss_ssp_desc_list = hs_desc_list + hs_num;
+
+	(void) setup_headers(opts, fs_desc_list, &path_descs, USB_SPEED_FULL);
+	(void) setup_headers(opts, hs_desc_list, &path_descs, USB_SPEED_HIGH);
+	(void) setup_headers(opts, ss_ssp_desc_list, &path_descs, USB_SPEED_SUPER);
 
-	io_in_it_desc.wTerminalType = cpu_to_le16(opts->c_terminal_type);
-	io_out_ot_desc.wTerminalType = cpu_to_le16(opts->p_terminal_type);
+	/* copy descriptors, and track endpoint copies */
+	status = usb_assign_descriptors(&uac2->g_audio.func, fs_desc_list, hs_desc_list,
+					ss_ssp_desc_list, ss_ssp_desc_list);
 
-	setup_headers(opts, fs_audio_desc, USB_SPEED_FULL);
-	setup_headers(opts, hs_audio_desc, USB_SPEED_HIGH);
-	setup_headers(opts, ss_audio_desc, USB_SPEED_SUPER);
+	if (status)
+		dev_err(dev, "Failed to assign descriptors (%d)\n", status);
+
+	kfree(fs_desc_list);
+
+cleanup:
+	list_for_each_safe(path_desc, tmp, &path_descs) {
+		free_path_descriptors(
+			container_of(path_desc, struct f_uac2_path_descriptors, list));
+	}
+
+	return status;
 }
 
 static int afunc_validate_opts(struct g_audio *agdev, struct device *dev)
 {
 	struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev);
+	struct f_uac2_alt_opts *alt_opts;
 	const char *msg = NULL;
 
-	if (!opts->p_chmask && !opts->c_chmask)
+	if (!epin_en_any(opts) && !epout_en_any(opts))
 		msg = "no playback and capture channels";
-	else if (opts->p_chmask & ~UAC2_CHANNEL_MASK)
-		msg = "unsupported playback channels mask";
-	else if (opts->c_chmask & ~UAC2_CHANNEL_MASK)
-		msg = "unsupported capture channels mask";
-	else if ((opts->p_ssize < 1) || (opts->p_ssize > 4))
-		msg = "incorrect playback sample size";
-	else if ((opts->c_ssize < 1) || (opts->c_ssize > 4))
-		msg = "incorrect capture sample size";
-	else if (!opts->p_srates[0])
+
+	list_for_each_entry(alt_opts, &opts->p_alt_opts, list) {
+		if (alt_opts->chmask & ~UAC2_CHANNEL_MASK)
+			msg = "unsupported playback channels mask";
+		else if ((alt_opts->ssize < 1) || (alt_opts->ssize > 4))
+			msg = "incorrect playback sample size";
+		else if ((alt_opts->hs_bint < 0) || (alt_opts->hs_bint > 4))
+			msg = "incorrect playback HS/SS bInterval (1-4: fixed, 0: auto)";
+	}
+
+	list_for_each_entry(alt_opts, &opts->c_alt_opts, list) {
+		if (alt_opts->chmask & ~UAC2_CHANNEL_MASK)
+			msg = "unsupported capture channels mask";
+		else if ((alt_opts->ssize < 1) || (alt_opts->ssize > 4))
+			msg = "incorrect capture sample size";
+		else if ((alt_opts->hs_bint < 0) || (alt_opts->hs_bint > 4))
+			msg = "incorrect capture HS/SS bInterval (1-4: fixed, 0: auto)";
+	}
+
+	if (!opts->p_srates[0])
 		msg = "incorrect playback sampling rate";
 	else if (!opts->c_srates[0])
 		msg = "incorrect capture sampling rate";
@@ -962,11 +1004,6 @@ static int afunc_validate_opts(struct g_audio *agdev, struct device *dev)
 	else if ((opts->c_volume_max - opts->c_volume_min) % opts->c_volume_res)
 		msg = "incorrect capture volume resolution";
 
-	else if ((opts->p_hs_bint < 0) || (opts->p_hs_bint > 4))
-		msg = "incorrect playback HS/SS bInterval (1-4: fixed, 0: auto)";
-	else if ((opts->c_hs_bint < 0) || (opts->c_hs_bint > 4))
-		msg = "incorrect capture HS/SS bInterval (1-4: fixed, 0: auto)";
-
 	if (msg) {
 		dev_err(dev, "Error: %s\n", msg);
 		return -EINVAL;
@@ -1025,6 +1062,25 @@ static void init_alt_opts(struct f_uac2_alt_opts *alt_opts, struct f_uac2_opts *
 	alt_opts->terminal_type = (!playback) ? opts->p_terminal_type : opts->c_terminal_type;
 }
 
+static u16 get_max_packet_size(struct f_uac2_alt_opts *alt_opts, struct list_head *list)
+{
+	u16 max_psize = max_t(u16,
+				le16_to_cpu(alt_opts->fs_iso_ep_desc.wMaxPacketSize),
+				le16_to_cpu(alt_opts->hs_iso_ep_desc.wMaxPacketSize));
+	max_psize = max_t(u16, max_psize,
+			le16_to_cpu(alt_opts->ss_iso_ep_desc.wMaxPacketSize));
+
+	list_for_each_entry(alt_opts, list, list) {
+		max_psize = max_t(u16, max_psize,
+				 le16_to_cpu(alt_opts->fs_iso_ep_desc.wMaxPacketSize));
+		max_psize = max_t(u16, max_psize,
+				 le16_to_cpu(alt_opts->hs_iso_ep_desc.wMaxPacketSize));
+		max_psize = max_t(u16, max_psize,
+				 le16_to_cpu(alt_opts->ss_iso_ep_desc.wMaxPacketSize));
+	}
+
+	return max_psize;
+}
 
 static int
 afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
@@ -1062,272 +1118,147 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
 	if (IS_ERR(us))
 		return PTR_ERR(us);
 
-	if (FUOUT_EN(uac2_opts)) {
-		out_feature_unit_desc = build_fu_desc(uac2_opts->c_chmask);
-		if (!out_feature_unit_desc)
-			return -ENOMEM;
-	}
-	if (FUIN_EN(uac2_opts)) {
-		in_feature_unit_desc = build_fu_desc(uac2_opts->p_chmask);
-		if (!in_feature_unit_desc) {
-			ret = -ENOMEM;
-			goto err_free_fu;
-		}
-	}
-
 	iad_desc.iFunction = add_string(us, uac2_opts->function_name);
 	std_ac_if_desc.iInterface = add_string(us, uac2_opts->if_ctrl_name);
 	in_clk_src_desc.iClockSource = add_string(us, uac2_opts->clksrc_in_name);
 	out_clk_src_desc.iClockSource = add_string(us, uac2_opts->clksrc_out_name);
-	usb_out_it_desc.iTerminal = add_string(us, uac2_opts->c_alt_1_opts.it_name);
-	usb_out_it_desc.iChannelNames = add_string(us, uac2_opts->c_alt_1_opts.it_ch_name);
-	io_in_it_desc.iTerminal = add_string(us, uac2_opts->p_alt_1_opts.it_name);
-	io_in_it_desc.iChannelNames = add_string(us, uac2_opts->p_alt_1_opts.it_ch_name);
-	usb_in_ot_desc.iTerminal = add_string(us, uac2_opts->p_alt_1_opts.ot_name);
-	io_out_ot_desc.iTerminal = add_string(us, uac2_opts->c_alt_1_opts.ot_name);
-	std_as_out_if0_desc.iInterface = add_string(us, uac2_opts->c_alt_0_opts.name);
-	std_as_out_if1_desc.iInterface = add_string(us, uac2_opts->c_alt_1_opts.name);
-	std_as_in_if0_desc.iInterface = add_string(us, uac2_opts->p_alt_0_opts.name);
-	std_as_in_if1_desc.iInterface = add_string(us, uac2_opts->p_alt_0_opts.name);
-
-	if (FUOUT_EN(uac2_opts)) {
-		u8 *i_feature = (u8 *)out_feature_unit_desc +
-				out_feature_unit_desc->bLength - 1;
-		*i_feature = add_string(us, uac2_opts->c_alt_1_opts.fu_vol_name);
-	}
-	if (FUIN_EN(uac2_opts)) {
-		u8 *i_feature = (u8 *)in_feature_unit_desc +
-				in_feature_unit_desc->bLength - 1;
-		*i_feature = add_string(us, uac2_opts->p_alt_1_opts.fu_vol_name);
-	}
-
-
-	/* Initialize the configurable parameters */
-	usb_out_it_desc.bNrChannels = num_channels(uac2_opts->c_chmask);
-	usb_out_it_desc.bmChannelConfig = cpu_to_le32(uac2_opts->c_chmask);
-	io_in_it_desc.bNrChannels = num_channels(uac2_opts->p_chmask);
-	io_in_it_desc.bmChannelConfig = cpu_to_le32(uac2_opts->p_chmask);
-	as_out_hdr_desc.bNrChannels = num_channels(uac2_opts->c_chmask);
-	as_out_hdr_desc.bmChannelConfig = cpu_to_le32(uac2_opts->c_chmask);
-	as_in_hdr_desc.bNrChannels = num_channels(uac2_opts->p_chmask);
-	as_in_hdr_desc.bmChannelConfig = cpu_to_le32(uac2_opts->p_chmask);
-	as_out_fmt1_desc.bSubslotSize = uac2_opts->c_ssize;
-	as_out_fmt1_desc.bBitResolution = uac2_opts->c_ssize * 8;
-	as_in_fmt1_desc.bSubslotSize = uac2_opts->p_ssize;
-	as_in_fmt1_desc.bBitResolution = uac2_opts->p_ssize * 8;
-	if (FUOUT_EN(uac2_opts)) {
-		__le32 *bma = (__le32 *)&out_feature_unit_desc->bmaControls[0];
-		u32 control = 0;
-
-		if (uac2_opts->c_mute_present)
-			control |= CONTROL_RDWR << FU_MUTE_CTRL;
-		if (uac2_opts->c_volume_present)
-			control |= CONTROL_RDWR << FU_VOL_CTRL;
-		*bma = cpu_to_le32(control);
-	}
-	if (FUIN_EN(uac2_opts)) {
-		__le32 *bma = (__le32 *)&in_feature_unit_desc->bmaControls[0];
-		u32 control = 0;
-
-		if (uac2_opts->p_mute_present)
-			control |= CONTROL_RDWR << FU_MUTE_CTRL;
-		if (uac2_opts->p_volume_present)
-			control |= CONTROL_RDWR << FU_VOL_CTRL;
-		*bma = cpu_to_le32(control);
-	}
 
+	/* allocate instance-specific interface IDs */
 	ret = usb_interface_id(cfg, fn);
 	if (ret < 0) {
 		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
-		goto err_free_fu;
+		goto fail;
 	}
 	iad_desc.bFirstInterface = ret;
+	iad_desc.bInterfaceCount = 1;
 
 	std_ac_if_desc.bInterfaceNumber = ret;
 	uac2->ac_intf = ret;
 	uac2->ac_alt = 0;
 
-	if (EPOUT_EN(uac2_opts)) {
+	if (epout_en_any(uac2_opts)) {
 		ret = usb_interface_id(cfg, fn);
 		if (ret < 0) {
 			dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
-			goto err_free_fu;
+			goto fail;
 		}
-		std_as_out_if0_desc.bInterfaceNumber = ret;
-		std_as_out_if1_desc.bInterfaceNumber = ret;
-		std_as_out_if1_desc.bNumEndpoints = 1;
+
+		iad_desc.bInterfaceCount++;
+
 		uac2->as_out_intf = ret;
 		uac2->as_out_alt = 0;
-
-		if (EPOUT_FBACK_IN_EN(uac2_opts)) {
-			fs_epout_desc.bmAttributes =
-			  USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC;
-			hs_epout_desc.bmAttributes =
-			  USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC;
-			ss_epout_desc.bmAttributes =
-			  USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC;
-			std_as_out_if1_desc.bNumEndpoints++;
-		} else {
-			fs_epout_desc.bmAttributes =
-			  USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE;
-			hs_epout_desc.bmAttributes =
-			  USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE;
-			ss_epout_desc.bmAttributes =
-			  USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE;
-		}
 	}
 
-	if (EPIN_EN(uac2_opts)) {
+	if (epin_en_any(uac2_opts)) {
 		ret = usb_interface_id(cfg, fn);
 		if (ret < 0) {
 			dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
-			goto err_free_fu;
+			goto fail;
 		}
-		std_as_in_if0_desc.bInterfaceNumber = ret;
-		std_as_in_if1_desc.bInterfaceNumber = ret;
+
+		iad_desc.bInterfaceCount++;
+
 		uac2->as_in_intf = ret;
 		uac2->as_in_alt = 0;
 	}
 
+	/* allocate AC interrupt endpoint */
 	if (FUOUT_EN(uac2_opts) || FUIN_EN(uac2_opts)) {
 		uac2->int_ep = usb_ep_autoconfig(gadget, &fs_ep_int_desc);
 		if (!uac2->int_ep) {
 			dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
 			ret = -ENODEV;
-			goto err_free_fu;
+			goto fail;
 		}
+		hs_ep_int_desc.bEndpointAddress = fs_ep_int_desc.bEndpointAddress;
+		ss_ep_int_desc.bEndpointAddress = fs_ep_int_desc.bEndpointAddress;
 
 		std_ac_if_desc.bNumEndpoints = 1;
 	}
 
-	hs_epin_desc.bInterval = uac2_opts->p_hs_bint;
-	ss_epin_desc.bInterval = uac2_opts->p_hs_bint;
-	hs_epout_desc.bInterval = uac2_opts->c_hs_bint;
-	ss_epout_desc.bInterval = uac2_opts->c_hs_bint;
-
-	/* Calculate wMaxPacketSize according to audio bandwidth */
-	ret = set_ep_max_packet_size_bint(dev, &uac2_opts->p_alt_1_opts, &fs_epin_desc,
-					USB_SPEED_FULL, true);
-	if (ret < 0) {
-		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
-		return ret;
-	}
-
-	ret = set_ep_max_packet_size_bint(dev, &uac2_opts->c_alt_1_opts, &fs_epout_desc,
-					USB_SPEED_FULL, false);
-	if (ret < 0) {
-		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
-		return ret;
-	}
-
-	ret = set_ep_max_packet_size_bint(dev, &uac2_opts->p_alt_1_opts, &hs_epin_desc,
-					USB_SPEED_HIGH, true);
-	if (ret < 0) {
-		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
-		return ret;
-	}
-
-	ret = set_ep_max_packet_size_bint(dev, &uac2_opts->c_alt_1_opts, &hs_epout_desc,
-					USB_SPEED_HIGH, false);
-	if (ret < 0) {
-		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
-		return ret;
-	}
-
-	ret = set_ep_max_packet_size_bint(dev, &uac2_opts->p_alt_1_opts, &ss_epin_desc,
-					USB_SPEED_SUPER, true);
-	if (ret < 0) {
-		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
-		return ret;
-	}
-
-	ret = set_ep_max_packet_size_bint(dev, &uac2_opts->c_alt_1_opts, &ss_epout_desc,
-					USB_SPEED_SUPER, false);
-	if (ret < 0) {
-		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
-		return ret;
-	}
+	/* Allocate instance-specific endpoints. These use the FS version for alt mode 1.
+	 * All other alt modes and speeds will be initialized to the same endpoint address
+	 * during the setup_descriptor() call. The u_audio code will update the currently
+	 * selected endpoint descriptor when the alt mode changes.
+	 */
+	if (epout_en_any(uac2_opts)) {
+		ret = init_isoc_ep_descriptor(dev, &uac2_opts->c_alt_1_opts.fs_iso_ep_desc,
+					      &uac2_opts->c_alt_1_opts, HOST_TO_DEVICE,
+					      USB_SPEED_FULL, USB_DIR_OUT);
+		if (ret) {
+			dev_err(dev, "Failed to init FS isoc ep desc for capture (%d)\n", ret);
+			goto fail;
+		}
 
-	if (EPOUT_EN(uac2_opts)) {
-		agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc);
+		agdev->out_ep = usb_ep_autoconfig(gadget, &uac2_opts->c_alt_1_opts.fs_iso_ep_desc);
 		if (!agdev->out_ep) {
 			dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
 			ret = -ENODEV;
-			goto err_free_fu;
+			goto fail;
 		}
-		if (EPOUT_FBACK_IN_EN(uac2_opts)) {
+		if (epout_fback_in_en_any(uac2_opts)) {
 			agdev->in_ep_fback = usb_ep_autoconfig(gadget,
 						       &fs_epin_fback_desc);
 			if (!agdev->in_ep_fback) {
 				dev_err(dev, "%s:%d Error!\n",
 					__func__, __LINE__);
 				ret = -ENODEV;
-				goto err_free_fu;
+				goto fail;
 			}
+			hs_epin_fback_desc.bEndpointAddress = fs_epin_fback_desc.bEndpointAddress;
+			ss_epin_fback_desc.bEndpointAddress = fs_epin_fback_desc.bEndpointAddress;
 		}
 	}
 
-	if (EPIN_EN(uac2_opts)) {
-		agdev->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc);
+	if (epin_en_any(uac2_opts)) {
+		ret = init_isoc_ep_descriptor(dev, &uac2_opts->p_alt_1_opts.fs_iso_ep_desc,
+					      &uac2_opts->p_alt_1_opts, HOST_TO_DEVICE,
+					      USB_SPEED_FULL, USB_DIR_IN);
+		if (ret) {
+			dev_err(dev, "Failed to init FS isoc ep desc for playback (%d)\n", ret);
+			goto fail;
+		}
+
+		agdev->in_ep = usb_ep_autoconfig(gadget, &uac2_opts->p_alt_1_opts.fs_iso_ep_desc);
 		if (!agdev->in_ep) {
 			dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
 			ret = -ENODEV;
-			goto err_free_fu;
+			goto fail;
 		}
 	}
 
-	agdev->in_ep_maxpsize = max_t(u16,
-				le16_to_cpu(fs_epin_desc.wMaxPacketSize),
-				le16_to_cpu(hs_epin_desc.wMaxPacketSize));
-	agdev->out_ep_maxpsize = max_t(u16,
-				le16_to_cpu(fs_epout_desc.wMaxPacketSize),
-				le16_to_cpu(hs_epout_desc.wMaxPacketSize));
-
-	agdev->in_ep_maxpsize = max_t(u16, agdev->in_ep_maxpsize,
-				le16_to_cpu(ss_epin_desc.wMaxPacketSize));
-	agdev->out_ep_maxpsize = max_t(u16, agdev->out_ep_maxpsize,
-				le16_to_cpu(ss_epout_desc.wMaxPacketSize));
-
-	ss_epin_desc_comp.wBytesPerInterval = ss_epin_desc.wMaxPacketSize;
-	ss_epout_desc_comp.wBytesPerInterval = ss_epout_desc.wMaxPacketSize;
-
-	// HS and SS endpoint addresses are copied from autoconfigured FS descriptors
-	hs_ep_int_desc.bEndpointAddress = fs_ep_int_desc.bEndpointAddress;
-	hs_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress;
-	hs_epin_fback_desc.bEndpointAddress = fs_epin_fback_desc.bEndpointAddress;
-	hs_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress;
-	ss_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress;
-	ss_epin_fback_desc.bEndpointAddress = fs_epin_fback_desc.bEndpointAddress;
-	ss_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress;
-	ss_ep_int_desc.bEndpointAddress = fs_ep_int_desc.bEndpointAddress;
-
-	setup_descriptor(uac2_opts);
-
-	ret = usb_assign_descriptors(fn, fs_audio_desc, hs_audio_desc, ss_audio_desc,
-				     ss_audio_desc);
-	if (ret)
-		goto err_free_fu;
+	agdev->out_ep_maxpsize = get_max_packet_size(&uac2_opts->c_alt_1_opts,
+						     &uac2_opts->c_alt_opts);
+	agdev->in_ep_maxpsize = get_max_packet_size(&uac2_opts->p_alt_1_opts,
+						    &uac2_opts->p_alt_opts);
+
+	setup_descriptor(dev, uac2, uac2_opts, us);
 
 	agdev->gadget = gadget;
 
+	// TODO: This may need some change with the audio params for the current alt mode
 	agdev->params.p_chmask = uac2_opts->p_chmask;
 	memcpy(agdev->params.p_srates, uac2_opts->p_srates,
 			sizeof(agdev->params.p_srates));
 	agdev->params.p_ssize = uac2_opts->p_ssize;
+
 	if (FUIN_EN(uac2_opts)) {
-		agdev->params.p_fu.id = USB_IN_FU_ID;
+		agdev->params.p_fu.id = USB_IN_FU_ID(uac2_opts);
 		agdev->params.p_fu.mute_present = uac2_opts->p_mute_present;
 		agdev->params.p_fu.volume_present = uac2_opts->p_volume_present;
 		agdev->params.p_fu.volume_min = uac2_opts->p_volume_min;
 		agdev->params.p_fu.volume_max = uac2_opts->p_volume_max;
 		agdev->params.p_fu.volume_res = uac2_opts->p_volume_res;
 	}
+
+	// TODO: This may need some change with the audio params for the current alt mode
 	agdev->params.c_chmask = uac2_opts->c_chmask;
 	memcpy(agdev->params.c_srates, uac2_opts->c_srates,
 			sizeof(agdev->params.c_srates));
 	agdev->params.c_ssize = uac2_opts->c_ssize;
+
 	if (FUOUT_EN(uac2_opts)) {
-		agdev->params.c_fu.id = USB_OUT_FU_ID;
+		agdev->params.c_fu.id = USB_OUT_FU_ID(uac2_opts);
 		agdev->params.c_fu.mute_present = uac2_opts->c_mute_present;
 		agdev->params.c_fu.volume_present = uac2_opts->c_volume_present;
 		agdev->params.c_fu.volume_min = uac2_opts->c_volume_min;
@@ -1338,7 +1269,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
 	agdev->params.fb_max = uac2_opts->fb_max;
 
 	if (FUOUT_EN(uac2_opts) || FUIN_EN(uac2_opts))
-    agdev->notify = afunc_notify;
+		agdev->notify = afunc_notify;
 
 	ret = g_audio_setup(agdev, "UAC2 PCM", "UAC2_Gadget");
 	if (ret)
@@ -1349,11 +1280,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
 err_free_descs:
 	usb_free_all_descriptors(fn);
 	agdev->gadget = NULL;
-err_free_fu:
-	kfree(out_feature_unit_desc);
-	out_feature_unit_desc = NULL;
-	kfree(in_feature_unit_desc);
-	in_feature_unit_desc = NULL;
+fail:
 	return ret;
 }
 
@@ -1428,7 +1355,7 @@ afunc_notify(struct g_audio *agdev, int unit_id, int cs)
 }
 
 static int
-afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
+afunc_set_alt(struct usb_function *fn, unsigned int intf, unsigned int alt)
 {
 	struct usb_composite_dev *cdev = fn->config->cdev;
 	struct f_uac2 *uac2 = func_to_uac2(fn);
@@ -1483,7 +1410,7 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
 }
 
 static int
-afunc_get_alt(struct usb_function *fn, unsigned intf)
+afunc_get_alt(struct usb_function *fn, unsigned int intf)
 {
 	struct f_uac2 *uac2 = func_to_uac2(fn);
 	struct g_audio *agdev = func_to_g_audio(fn);
@@ -1561,11 +1488,11 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
 				"%s:%d control_selector=%d TODO!\n",
 				__func__, __LINE__, control_selector);
 		}
-	} else if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
-			(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
+	} else if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) ||
+			(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID(opts)))) {
 		unsigned int is_playback = 0;
 
-		if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
+		if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts)))
 			is_playback = 1;
 
 		if (control_selector == UAC_FU_MUTE) {
@@ -1650,11 +1577,11 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr)
 				"%s:%d control_selector=%d TODO!\n",
 				__func__, __LINE__, control_selector);
 		}
-	} else if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
-			(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
+	} else if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) ||
+			(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID(opts)))) {
 		unsigned int is_playback = 0;
 
-		if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
+		if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts)))
 			is_playback = 1;
 
 		if (control_selector == UAC_FU_VOLUME) {
@@ -1740,11 +1667,11 @@ out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req)
 		return;
 	}
 
-	if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
-		(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
+	if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) ||
+		(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID(opts)))) {
 		unsigned int is_playback = 0;
 
-		if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
+		if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts)))
 			is_playback = 1;
 
 		if (control_selector == UAC_FU_MUTE) {
@@ -1794,8 +1721,8 @@ out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
 			req->complete = uac2_cs_control_sam_freq;
 			return w_length;
 		}
-	} else if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
-			(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
+	} else if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) ||
+			(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID(opts)))) {
 		memcpy(&uac2->setup_cr, cr, sizeof(*cr));
 		req->context = agdev;
 		req->complete = out_rq_cur_complete;
@@ -2292,11 +2219,6 @@ static void afunc_unbind(struct usb_configuration *c, struct usb_function *f)
 	usb_free_all_descriptors(f);
 
 	agdev->gadget = NULL;
-
-	kfree(out_feature_unit_desc);
-	out_feature_unit_desc = NULL;
-	kfree(in_feature_unit_desc);
-	in_feature_unit_desc = NULL;
 }
 
 static struct usb_function *afunc_alloc(struct usb_function_instance *fi)
diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h
index 8c061e588324..91171c6e493a 100644
--- a/drivers/usb/gadget/function/u_uac2.h
+++ b/drivers/usb/gadget/function/u_uac2.h
@@ -53,6 +53,9 @@ struct f_uac2_alt_0_opts {
 	struct f_uac2_alt_opts_common c;
 
 	char			name[USB_MAX_STRING_LEN];
+
+	/* Descriptors */
+	struct usb_interface_descriptor	intf_desc;
 };
 
 /* Alt modes 1+ */
@@ -75,6 +78,35 @@ struct f_uac2_alt_opts {
 	u8			hs_bint;
 	s16			terminal_type;
 
+	/* Descriptors */
+	struct usb_interface_descriptor		intf_desc;
+	struct uac2_as_header_descriptor	as_header_desc;
+	struct uac2_format_type_i_descriptor	fmt_desc;
+
+	struct usb_endpoint_descriptor		fs_iso_ep_desc;
+	struct usb_endpoint_descriptor		hs_iso_ep_desc;
+	struct usb_endpoint_descriptor		ss_iso_ep_desc;
+	struct usb_ss_ep_comp_descriptor	ss_iso_ep_desc_comp;
+
+	u8 clk_id; /* Clock Source Descriptor bClockID */
+	u8 it_id;  /* Input Terminal Descriptor bTerminalID */
+	u8 fu_id;  /* Feature Unit Descriptor bUnitID */
+	u8 ot_id;  /* Output Terminal Descriptor bTerminalID */
+};
+
+struct f_uac2_path_descriptors {
+	struct list_head list;
+
+	int dir; /* HOST_TO_DEVICE or DEVICE_TO_HOST */
+
+	/* Alt mode opts this path descriptor is from */
+	struct f_uac2_alt_opts *alt_opts;
+
+	struct uac2_input_terminal_descriptor it_desc;
+	struct uac2_output_terminal_descriptor ot_desc;
+
+	/* Feature unit is optional */
+	struct uac2_feature_unit_descriptor *fu_desc;
 };
 
 struct f_uac2_opts {
