From 02c6e7f24bc4dfec2653d44a3aac309a521450fe Mon Sep 17 00:00:00 2001 From: William Wu Date: Thu, 13 May 2021 11:59:21 +0800 Subject: [PATCH] BACKPORT: usb: gadget: f_uac1: finalize wMaxPacketSize according to bandwidth According to USB Audio Device 1.0 Spec, Ch4.6.1.1: The wMaxPacketSize of endpoint is defined as follows: Maximum packet size of endpoint is capable of sending or receiving when this configuration is selected. This is determined by the audio bandwidth constraints of the endpoint. In current code, the wMaxPacketSize is limited to 200, and the bInterval is set to 4 (1ms). That is, the maximum bandwidth over USB bus is 200 * 1000 = 200000 bytes. We find an issue about bandwidth limitation when we try to support UAC1 with 8ch * 16bit * 16KHz on RK3308 platform, which needs more bandwidth than it can support. This patch sets the wMaxPacketSize dynamically according to the parameters of UAC1. It is similar to the same thing done earlier for f_uac2. Signed-off-by: William Wu (backported from develop-4.19 commit 573d2e1e2e4b4fbf243f178f36b4328fbf535cf9) Change-Id: I3304f9eb4d1eee18f4beb44af46a246940c2dfe3 Signed-off-by: Frank Wang --- drivers/usb/gadget/function/f_uac1.c | 99 ++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 7d8fadf93422..82d63dc9d792 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -238,7 +238,7 @@ static struct usb_endpoint_descriptor fs_out_ep_desc = { .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_SYNC_ADAPTIVE | USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE), + /* .wMaxPacketSize = DYNAMIC */ .bInterval = 1, }; @@ -248,7 +248,7 @@ static struct usb_endpoint_descriptor as_out_ep_desc = { .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_SYNC_ADAPTIVE | USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE), + /* .wMaxPacketSize = DYNAMIC */ .bInterval = 4, }; @@ -285,7 +285,7 @@ static struct usb_endpoint_descriptor fs_in_ep_desc = { .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_SYNC_ASYNC | USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE), + /* .wMaxPacketSize = DYNAMIC */ .bInterval = 1, }; @@ -295,7 +295,7 @@ static struct usb_endpoint_descriptor as_in_ep_desc = { .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_SYNC_ASYNC | USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE), + /* .wMaxPacketSize = DYNAMIC */ .bInterval = 4, }; @@ -1381,6 +1381,60 @@ static int f_audio_validate_opts(struct g_audio *audio, struct device *dev) return 0; } +static int set_ep_max_packet_size(const struct f_uac1_opts *opts, + struct usb_endpoint_descriptor *ep_desc, + enum usb_device_speed speed, bool is_playback) +{ + int chmask, srate = 0, ssize; + u16 max_size_bw, max_size_ep; + unsigned int factor; + int i; + + switch (speed) { + case USB_SPEED_FULL: + max_size_ep = 1023; + factor = 1000; + break; + + case USB_SPEED_HIGH: + fallthrough; + case USB_SPEED_SUPER: + max_size_ep = 1024; + factor = 8000; + break; + + default: + return -EINVAL; + } + + if (is_playback) { + chmask = opts->p_chmask; + for (i = 0; i < UAC_MAX_RATES; i++) { + if (opts->p_srates[i] == 0) + break; + if (opts->p_srates[i] > srate) + srate = opts->p_srates[i]; + } + ssize = opts->p_ssize; + } else { + chmask = opts->c_chmask; + for (i = 0; i < UAC_MAX_RATES; i++) { + if (opts->c_srates[i] == 0) + break; + if (opts->c_srates[i] > srate) + srate = opts->c_srates[i]; + } + ssize = opts->c_ssize; + } + + max_size_bw = num_channels(chmask) * ssize * + ((srate / (factor / (1 << (ep_desc->bInterval - 1)))) + 1); + ep_desc->wMaxPacketSize = cpu_to_le16(min_t(u16, max_size_bw, + max_size_ep)); + + return 0; +} + /* audio function driver setup/binding */ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) { @@ -1495,6 +1549,43 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) memcpy(as_out_type_i_desc.tSamFreq[idx++], &audio_opts->c_srates[i], 3); } + + /* + * Calculate wMaxPacketSize according to audio bandwidth. + * Set the max packet with USB_SPEED_HIGH by default to + * be compatible with larger bandwidth requirements for + * high speed mode. + */ + status = set_ep_max_packet_size(audio_opts, &fs_out_ep_desc, + USB_SPEED_FULL, false); + if (status < 0) { + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); + goto fail; + } + + status = set_ep_max_packet_size(audio_opts, &fs_in_ep_desc, + USB_SPEED_FULL, true); + if (status < 0) { + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); + goto fail; + } + + status = set_ep_max_packet_size(audio_opts, &as_out_ep_desc, + USB_SPEED_HIGH, false); + if (status < 0) { + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); + goto fail; + } + + status = set_ep_max_packet_size(audio_opts, &as_in_ep_desc, + USB_SPEED_HIGH, true); + if (status < 0) { + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); + goto fail; + } + + as_out_ep_desc_comp.wBytesPerInterval = as_out_ep_desc.wMaxPacketSize; + as_in_ep_desc_comp.wBytesPerInterval = as_in_ep_desc.wMaxPacketSize; as_out_type_i_desc.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(idx); as_out_type_i_desc.bSamFreqType = idx;