diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index 3484aa237354..7803957e4f82 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -27,16 +27,36 @@
 /* UAC1 spec: 3.7.2.3 Audio Channel Cluster Format */
 #define UAC1_CHANNEL_MASK 0x0FFF
 
-#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 EPIN_EN(_opts) ((_opts)->p_chmask != 0)
-#define EPOUT_EN(_opts) ((_opts)->c_chmask != 0)
+#define EP_EN(_alt_opts) ((_alt_opts) && ((_alt_opts)->chmask != 0))
 #define FUIN_EN(_opts) ((_opts)->p_mute_present \
 			|| (_opts)->p_volume_present)
 #define FUOUT_EN(_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_uac1_opts *opts)					\
+{									\
+	struct f_uac1_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_uac1 {
 	struct g_audio g_audio;
@@ -88,49 +108,6 @@ static struct usb_interface_descriptor ac_interface_desc = {
 /* B.3.2  Class-Specific AC Interface Descriptor */
 static struct uac1_ac_header_descriptor *ac_header_desc;
 
-static struct uac_input_terminal_descriptor usb_out_it_desc = {
-	.bLength =		UAC_DT_INPUT_TERMINAL_SIZE,
-	.bDescriptorType =	USB_DT_CS_INTERFACE,
-	.bDescriptorSubtype =	UAC_INPUT_TERMINAL,
-	/* .bTerminalID =	DYNAMIC */
-	.wTerminalType =	cpu_to_le16(UAC_TERMINAL_STREAMING),
-	.bAssocTerminal =	0,
-	.wChannelConfig =	cpu_to_le16(0x3),
-};
-
-static struct uac1_output_terminal_descriptor io_out_ot_desc = {
-	.bLength		= UAC_DT_OUTPUT_TERMINAL_SIZE,
-	.bDescriptorType	= USB_DT_CS_INTERFACE,
-	.bDescriptorSubtype	= UAC_OUTPUT_TERMINAL,
-	/* .bTerminalID =	DYNAMIC */
-	.wTerminalType		= cpu_to_le16(UAC_OUTPUT_TERMINAL_SPEAKER),
-	.bAssocTerminal		= 0,
-	/* .bSourceID =		DYNAMIC */
-};
-
-static struct uac_input_terminal_descriptor io_in_it_desc = {
-	.bLength		= UAC_DT_INPUT_TERMINAL_SIZE,
-	.bDescriptorType	= USB_DT_CS_INTERFACE,
-	.bDescriptorSubtype	= UAC_INPUT_TERMINAL,
-	/* .bTerminalID		= DYNAMIC */
-	.wTerminalType		= cpu_to_le16(UAC_INPUT_TERMINAL_MICROPHONE),
-	.bAssocTerminal		= 0,
-	.wChannelConfig		= cpu_to_le16(0x3),
-};
-
-static struct uac1_output_terminal_descriptor usb_in_ot_desc = {
-	.bLength =		UAC_DT_OUTPUT_TERMINAL_SIZE,
-	.bDescriptorType =	USB_DT_CS_INTERFACE,
-	.bDescriptorSubtype =	UAC_OUTPUT_TERMINAL,
-	/* .bTerminalID =	DYNAMIC */
-	.wTerminalType =	cpu_to_le16(UAC_TERMINAL_STREAMING),
-	.bAssocTerminal =	0,
-	/* .bSourceID =		DYNAMIC */
-};
-
-static struct uac_feature_unit_descriptor *in_feature_unit_desc;
-static struct uac_feature_unit_descriptor *out_feature_unit_desc;
-
 /* AC IN Interrupt Endpoint */
 static struct usb_endpoint_descriptor fs_ac_int_ep_desc = {
 	.bLength = USB_DT_ENDPOINT_SIZE,
@@ -167,97 +144,6 @@ static struct usb_ss_ep_comp_descriptor ss_ac_int_ep_desc_comp = {
 	.wBytesPerInterval = cpu_to_le16(2),
 };
 
-/* B.4.1  Standard AS Interface Descriptor */
-static struct usb_interface_descriptor as_out_interface_alt_0_desc = {
-	.bLength =		USB_DT_INTERFACE_SIZE,
-	.bDescriptorType =	USB_DT_INTERFACE,
-	.bAlternateSetting =	0,
-	.bNumEndpoints =	0,
-	.bInterfaceClass =	USB_CLASS_AUDIO,
-	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOSTREAMING,
-};
-
-static struct usb_interface_descriptor as_out_interface_alt_1_desc = {
-	.bLength =		USB_DT_INTERFACE_SIZE,
-	.bDescriptorType =	USB_DT_INTERFACE,
-	.bAlternateSetting =	1,
-	.bNumEndpoints =	1,
-	.bInterfaceClass =	USB_CLASS_AUDIO,
-	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOSTREAMING,
-};
-
-static struct usb_interface_descriptor as_in_interface_alt_0_desc = {
-	.bLength =		USB_DT_INTERFACE_SIZE,
-	.bDescriptorType =	USB_DT_INTERFACE,
-	.bAlternateSetting =	0,
-	.bNumEndpoints =	0,
-	.bInterfaceClass =	USB_CLASS_AUDIO,
-	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOSTREAMING,
-};
-
-static struct usb_interface_descriptor as_in_interface_alt_1_desc = {
-	.bLength =		USB_DT_INTERFACE_SIZE,
-	.bDescriptorType =	USB_DT_INTERFACE,
-	.bAlternateSetting =	1,
-	.bNumEndpoints =	1,
-	.bInterfaceClass =	USB_CLASS_AUDIO,
-	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOSTREAMING,
-};
-
-/* B.4.2  Class-Specific AS Interface Descriptor */
-static struct uac1_as_header_descriptor as_out_header_desc = {
-	.bLength =		UAC_DT_AS_HEADER_SIZE,
-	.bDescriptorType =	USB_DT_CS_INTERFACE,
-	.bDescriptorSubtype =	UAC_AS_GENERAL,
-	/* .bTerminalLink =	DYNAMIC */
-	.bDelay =		1,
-	.wFormatTag =		cpu_to_le16(UAC_FORMAT_TYPE_I_PCM),
-};
-
-static struct uac1_as_header_descriptor as_in_header_desc = {
-	.bLength =		UAC_DT_AS_HEADER_SIZE,
-	.bDescriptorType =	USB_DT_CS_INTERFACE,
-	.bDescriptorSubtype =	UAC_AS_GENERAL,
-	/* .bTerminalLink =	DYNAMIC */
-	.bDelay =		1,
-	.wFormatTag =		cpu_to_le16(UAC_FORMAT_TYPE_I_PCM),
-};
-
-DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(UAC_MAX_RATES);
-#define uac_format_type_i_discrete_descriptor			\
-	uac_format_type_i_discrete_descriptor_##UAC_MAX_RATES
-
-static struct uac_format_type_i_discrete_descriptor as_out_type_i_desc = {
-	.bLength =		0, /* filled on rate setup */
-	.bDescriptorType =	USB_DT_CS_INTERFACE,
-	.bDescriptorSubtype =	UAC_FORMAT_TYPE,
-	.bFormatType =		UAC_FORMAT_TYPE_I,
-	.bSubframeSize =	2,
-	.bBitResolution =	16,
-	.bSamFreqType =		0, /* filled on rate setup */
-};
-
-/* Standard ISO OUT Endpoint Descriptor */
-static struct usb_endpoint_descriptor fs_as_out_ep_desc  = {
-	.bLength =		USB_DT_ENDPOINT_AUDIO_SIZE,
-	.bDescriptorType =	USB_DT_ENDPOINT,
-	.bEndpointAddress =	USB_DIR_OUT,
-	.bmAttributes =		USB_ENDPOINT_SYNC_ADAPTIVE
-				| USB_ENDPOINT_XFER_ISOC,
-	.wMaxPacketSize	=	cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE),
-	.bInterval =		1,
-};
-
-static struct usb_endpoint_descriptor hs_as_out_ep_desc  = {
-	.bLength =		USB_DT_ENDPOINT_AUDIO_SIZE,
-	.bDescriptorType =	USB_DT_ENDPOINT,
-	.bEndpointAddress =	USB_DIR_OUT,
-	.bmAttributes =		USB_ENDPOINT_SYNC_ADAPTIVE
-				| USB_ENDPOINT_XFER_ISOC,
-	.wMaxPacketSize	=	cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE),
-	.bInterval =		4,
-};
-
 /* Class-specific AS ISO OUT Endpoint Descriptor */
 static struct uac_iso_endpoint_descriptor as_iso_out_desc = {
 	.bLength =		UAC_ISO_ENDPOINT_DESC_SIZE,
@@ -268,37 +154,6 @@ static struct uac_iso_endpoint_descriptor as_iso_out_desc = {
 	.wLockDelay =		cpu_to_le16(1),
 };
 
-static struct uac_format_type_i_discrete_descriptor as_in_type_i_desc = {
-	.bLength =		0, /* filled on rate setup */
-	.bDescriptorType =	USB_DT_CS_INTERFACE,
-	.bDescriptorSubtype =	UAC_FORMAT_TYPE,
-	.bFormatType =		UAC_FORMAT_TYPE_I,
-	.bSubframeSize =	2,
-	.bBitResolution =	16,
-	.bSamFreqType =		0, /* filled on rate setup */
-};
-
-/* Standard ISO IN Endpoint Descriptor */
-static struct usb_endpoint_descriptor fs_as_in_ep_desc  = {
-	.bLength =		USB_DT_ENDPOINT_AUDIO_SIZE,
-	.bDescriptorType =	USB_DT_ENDPOINT,
-	.bEndpointAddress =	USB_DIR_IN,
-	.bmAttributes =		USB_ENDPOINT_SYNC_ASYNC
-				| USB_ENDPOINT_XFER_ISOC,
-	.wMaxPacketSize	=	cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE),
-	.bInterval =		1,
-};
-
-static struct usb_endpoint_descriptor hs_as_in_ep_desc  = {
-	.bLength =		USB_DT_ENDPOINT_AUDIO_SIZE,
-	.bDescriptorType =	USB_DT_ENDPOINT,
-	.bEndpointAddress =	USB_DIR_IN,
-	.bmAttributes =		USB_ENDPOINT_SYNC_ASYNC
-				| USB_ENDPOINT_XFER_ISOC,
-	.wMaxPacketSize	=	cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE),
-	.bInterval =		4,
-};
-
 /* Class-specific AS ISO IN Endpoint Descriptor */
 static struct uac_iso_endpoint_descriptor as_iso_in_desc = {
 	.bLength =		UAC_ISO_ENDPOINT_DESC_SIZE,
@@ -351,153 +206,6 @@ static struct usb_ss_ep_comp_descriptor ss_as_in_fback_desc_comp = {
 	.wBytesPerInterval	= cpu_to_le16(4),
 };
 
-static struct usb_descriptor_header *f_audio_fs_desc[] = {
-	(struct usb_descriptor_header *)&ac_interface_desc,
-	(struct usb_descriptor_header *)&ac_header_desc,
-
-	(struct usb_descriptor_header *)&usb_out_it_desc,
-	(struct usb_descriptor_header *)&io_out_ot_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 *)&fs_ac_int_ep_desc,
-
-	(struct usb_descriptor_header *)&as_out_interface_alt_0_desc,
-	(struct usb_descriptor_header *)&as_out_interface_alt_1_desc,
-	(struct usb_descriptor_header *)&as_out_header_desc,
-
-	(struct usb_descriptor_header *)&as_out_type_i_desc,
-
-	(struct usb_descriptor_header *)&fs_as_out_ep_desc,
-	(struct usb_descriptor_header *)&as_iso_out_desc,
-	(struct usb_descriptor_header *)&fs_as_in_fback_desc,
-
-	(struct usb_descriptor_header *)&as_in_interface_alt_0_desc,
-	(struct usb_descriptor_header *)&as_in_interface_alt_1_desc,
-	(struct usb_descriptor_header *)&as_in_header_desc,
-
-	(struct usb_descriptor_header *)&as_in_type_i_desc,
-
-	(struct usb_descriptor_header *)&fs_as_in_ep_desc,
-	(struct usb_descriptor_header *)&as_iso_in_desc,
-	NULL,
-};
-
-static struct usb_descriptor_header *f_audio_hs_desc[] = {
-	(struct usb_descriptor_header *)&ac_interface_desc,
-	(struct usb_descriptor_header *)&ac_header_desc,
-
-	(struct usb_descriptor_header *)&usb_out_it_desc,
-	(struct usb_descriptor_header *)&io_out_ot_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 *)&hs_ac_int_ep_desc,
-
-	(struct usb_descriptor_header *)&as_out_interface_alt_0_desc,
-	(struct usb_descriptor_header *)&as_out_interface_alt_1_desc,
-	(struct usb_descriptor_header *)&as_out_header_desc,
-
-	(struct usb_descriptor_header *)&as_out_type_i_desc,
-
-	(struct usb_descriptor_header *)&hs_as_out_ep_desc,
-	(struct usb_descriptor_header *)&as_iso_out_desc,
-	(struct usb_descriptor_header *)&hs_as_in_fback_desc,
-
-	(struct usb_descriptor_header *)&as_in_interface_alt_0_desc,
-	(struct usb_descriptor_header *)&as_in_interface_alt_1_desc,
-	(struct usb_descriptor_header *)&as_in_header_desc,
-
-	(struct usb_descriptor_header *)&as_in_type_i_desc,
-
-	(struct usb_descriptor_header *)&hs_as_in_ep_desc,
-	(struct usb_descriptor_header *)&as_iso_in_desc,
-	NULL,
-};
-
-/* Standard ISO OUT Endpoint Descriptor */
-static struct usb_endpoint_descriptor ss_as_out_ep_desc  = {
-	.bLength =		USB_DT_ENDPOINT_AUDIO_SIZE,
-	.bDescriptorType =	USB_DT_ENDPOINT,
-	.bEndpointAddress =	USB_DIR_OUT,
-	.bmAttributes =		USB_ENDPOINT_SYNC_ADAPTIVE
-				| USB_ENDPOINT_XFER_ISOC,
-	.wMaxPacketSize	=	cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE),
-	.bInterval =		4,
-};
-
-static struct usb_ss_ep_comp_descriptor ss_as_out_ep_desc_comp = {
-	.bLength		= sizeof(ss_as_out_ep_desc_comp),
-	.bDescriptorType	= USB_DT_SS_ENDPOINT_COMP,
-	.bMaxBurst		= 0,
-	.bmAttributes		= 0,
-	/* wBytesPerInterval = DYNAMIC */
-};
-
-/* Standard ISO IN Endpoint Descriptor */
-static struct usb_endpoint_descriptor ss_as_in_ep_desc  = {
-	.bLength =		USB_DT_ENDPOINT_AUDIO_SIZE,
-	.bDescriptorType =	USB_DT_ENDPOINT,
-	.bEndpointAddress =	USB_DIR_IN,
-	.bmAttributes =		USB_ENDPOINT_SYNC_ASYNC
-				| USB_ENDPOINT_XFER_ISOC,
-	.wMaxPacketSize	=	cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE),
-	.bInterval =		4,
-};
-
-static struct usb_ss_ep_comp_descriptor ss_as_in_ep_desc_comp = {
-	.bLength		= sizeof(ss_as_in_ep_desc_comp),
-	.bDescriptorType	= USB_DT_SS_ENDPOINT_COMP,
-	.bMaxBurst		= 0,
-	.bmAttributes		= 0,
-	/* wBytesPerInterval = DYNAMIC */
-};
-
-static struct usb_descriptor_header *f_audio_ss_desc[] = {
-	(struct usb_descriptor_header *)&ac_interface_desc,
-	(struct usb_descriptor_header *)&ac_header_desc,
-
-	(struct usb_descriptor_header *)&usb_out_it_desc,
-	(struct usb_descriptor_header *)&io_out_ot_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 *)&ss_ac_int_ep_desc,
-	(struct usb_descriptor_header *)&ss_ac_int_ep_desc_comp,
-
-	(struct usb_descriptor_header *)&as_out_interface_alt_0_desc,
-	(struct usb_descriptor_header *)&as_out_interface_alt_1_desc,
-	(struct usb_descriptor_header *)&as_out_header_desc,
-
-	(struct usb_descriptor_header *)&as_out_type_i_desc,
-
-	(struct usb_descriptor_header *)&ss_as_out_ep_desc,
-	(struct usb_descriptor_header *)&ss_as_out_ep_desc_comp,
-	(struct usb_descriptor_header *)&as_iso_out_desc,
-	(struct usb_descriptor_header *)&ss_as_in_fback_desc,
-	(struct usb_descriptor_header *)&ss_as_in_fback_desc_comp,
-
-	(struct usb_descriptor_header *)&as_in_interface_alt_0_desc,
-	(struct usb_descriptor_header *)&as_in_interface_alt_1_desc,
-	(struct usb_descriptor_header *)&as_in_header_desc,
-
-	(struct usb_descriptor_header *)&as_in_type_i_desc,
-
-	(struct usb_descriptor_header *)&ss_as_in_ep_desc,
-	(struct usb_descriptor_header *)&ss_as_in_ep_desc_comp,
-	(struct usb_descriptor_header *)&as_iso_in_desc,
-	NULL,
-};
-
 /*
  * This function is an ALSA sound card following USB Audio Class Spec 1.0.
  */
@@ -602,11 +310,11 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
 	u8 control_selector = w_value >> 8;
 	int value = -EOPNOTSUPP;
 
-	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) {
@@ -653,11 +361,11 @@ in_rq_min(struct usb_function *fn, const struct usb_ctrlrequest *cr)
 	u8 control_selector = w_value >> 8;
 	int value = -EOPNOTSUPP;
 
-	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_VOLUME) {
@@ -700,11 +408,11 @@ in_rq_max(struct usb_function *fn, const struct usb_ctrlrequest *cr)
 	u8 control_selector = w_value >> 8;
 	int value = -EOPNOTSUPP;
 
-	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_VOLUME) {
@@ -747,11 +455,11 @@ in_rq_res(struct usb_function *fn, const struct usb_ctrlrequest *cr)
 	u8 control_selector = w_value >> 8;
 	int value = -EOPNOTSUPP;
 
-	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_VOLUME) {
@@ -799,11 +507,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) {
@@ -848,8 +556,8 @@ out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
 	u8 entity_id = (w_index >> 8) & 0xff;
 	u8 control_selector = w_value >> 8;
 
-	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)))) {
 		memcpy(&uac1->setup_cr, cr, sizeof(*cr));
 		req->context = audio;
 		req->complete = out_rq_cur_complete;
@@ -1221,6 +929,10 @@ static struct usb_string *attach_strings(struct usb_composite_dev *cdev,
 
 /*-------------------------------------------------------------------------*/
 
+/*
+ * Descriptor building functions
+ */
+
 static int set_ep_max_packet_size_bint(struct device *dev, const struct f_uac1_alt_opts *alt_opts,
 					struct usb_endpoint_descriptor *ep_desc,
 					enum usb_device_speed speed, bool is_playback)
@@ -1231,11 +943,59 @@ static int set_ep_max_packet_size_bint(struct device *dev, const struct f_uac1_a
 		alt_opts->ssize, alt_opts->sync, alt_opts->c.opts->fb_max);
 }
 
-static struct uac_feature_unit_descriptor *build_fu_desc(int chmask)
+struct path_params {
+	int dir;
+	int id;
+	struct f_uac1_opts *opts;
+	struct usb_string *strings;
+};
+
+/* 4.3.2.1 Input Terminal Descriptor */
+static void init_it_desc(struct uac_input_terminal_descriptor *it_desc,
+			 struct f_uac1_alt_opts *alt_opts,
+			 struct path_params *params)
+{
+	it_desc->bLength =		UAC_DT_INPUT_TERMINAL_SIZE;
+	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->bNrChannels =		num_channels(alt_opts->chmask);
+	it_desc->wChannelConfig =	cpu_to_le16(alt_opts->chmask);
+	it_desc->iTerminal =		add_string(params->strings, alt_opts->it_name);
+	it_desc->iChannelNames =	add_string(params->strings, alt_opts->it_ch_name);
+}
+
+/* 4.3.2.2 Output Terminal Descriptor */
+static void init_ot_desc(struct uac1_output_terminal_descriptor *ot_desc,
+			 struct f_uac1_alt_opts *alt_opts,
+			 struct path_params *params, int src_id)
+{
+	ot_desc->bLength =		UAC_DT_OUTPUT_TERMINAL_SIZE;
+	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->iTerminal =		add_string(params->strings, alt_opts->ot_name);
+}
+
+/* 4.3.2.5 Feature Unit Descriptor */
+static struct uac_feature_unit_descriptor *build_fu_desc(struct f_uac1_alt_opts *alt_opts,
+							 struct path_params *params,
+							 int src_id)
 {
 	struct uac_feature_unit_descriptor *fu_desc;
-	int channels = num_channels(chmask);
+	int channels = num_channels(alt_opts->chmask);
 	int fu_desc_size = UAC_DT_FEATURE_UNIT_SIZE(channels);
+	u32 control = 0;
+	u8 *i_feature;
 
 	fu_desc = kzalloc(fu_desc_size, GFP_KERNEL);
 	if (!fu_desc)
@@ -1243,29 +1003,46 @@ static struct uac_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;
 	fu_desc->bControlSize  = 2;
 
-	/* bUnitID, bSourceID and bmaControls will be defined later */
+	if (params->dir == HOST_TO_DEVICE) {
+		if (params->opts->c_mute_present)
+			control |= UAC_FU_MUTE;
+		if (params->opts->c_volume_present)
+			control |= UAC_FU_VOLUME;
+	}
+
+	if (params->dir == DEVICE_TO_HOST) {
+		if (params->opts->p_mute_present)
+			control |= UAC_FU_MUTE;
+		if (params->opts->p_volume_present)
+			control |= UAC_FU_VOLUME;
+	}
+
+	/* Only master volume/mute is supported. Per-channel controls are all zero. */
+	fu_desc->bmaControls[0] = cpu_to_le16(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;
 }
 
-/* B.3.2  Class-Specific AC Interface Descriptor */
+/* 4.3.2  Class-Specific AC Interface Descriptor */
 static struct
-uac1_ac_header_descriptor *build_ac_header_desc(struct f_uac1_opts *opts)
+uac1_ac_header_descriptor *build_ac_header_desc(struct f_uac1 *uac1, struct f_uac1_opts *opts)
 {
 	struct uac1_ac_header_descriptor *ac_desc;
 	int ac_header_desc_size;
-	int num_ifaces = 0;
-
-	if (EPOUT_EN(opts))
-		num_ifaces++;
-	if (EPIN_EN(opts))
-		num_ifaces++;
+	int capture = epout_en_any(opts);
+	int playback = epin_en_any(opts);
+	int ba_iface_id = 0;
 
-	ac_header_desc_size = UAC_DT_AC_HEADER_SIZE(num_ifaces);
+	ac_header_desc_size = UAC_DT_AC_HEADER_SIZE(capture + playback);
 
 	ac_desc = kzalloc(ac_header_desc_size, GFP_KERNEL);
 	if (!ac_desc)
@@ -1275,195 +1052,530 @@ uac1_ac_header_descriptor *build_ac_header_desc(struct f_uac1_opts *opts)
 	ac_desc->bDescriptorType = USB_DT_CS_INTERFACE;
 	ac_desc->bDescriptorSubtype = UAC_HEADER;
 	ac_desc->bcdADC = cpu_to_le16(0x0100);
-	ac_desc->bInCollection = num_ifaces;
+	ac_desc->bInCollection = capture + playback;
+
+	if (capture)
+		ac_desc->baInterfaceNr[ba_iface_id++] = uac1->as_out_intf;
+
+	if (playback)
+		ac_desc->baInterfaceNr[ba_iface_id++] = uac1->as_in_intf;
 
-	/* wTotalLength and baInterfaceNr will be defined later */
+	/* wTotalLength will be defined later */
 
 	return ac_desc;
 }
 
+/* 4.5.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 =			USB_DT_INTERFACE_SIZE;
+	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 =	0;
+	iface_desc->iInterface =		add_string(strings, name);
+}
+
+/* 4.5.2  Class-Specific AS Interface Descriptor */
+static void init_as_header_desc(struct uac1_as_header_descriptor *as_header_desc, int terminalId)
+{
+	as_header_desc->bLength =		UAC_DT_AS_HEADER_SIZE;
+	as_header_desc->bDescriptorType =	USB_DT_CS_INTERFACE;
+	as_header_desc->bDescriptorSubtype =	UAC_AS_GENERAL;
+	as_header_desc->bTerminalLink =		terminalId;
+	as_header_desc->bDelay =		1;
+	as_header_desc->wFormatTag =		cpu_to_le16(UAC_FORMAT_TYPE_I_PCM);
+}
+
+/* 4.5.3 Class-Specific AS Format Type Descriptor */
+static void init_uac_format_type_i_discrete_desc(struct f_uac1_alt_opts *alt_opts)
+{
+	int idx, i;
+
+	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.bNrChannels = num_channels(alt_opts->chmask);
+	alt_opts->fmt_desc.bSubframeSize = alt_opts->ssize;
+	alt_opts->fmt_desc.bBitResolution = alt_opts->ssize * 8;
+
+	/* Set sample rates */
+	for (i = 0, idx = 0; i < UAC_MAX_RATES; i++) {
+		if (alt_opts->srates[i] == 0)
+			break;
+		memcpy(alt_opts->fmt_desc.tSamFreq[idx++],
+			&alt_opts->srates[i], 3);
+	}
+	alt_opts->fmt_desc.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(idx);
+	alt_opts->fmt_desc.bSamFreqType = idx;
+}
+
+static int init_isoc_ep_descriptor(struct device *dev, struct usb_endpoint_descriptor *ep_desc,
+				   struct f_uac1_alt_opts *alt_opts, int dir,
+				   enum usb_device_speed speed, u8 addr)
+{
+	ep_desc->bLength =		USB_DT_ENDPOINT_AUDIO_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. */
+	ep_desc->bRefresh =		0;
+	ep_desc->bSynchAddress =	EPOUT_FBACK_IN_EN(alt_opts)
+						? fs_as_in_fback_desc.bEndpointAddress : 0;
+
+	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_uac1_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->as_header_desc, 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_uac1_path_descriptors *
+build_path_descriptors(struct path_params *params, struct f_uac1_alt_opts *alt_opts)
+{
+	struct f_uac1_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);
+		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_uac1_path_descriptors *path_descs)
+{
+	kfree(path_descs->fu_desc);
+	kfree(path_descs);
+}
+
+static struct f_uac1_path_descriptors *find_path_descriptors(struct list_head *list,
+							     struct f_uac1_alt_opts *alt_opts,
+							     int dir)
+{
+	struct f_uac1_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_uac1_alt_opts *alt_opts)
+{
+	int len = 0;
+	struct f_uac1_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)
 
-static void setup_headers(struct f_uac1_opts *opts,
-			  struct usb_descriptor_header **headers,
-			  enum usb_device_speed speed);
+static int setup_headers(struct usb_descriptor_header **desc_list,
+			 struct f_uac1 *uac1,
+			 struct f_uac1_opts *opts,
+			 struct list_head *path_descs,
+			 enum usb_device_speed speed);
 
-static void setup_descriptor(struct f_uac1_opts *opts)
+static int setup_descriptor(struct device *dev, struct f_uac1 *uac1, struct f_uac1_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 (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;
-	}
-	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;
+	int len;
+	struct list_head path_descs = LIST_HEAD_INIT(path_descs);
+	int fs_num, hs_num, ss_ssp_num;
+	struct f_uac1_alt_opts *alt_opts;
+	struct list_head *path_desc, *tmp;
+	struct path_params params;
+
+	params.id = 1; /* ID's start with 1 */
+	params.opts = opts;
+	params.strings = strings;
+
+	ac_header_desc = build_ac_header_desc(uac1, opts);
+	if (!ac_header_desc)
+		return -ENOMEM;
+
+	len = ac_header_desc->bLength;
+
+	if (uac1->g_audio.out_ep) {
+		params.dir = HOST_TO_DEVICE;
+		init_as_interface_desc(&opts->c_alt_0_opts.intf_desc, uac1->as_out_intf, 0, 0,
+				       opts->c_alt_0_opts.name, strings);
+
+		/* 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, uac1->as_out_intf,
+					      uac1->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, uac1->as_out_intf,
+						      uac1->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;
+			}
+		}
 	}
 
-	as_out_header_desc.bTerminalLink = usb_out_it_desc.bTerminalID;
-	as_in_header_desc.bTerminalLink = usb_in_ot_desc.bTerminalID;
+	if (uac1->g_audio.in_ep) {
+		params.dir = DEVICE_TO_HOST;
+		init_as_interface_desc(&opts->p_alt_0_opts.intf_desc, uac1->as_in_intf, 0, 0,
+				       opts->p_alt_0_opts.name, strings);
 
-	io_in_it_desc.wTerminalType = cpu_to_le16(opts->c_terminal_type);
-	io_out_ot_desc.wTerminalType = cpu_to_le16(opts->p_terminal_type);
-	ac_header_desc->wTotalLength = cpu_to_le16(ac_header_desc->bLength);
+		/* Audio path descriptors (input terminal -> <feature unit> -> output terminal) */
+		len += add_path_descriptors(&path_descs, &params, &opts->p_alt_1_opts);
 
-	if (EPIN_EN(opts)) {
-		u16 len = le16_to_cpu(ac_header_desc->wTotalLength);
+		status = init_alt_descriptors(dev, &opts->p_alt_1_opts, uac1->as_in_intf,
+					      uac1->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;
+		}
 
-		len += sizeof(usb_in_ot_desc);
-		len += sizeof(io_in_it_desc);
-		if (FUIN_EN(opts))
-			len += in_feature_unit_desc->bLength;
-		ac_header_desc->wTotalLength = cpu_to_le16(len);
+		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, uac1->as_in_intf,
+						      uac1->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_header_desc->wTotalLength);
 
-		len += sizeof(usb_out_it_desc);
-		len += sizeof(io_out_ot_desc);
-		if (FUOUT_EN(opts))
-			len += out_feature_unit_desc->bLength;
-		ac_header_desc->wTotalLength = cpu_to_le16(len);
+	ac_header_desc->wTotalLength = cpu_to_le16(len);
+
+	/* Count how many descriptors we have and then allocate and populate */
+	fs_num = setup_headers(NULL, uac1, opts, &path_descs, USB_SPEED_FULL);
+	hs_num = setup_headers(NULL, uac1, opts, &path_descs, USB_SPEED_HIGH);
+	ss_ssp_num = setup_headers(NULL, uac1, opts, &path_descs, USB_SPEED_SUPER);
+
+	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;
 
-	setup_headers(opts, f_audio_fs_desc, USB_SPEED_FULL);
-	setup_headers(opts, f_audio_hs_desc, USB_SPEED_HIGH);
-	setup_headers(opts, f_audio_ss_desc, USB_SPEED_SUPER);
+	(void) setup_headers(fs_desc_list, uac1, opts, &path_descs, USB_SPEED_FULL);
+	(void) setup_headers(hs_desc_list, uac1, opts, &path_descs, USB_SPEED_HIGH);
+	(void) setup_headers(ss_ssp_desc_list, uac1, opts, &path_descs, USB_SPEED_SUPER);
+
+	/* copy descriptors, and track endpoint copies */
+	status = usb_assign_descriptors(&uac1->g_audio.func, fs_desc_list, hs_desc_list,
+					ss_ssp_desc_list, ss_ssp_desc_list);
+
+	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_uac1_path_descriptors, list));
+	}
+	kfree(ac_header_desc);
+	ac_header_desc = NULL;
+
+	return status;
 }
 
-static void setup_headers(struct f_uac1_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_uac1_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 usb_descriptor_header **desc_list,
+			 struct f_uac1 *uac1,
+			 struct f_uac1_opts *opts,
+			 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_as_out_ep_desc;
-		epin_desc = &fs_as_in_ep_desc;
 		epin_fback_desc = &fs_as_in_fback_desc;
 		ep_int_desc = &fs_ac_int_ep_desc;
 		break;
 	case USB_SPEED_HIGH:
-		epout_desc = &hs_as_out_ep_desc;
-		epin_desc = &hs_as_in_ep_desc;
 		epin_fback_desc = &hs_as_in_fback_desc;
 		ep_int_desc = &hs_ac_int_ep_desc;
 		break;
 	default:
-		epout_desc = &ss_as_out_ep_desc;
-		epin_desc = &ss_as_in_ep_desc;
-		epout_desc_comp = &ss_as_out_ep_desc_comp;
-		epin_desc_comp = &ss_as_in_ep_desc_comp;
 		epin_fback_desc = &ss_as_in_fback_desc;
 		epin_fback_desc_comp = &ss_as_in_fback_desc_comp;
 		ep_int_desc = &ss_ac_int_ep_desc;
 		ep_int_desc_comp = &ss_ac_int_ep_desc_comp;
 	}
 
-	i = 0;
-	headers[i++] = USBDHDR(&ac_interface_desc);
-	headers[i++] = USBDHDR(ac_header_desc);
+	add_descriptor(i++, desc_list, USBDHDR(&ac_interface_desc));
+	add_descriptor(i++, desc_list, USBDHDR(ac_header_desc));
 
-	if (EPOUT_EN(opts)) {
-		headers[i++] = USBDHDR(&usb_out_it_desc);
-		headers[i++] = USBDHDR(&io_out_ot_desc);
-		if (FUOUT_EN(opts))
-			headers[i++] = USBDHDR(out_feature_unit_desc);
-	}
-
-	if (EPIN_EN(opts)) {
-		headers[i++] = USBDHDR(&io_in_it_desc);
-		headers[i++] = USBDHDR(&usb_in_ot_desc);
-		if (FUIN_EN(opts))
-			headers[i++] = USBDHDR(in_feature_unit_desc);
+	list_for_each(pos, path_descs) {
+		struct f_uac1_path_descriptors *path_desc =
+			container_of(pos, struct f_uac1_path_descriptors, list);
+		add_descriptor(i++, desc_list, USBDHDR(&path_desc->it_desc));
+		add_descriptor(i++, desc_list, USBDHDR(&path_desc->ot_desc));
+		if (path_desc->fu_desc)
+			add_descriptor(i++, desc_list, USBDHDR(path_desc->fu_desc));
 	}
 
+	// If any FU exists, add the interrupt endpoint descriptor
 	if (FUOUT_EN(opts) || FUIN_EN(opts)) {
-		headers[i++] = USBDHDR(ep_int_desc);
+		add_descriptor(i++, desc_list, USBDHDR(ep_int_desc));
 		if (ep_int_desc_comp)
-			headers[i++] = USBDHDR(ep_int_desc_comp);
+			add_descriptor(i++, desc_list, USBDHDR(ep_int_desc_comp));
 	}
 
-	if (EPOUT_EN(opts)) {
-		headers[i++] = USBDHDR(&as_out_interface_alt_0_desc);
-		headers[i++] = USBDHDR(&as_out_interface_alt_1_desc);
-		headers[i++] = USBDHDR(&as_out_header_desc);
-		headers[i++] = USBDHDR(&as_out_type_i_desc);
-		headers[i++] = USBDHDR(epout_desc);
-		if (epout_desc_comp)
-			headers[i++] = USBDHDR(epout_desc_comp);
+	// If any capture interface is active
+	if (epout_en_any(opts)) {
+		struct f_uac1_alt_opts *alt_opts;
+
+		add_descriptor(i++, desc_list, USBDHDR(&opts->c_alt_0_opts.intf_desc));
 
-		headers[i++] = USBDHDR(&as_iso_out_desc);
+		if (EP_EN(&opts->c_alt_1_opts)) {
+			i = add_alt_descriptors(i, desc_list, &opts->c_alt_1_opts, speed);
 
-		if (EPOUT_FBACK_IN_EN(opts)) {
-			headers[i++] = USBDHDR(epin_fback_desc);
-			if (epin_fback_desc_comp)
-				headers[i++] = USBDHDR(epin_fback_desc_comp);
+			add_descriptor(i++, desc_list, USBDHDR(&as_iso_out_desc));
+			if (EPOUT_FBACK_IN_EN(&opts->c_alt_1_opts)) {
+				add_descriptor(i++, desc_list, USBDHDR(epin_fback_desc));
+				if (epin_fback_desc_comp)
+					add_descriptor(i++, desc_list,
+						       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, desc_list, alt_opts, speed);
+
+				add_descriptor(i++, desc_list, USBDHDR(&as_iso_out_desc));
+				if (EPOUT_FBACK_IN_EN(alt_opts)) {
+					add_descriptor(i++, desc_list, USBDHDR(epin_fback_desc));
+					if (epin_fback_desc_comp)
+						add_descriptor(i++, desc_list,
+							       USBDHDR(epin_fback_desc_comp));
+				}
+			}
 		}
 	}
-	if (EPIN_EN(opts)) {
-		headers[i++] = USBDHDR(&as_in_interface_alt_0_desc);
-		headers[i++] = USBDHDR(&as_in_interface_alt_1_desc);
-		headers[i++] = USBDHDR(&as_in_header_desc);
-		headers[i++] = USBDHDR(&as_in_type_i_desc);
-		headers[i++] = USBDHDR(epin_desc);
-		if (epin_desc_comp)
-			headers[i++] = USBDHDR(epin_desc_comp);
 
-		headers[i++] = USBDHDR(&as_iso_in_desc);
+	// If any playback interface is active
+	if (epin_en_any(opts)) {
+		struct f_uac1_alt_opts *alt_opts;
+
+		add_descriptor(i++, desc_list, USBDHDR(&opts->p_alt_0_opts.intf_desc));
+
+		if (EP_EN(&opts->p_alt_1_opts)) {
+			i = add_alt_descriptors(i, desc_list, &opts->p_alt_1_opts, speed);
+
+			add_descriptor(i++, desc_list, 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, desc_list, alt_opts, speed);
+
+				add_descriptor(i++, desc_list, USBDHDR(&as_iso_in_desc));
+			}
+		}
 	}
-	headers[i] = NULL;
+
+	add_descriptor(i++, desc_list, NULL);
+
+	return i;
 }
 
 static int f_audio_validate_opts(struct g_audio *audio, struct device *dev)
 {
 	struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
+	struct f_uac1_alt_opts *alt_opts;
 
-	if (!opts->p_chmask && !opts->c_chmask) {
+	if (!epin_en_any(opts) && !epout_en_any(opts)) {
 		dev_err(dev, "Error: no playback and capture channels\n");
 		return -EINVAL;
-	} else if (opts->p_chmask & ~UAC1_CHANNEL_MASK) {
-		dev_err(dev, "Error: unsupported playback channels mask\n");
-		return -EINVAL;
-	} else if (opts->c_chmask & ~UAC1_CHANNEL_MASK) {
-		dev_err(dev, "Error: unsupported capture channels mask\n");
-		return -EINVAL;
-	} else if ((opts->p_ssize < 1) || (opts->p_ssize > 4)) {
-		dev_err(dev, "Error: incorrect playback sample size\n");
-		return -EINVAL;
-	} else if ((opts->c_ssize < 1) || (opts->c_ssize > 4)) {
-		dev_err(dev, "Error: incorrect capture sample size\n");
-		return -EINVAL;
-	} else if (!opts->p_srates[0]) {
+	}
+
+	list_for_each_entry(alt_opts, &opts->p_alt_opts, list) {
+		if (alt_opts->chmask & ~UAC1_CHANNEL_MASK) {
+			dev_err(dev, "Error: unsupported playback channels mask for alt %d\n",
+				alt_opts->c.alt_num);
+			return -EINVAL;
+		} else if ((alt_opts->ssize < 1) || (alt_opts->ssize > 4)) {
+			dev_err(dev, "Error: incorrect playback sample size for alt %d\n",
+				alt_opts->c.alt_num);
+			return -EINVAL;
+		} else if ((alt_opts->hs_bint < 0) || (alt_opts->hs_bint > 4)) {
+			dev_err(dev, "Error: incorrect playback HS/SS bInterval (1-4: fixed, 0: auto) for alt %d\n",
+				alt_opts->c.alt_num);
+
+			return -EINVAL;
+		}
+	}
+
+	list_for_each_entry(alt_opts, &opts->c_alt_opts, list) {
+		if (alt_opts->chmask & ~UAC1_CHANNEL_MASK) {
+			dev_err(dev, "Error: unsupported capture channels mask for alt %d\n",
+				alt_opts->c.alt_num);
+			return -EINVAL;
+		} else if ((alt_opts->ssize < 1) || (alt_opts->ssize > 4)) {
+			dev_err(dev, "Error: incorrect capture sample size for alt %d\n",
+				alt_opts->c.alt_num);
+			return -EINVAL;
+		} else if ((alt_opts->hs_bint < 0) || (alt_opts->hs_bint > 4)) {
+			dev_err(dev, "Error: incorrect capture HS/SS bInterval (1-4: fixed, 0: auto) for alt %d\n",
+				alt_opts->c.alt_num);
+
+			return -EINVAL;
+		}
+	}
+
+	if (!opts->p_srates[0]) {
 		dev_err(dev, "Error: incorrect playback sampling rate\n");
 		return -EINVAL;
 	} else if (!opts->c_srates[0]) {
@@ -1493,15 +1605,6 @@ static int f_audio_validate_opts(struct g_audio *audio, struct device *dev)
 		return -EINVAL;
 	}
 
-	if ((opts->p_hs_bint < 0) || (opts->p_hs_bint > 4)) {
-		dev_err(dev, "Error: incorrect playback HS/SS bInterval (1-4: fixed, 0: auto)\n");
-		return -EINVAL;
-	}
-	if ((opts->c_hs_bint < 0) || (opts->c_hs_bint > 4)) {
-		dev_err(dev, "Error: incorrect capture HS/SS bInterval (1-4: fixed, 0: auto)\n");
-		return -EINVAL;
-	}
-
 	return 0;
 }
 
@@ -1551,6 +1654,26 @@ static void init_alt_opts(struct f_uac1_alt_opts *alt_opts, struct f_uac1_opts *
 	alt_opts->terminal_type = (playback) ? opts->p_terminal_type : opts->c_terminal_type;
 }
 
+static u16 get_max_packet_size(struct f_uac1_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;
+}
+
 /* audio function driver setup/binding */
 static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
 {
@@ -1562,9 +1685,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
 	struct f_uac1_opts		*audio_opts;
 	struct usb_ep			*ep = NULL;
 	struct usb_string		*us;
-	int				ba_iface_id;
 	int				status;
-	int				idx, i;
+	struct list_head		strings = LIST_HEAD_INIT(strings);
 
 	audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst);
 
@@ -1588,214 +1710,41 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
 	 * be used from their alt mode opts.
 	 */
 
-	us = attach_strings(cdev, audio_opts);
-	if (IS_ERR(us))
-		return PTR_ERR(us);
-
-	ac_header_desc = build_ac_header_desc(audio_opts);
-	if (!ac_header_desc)
-		return -ENOMEM;
-
-	if (FUOUT_EN(audio_opts)) {
-		out_feature_unit_desc = build_fu_desc(audio_opts->c_chmask);
-		if (!out_feature_unit_desc) {
-			status = -ENOMEM;
-			goto fail;
-		}
-	}
-	if (FUIN_EN(audio_opts)) {
-		in_feature_unit_desc = build_fu_desc(audio_opts->p_chmask);
-		if (!in_feature_unit_desc) {
-			status = -ENOMEM;
-			goto err_free_fu;
-		}
-	}
-
-	ac_interface_desc.iInterface = add_string(us, audio_opts->function_name);
-	usb_out_it_desc.iTerminal = add_string(us, audio_opts->c_alt_1_opts.it_name);
-	usb_out_it_desc.iChannelNames = add_string(us, audio_opts->c_alt_1_opts.it_ch_name);
-	io_out_ot_desc.iTerminal = add_string(us, audio_opts->c_alt_1_opts.ot_name);
-	as_out_interface_alt_0_desc.iInterface = add_string(us, audio_opts->c_alt_0_opts.name);
-	as_out_interface_alt_1_desc.iInterface = add_string(us, audio_opts->c_alt_1_opts.name);
-	io_in_it_desc.iTerminal = add_string(us, audio_opts->p_alt_1_opts.it_name);
-	io_in_it_desc.iChannelNames = add_string(us, audio_opts->p_alt_1_opts.it_ch_name);
-	usb_in_ot_desc.iTerminal = add_string(us, audio_opts->p_alt_1_opts.ot_name);
-	as_in_interface_alt_0_desc.iInterface = add_string(us, audio_opts->p_alt_0_opts.name);
-	as_in_interface_alt_1_desc.iInterface = add_string(us, audio_opts->p_alt_1_opts.name);
-
-	if (FUOUT_EN(audio_opts)) {
-		u8 *i_feature;
-
-		i_feature = (u8 *)out_feature_unit_desc +
-					out_feature_unit_desc->bLength - 1;
-		*i_feature = add_string(us, audio_opts->c_alt_1_opts.fu_vol_name);
-	}
-	if (FUIN_EN(audio_opts)) {
-		u8 *i_feature;
-
-		i_feature = (u8 *)in_feature_unit_desc +
-					in_feature_unit_desc->bLength - 1;
-		*i_feature = add_string(us, audio_opts->p_alt_1_opts.fu_vol_name);
-	}
-
 	us = attach_strings(cdev, audio_opts);
 	if (IS_ERR(us)) {
 		status = PTR_ERR(us);
 		goto fail;
 	}
 
-	/* Set channel numbers */
-	usb_out_it_desc.bNrChannels = num_channels(audio_opts->c_chmask);
-	usb_out_it_desc.wChannelConfig = cpu_to_le16(audio_opts->c_chmask);
-	as_out_type_i_desc.bNrChannels = num_channels(audio_opts->c_chmask);
-	as_out_type_i_desc.bSubframeSize = audio_opts->c_ssize;
-	as_out_type_i_desc.bBitResolution = audio_opts->c_ssize * 8;
-	io_in_it_desc.bNrChannels = num_channels(audio_opts->p_chmask);
-	io_in_it_desc.wChannelConfig = cpu_to_le16(audio_opts->p_chmask);
-	as_in_type_i_desc.bNrChannels = num_channels(audio_opts->p_chmask);
-	as_in_type_i_desc.bSubframeSize = audio_opts->p_ssize;
-	as_in_type_i_desc.bBitResolution = audio_opts->p_ssize * 8;
-
-	if (FUOUT_EN(audio_opts)) {
-		__le16 *bma = (__le16 *)&out_feature_unit_desc->bmaControls[0];
-		u32 control = 0;
-
-		if (audio_opts->c_mute_present)
-			control |= UAC_FU_MUTE;
-		if (audio_opts->c_volume_present)
-			control |= UAC_FU_VOLUME;
-		*bma = cpu_to_le16(control);
-	}
-	if (FUIN_EN(audio_opts)) {
-		__le16 *bma = (__le16 *)&in_feature_unit_desc->bmaControls[0];
-		u32 control = 0;
-
-		if (audio_opts->p_mute_present)
-			control |= UAC_FU_MUTE;
-		if (audio_opts->p_volume_present)
-			control |= UAC_FU_VOLUME;
-		*bma = cpu_to_le16(control);
-	}
-
-	/* Set sample rates */
-	for (i = 0, idx = 0; i < UAC_MAX_RATES; i++) {
-		if (audio_opts->c_srates[i] == 0)
-			break;
-		memcpy(as_out_type_i_desc.tSamFreq[idx++],
-				&audio_opts->c_srates[i], 3);
-	}
-	as_out_type_i_desc.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(idx);
-	as_out_type_i_desc.bSamFreqType = idx;
+	ac_interface_desc.iInterface = add_string(us, audio_opts->function_name);
 
-	for (i = 0, idx = 0; i < UAC_MAX_RATES; i++) {
-		if (audio_opts->p_srates[i] == 0)
-			break;
-		memcpy(as_in_type_i_desc.tSamFreq[idx++],
-				&audio_opts->p_srates[i], 3);
-	}
-	as_in_type_i_desc.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(idx);
-	as_in_type_i_desc.bSamFreqType = idx;
 	uac1->p_srate = audio_opts->p_srates[0];
 	uac1->c_srate = audio_opts->c_srates[0];
 
-	/* allocate instance-specific interface IDs, and patch descriptors */
+	/* allocate instance-specific interface IDs */
 	status = usb_interface_id(c, f);
 	if (status < 0)
-		goto err_free_fu;
+		goto fail;
 	ac_interface_desc.bInterfaceNumber = status;
-	ac_interface_desc.bNumEndpoints = 1;
 	uac1->ac_intf = status;
 	uac1->ac_alt = 0;
 
-	ba_iface_id = 0;
-
-	if (EPOUT_EN(audio_opts)) {
+	if (epout_en_any(audio_opts)) {
 		status = usb_interface_id(c, f);
 		if (status < 0)
-			goto err_free_fu;
-		as_out_interface_alt_0_desc.bInterfaceNumber = status;
-		as_out_interface_alt_1_desc.bInterfaceNumber = status;
-		ac_header_desc->baInterfaceNr[ba_iface_id++] = status;
+			goto fail;
 		uac1->as_out_intf = status;
 		uac1->as_out_alt = 0;
-
-		if (EPOUT_FBACK_IN_EN(audio_opts)) {
-			fs_as_out_ep_desc.bmAttributes =
-			  USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC;
-			hs_as_out_ep_desc.bmAttributes =
-			  USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC;
-			ss_as_out_ep_desc.bmAttributes =
-			  USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC;
-			ac_interface_desc.bNumEndpoints++;
-		} else {
-			fs_as_out_ep_desc.bmAttributes =
-			  USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE;
-			hs_as_out_ep_desc.bmAttributes =
-			  USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE;
-			ss_as_out_ep_desc.bmAttributes =
-			  USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE;
-		}
 	}
 
-	if (EPIN_EN(audio_opts)) {
+	if (epin_en_any(audio_opts)) {
 		status = usb_interface_id(c, f);
 		if (status < 0)
-			goto err_free_fu;
-		as_in_interface_alt_0_desc.bInterfaceNumber = status;
-		as_in_interface_alt_1_desc.bInterfaceNumber = status;
-		ac_header_desc->baInterfaceNr[ba_iface_id++] = status;
+			goto fail;
 		uac1->as_in_intf = status;
 		uac1->as_in_alt = 0;
 	}
 
-	hs_as_in_ep_desc.bInterval = audio_opts->p_hs_bint;
-	ss_as_in_ep_desc.bInterval = audio_opts->p_hs_bint;
-	hs_as_out_ep_desc.bInterval = audio_opts->c_hs_bint;
-	ss_as_out_ep_desc.bInterval = audio_opts->c_hs_bint;
-
-	/* Calculate wMaxPacketSize according to audio bandwidth */
-	status = set_ep_max_packet_size_bint(dev, &audio_opts->p_alt_1_opts, &fs_as_in_ep_desc,
-					     USB_SPEED_FULL, true);
-	if (status < 0) {
-		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
-		goto err_free_fu;
-	}
-
-	status = set_ep_max_packet_size_bint(dev, &audio_opts->c_alt_1_opts, &fs_as_out_ep_desc,
-					     USB_SPEED_FULL, false);
-	if (status < 0) {
-		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
-		goto err_free_fu;
-	}
-
-	status = set_ep_max_packet_size_bint(dev, &audio_opts->p_alt_1_opts, &hs_as_in_ep_desc,
-					     USB_SPEED_HIGH, true);
-	if (status < 0) {
-		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
-		goto err_free_fu;
-	}
-
-	status = set_ep_max_packet_size_bint(dev, &audio_opts->c_alt_1_opts, &hs_as_out_ep_desc,
-					     USB_SPEED_HIGH, false);
-	if (status < 0) {
-		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
-		goto err_free_fu;
-	}
-
-	status = set_ep_max_packet_size_bint(dev, &audio_opts->p_alt_1_opts, &ss_as_in_ep_desc,
-					     USB_SPEED_SUPER, true);
-	if (status < 0) {
-		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
-		goto err_free_fu;
-	}
-
-	status = set_ep_max_packet_size_bint(dev, &audio_opts->c_alt_1_opts, &hs_as_out_ep_desc,
-					     USB_SPEED_SUPER, false);
-	if (status < 0) {
-		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
-		goto err_free_fu;
-	}
-
 	audio->gadget = gadget;
 
 	status = -ENODEV;
@@ -1805,36 +1754,49 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
 	/* allocate AC interrupt endpoint */
 	if (FUOUT_EN(audio_opts) || FUIN_EN(audio_opts)) {
 		ep = usb_ep_autoconfig(cdev->gadget, &fs_ac_int_ep_desc);
-		if (!ep)
-			goto err_free_fu;
-
+		if (!ep) {
+			status = -EINVAL;
+			dev_err(dev, "Failed to allocate interrupt endpoint\n");
+			goto fail;
+		}
 		hs_ac_int_ep_desc.bEndpointAddress = fs_ac_int_ep_desc.bEndpointAddress;
 		ss_ac_int_ep_desc.bEndpointAddress = fs_ac_int_ep_desc.bEndpointAddress;
 
 		uac1->int_ep = ep;
-		uac1->int_ep->desc = &fs_ac_int_ep_desc;
 
 		ac_interface_desc.bNumEndpoints = 1;
 	}
 
-	/* allocate instance-specific endpoints */
-	if (EPOUT_EN(audio_opts)) {
-		ep = usb_ep_autoconfig(cdev->gadget, &fs_as_out_ep_desc);
-		if (!ep)
-			goto err_free_fu;
-
-		hs_as_out_ep_desc.bEndpointAddress = fs_as_out_ep_desc.bEndpointAddress;
-		ss_as_out_ep_desc.bEndpointAddress = fs_as_out_ep_desc.bEndpointAddress;
-		ss_as_out_ep_desc_comp.wBytesPerInterval = ss_as_out_ep_desc.wMaxPacketSize;
+	/* 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(audio_opts)) {
+		status = init_isoc_ep_descriptor(dev, &audio_opts->c_alt_1_opts.fs_iso_ep_desc,
+						 &audio_opts->c_alt_1_opts, HOST_TO_DEVICE,
+						 USB_SPEED_FULL, USB_DIR_OUT);
+		if (status) {
+			dev_err(dev, "Failed to init FS isoc ep descriptor for capture (%d)\n",
+				status);
+			goto fail;
+		}
 
+		ep = usb_ep_autoconfig(cdev->gadget, &audio_opts->c_alt_1_opts.fs_iso_ep_desc);
+		if (!ep) {
+			status = -EINVAL;
+			dev_err(dev, "Failed to allocate isoc endpoint for capture\n");
+			goto fail;
+		}
 		audio->out_ep = ep;
-		audio->out_ep->desc = &fs_as_out_ep_desc;
 
-		if (EPOUT_FBACK_IN_EN(audio_opts)) {
+		if (epout_fback_in_en_any(audio_opts)) {
 			ep = usb_ep_autoconfig(cdev->gadget, &fs_as_in_fback_desc);
-			if (!ep)
-				goto err_free_fu;
-
+			if (!ep) {
+				status = -EINVAL;
+				dev_err(dev, "Failed to allocate feedback endpoint for capture\n");
+				goto fail;
+			}
 			hs_as_in_fback_desc.bEndpointAddress = fs_as_in_fback_desc.bEndpointAddress;
 			ss_as_in_fback_desc.bEndpointAddress = fs_as_in_fback_desc.bEndpointAddress;
 
@@ -1842,45 +1804,44 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
 		}
 	}
 
-	if (EPIN_EN(audio_opts)) {
-		ep = usb_ep_autoconfig(cdev->gadget, &fs_as_in_ep_desc);
-		if (!ep)
-			goto err_free_fu;
-
-		hs_as_in_ep_desc.bEndpointAddress = fs_as_in_ep_desc.bEndpointAddress;
-		ss_as_in_ep_desc.bEndpointAddress = fs_as_in_ep_desc.bEndpointAddress;
-		ss_as_in_ep_desc_comp.wBytesPerInterval = ss_as_in_ep_desc.wMaxPacketSize;
+	if (epin_en_any(audio_opts)) {
+		status = init_isoc_ep_descriptor(dev, &audio_opts->p_alt_1_opts.fs_iso_ep_desc,
+						 &audio_opts->p_alt_1_opts, HOST_TO_DEVICE,
+						 USB_SPEED_FULL, USB_DIR_IN);
+		if (status) {
+			dev_err(dev, "Failed to init FS isoc ep descriptor for playback (%d)\n",
+				status);
+			goto fail;
+		}
 
+		ep = usb_ep_autoconfig(cdev->gadget, &audio_opts->p_alt_1_opts.fs_iso_ep_desc);
+		if (!ep) {
+			status = -EINVAL;
+			dev_err(dev, "Failed to allocate isoc endpoint for playback\n");
+			goto fail;
+		}
 		audio->in_ep = ep;
-		audio->in_ep->desc = &fs_as_in_ep_desc;
 	}
 
-	setup_descriptor(audio_opts);
+	status = setup_descriptor(dev, uac1, audio_opts, us);
 
-	/* copy descriptors, and track endpoint copies */
-	status = usb_assign_descriptors(f, f_audio_fs_desc, f_audio_hs_desc, f_audio_ss_desc,
-					f_audio_ss_desc);
 	if (status)
-		goto err_free_fu;
-
-	audio->in_ep_maxpsize = max_t(u16,
-				le16_to_cpu(fs_as_in_ep_desc.wMaxPacketSize),
-				le16_to_cpu(hs_as_in_ep_desc.wMaxPacketSize));
-	audio->out_ep_maxpsize = max_t(u16,
-				le16_to_cpu(fs_as_out_ep_desc.wMaxPacketSize),
-				le16_to_cpu(hs_as_out_ep_desc.wMaxPacketSize));
+		goto fail;
 
-	audio->in_ep_maxpsize = max_t(u16, audio->in_ep_maxpsize,
-				le16_to_cpu(ss_as_in_ep_desc.wMaxPacketSize));
-	audio->out_ep_maxpsize = max_t(u16, audio->out_ep_maxpsize,
-				le16_to_cpu(ss_as_out_ep_desc.wMaxPacketSize));
+	// Set max packet size for all alt modes. These are used to allocate the buffers in u_audio.
+	audio->out_ep_maxpsize = get_max_packet_size(&audio_opts->c_alt_1_opts,
+						     &audio_opts->c_alt_opts);
+	audio->in_ep_maxpsize = get_max_packet_size(&audio_opts->p_alt_1_opts,
+						    &audio_opts->p_alt_opts);
 
+	// TODO: This may need some change with the audio params for the current alt mode
 	audio->params.c_chmask = audio_opts->c_chmask;
 	memcpy(audio->params.c_srates, audio_opts->c_srates,
 			sizeof(audio->params.c_srates));
 	audio->params.c_ssize = audio_opts->c_ssize;
+
 	if (FUIN_EN(audio_opts)) {
-		audio->params.p_fu.id = USB_IN_FU_ID;
+		audio->params.p_fu.id = USB_IN_FU_ID(audio_opts);
 		audio->params.p_fu.mute_present = audio_opts->p_mute_present;
 		audio->params.p_fu.volume_present =
 				audio_opts->p_volume_present;
@@ -1888,12 +1849,15 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
 		audio->params.p_fu.volume_max = audio_opts->p_volume_max;
 		audio->params.p_fu.volume_res = audio_opts->p_volume_res;
 	}
+
+	// TODO: This may need some change with the audio params for the current alt mode
 	audio->params.p_chmask = audio_opts->p_chmask;
 	memcpy(audio->params.p_srates, audio_opts->p_srates,
 			sizeof(audio->params.p_srates));
 	audio->params.p_ssize = audio_opts->p_ssize;
+
 	if (FUOUT_EN(audio_opts)) {
-		audio->params.c_fu.id = USB_OUT_FU_ID;
+		audio->params.c_fu.id = USB_OUT_FU_ID(audio_opts);
 		audio->params.c_fu.mute_present = audio_opts->c_mute_present;
 		audio->params.c_fu.volume_present =
 				audio_opts->c_volume_present;
@@ -1914,14 +1878,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
 
 err_card_register:
 	usb_free_all_descriptors(f);
-err_free_fu:
-	kfree(out_feature_unit_desc);
-	out_feature_unit_desc = NULL;
-	kfree(in_feature_unit_desc);
-	in_feature_unit_desc = NULL;
 fail:
-	kfree(ac_header_desc);
-	ac_header_desc = NULL;
 	return status;
 }
 
@@ -2342,11 +2299,6 @@ static void f_audio_unbind(struct usb_configuration *c, struct usb_function *f)
 	g_audio_cleanup(audio);
 	usb_free_all_descriptors(f);
 
-	kfree(out_feature_unit_desc);
-	out_feature_unit_desc = NULL;
-	kfree(in_feature_unit_desc);
-	in_feature_unit_desc = NULL;
-
 	kfree(ac_header_desc);
 	ac_header_desc = NULL;
 
diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h
index ae69f1eb872d..e45bd17eb92f 100644
--- a/drivers/usb/gadget/function/u_uac1.h
+++ b/drivers/usb/gadget/function/u_uac1.h
@@ -9,6 +9,7 @@
 #define __U_UAC1_H
 
 #include <linux/usb/composite.h>
+#include <linux/usb/audio.h>
 #include "uac_common.h"
 
 #define UAC1_OUT_EP_MAX_PACKET_SIZE	200
@@ -47,8 +48,15 @@ struct f_uac1_alt_0_opts {
 	struct f_uac1_alt_opts_common c;
 
 	char			name[USB_MAX_STRING_LEN];
+
+	/* Descriptors */
+	struct usb_interface_descriptor	intf_desc;
 };
 
+DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(UAC_MAX_RATES);
+#define uac_format_type_i_discrete_descriptor			\
+	uac_format_type_i_discrete_descriptor_##UAC_MAX_RATES
+
 /* Alt modes 1+ */
 struct f_uac1_alt_opts {
 	struct f_uac1_alt_opts_common c;
@@ -70,6 +78,36 @@ struct f_uac1_alt_opts {
 	u8			hs_bint;
 	s16			terminal_type;
 
+	/* Descriptors */
+	struct usb_interface_descriptor			intf_desc;
+	struct uac1_as_header_descriptor		as_header_desc;
+	struct uac_format_type_i_discrete_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 it_id; /* Input Terminal Descriptor bTerminalID */
+	u8 fu_id; /* Feature Unit Descriptor bUnitID */
+	u8 ot_id; /* Output Terminal Descriptor bTerminalID */
+};
+
+#undef uac_format_type_i_discrete_descriptor
+
+struct f_uac1_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_uac1_alt_opts *alt_opts;
+
+	struct uac_input_terminal_descriptor it_desc;
+	struct uac1_output_terminal_descriptor ot_desc;
+
+	/* Feature unit is optional */
+	struct uac_feature_unit_descriptor *fu_desc;
 };
 
 struct f_uac1_opts {
