usb: gadget: f_uac2: send reasonably sized packets

The UAC2 function driver currently responds to all packets at all times
with wMaxPacketSize packets. That results in way too fast audio
playback as the function driver (which is in fact supposed to define
the audio stream pace) delivers as fast as it can.

Fix this by sizing each packet correctly with the following steps:

 a) Set the packet's size by dividing the nominal data rate by the
    playback endpoint's interval.

 b) If there is a residual value from the calculation in a), add
    it to a accumulator to keep track of it across packets.

 c) If the accumulator has gathered at least the number of bytes
    that are needed for one sample frame, increase the packet size.

This way, the packet size calculation will get rid of any kind of
imprecision that would otherwise occur with a simple division over
time.

Some of the variables that are needed while processing each packet
are pre-computed for performance reasons.

Signed-off-by: Daniel Mack <zonque@gmail.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 246a778..a5a27a5 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -92,6 +92,15 @@
 
 	struct snd_card *card;
 	struct snd_pcm *pcm;
+
+	/* timekeeping for the playback endpoint */
+	unsigned int p_interval;
+	unsigned int p_residue;
+
+	/* pre-calculated values for playback iso completion */
+	unsigned int p_pktsize;
+	unsigned int p_pktsize_residue;
+	unsigned int p_framesize;
 };
 
 #define BUFF_SIZE_MAX	(PAGE_SIZE * 16)
@@ -191,8 +200,29 @@
 
 	spin_lock_irqsave(&prm->lock, flags);
 
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		/*
+		 * For each IN packet, take the quotient of the current data
+		 * rate and the endpoint's interval as the base packet size.
+		 * If there is a residue from this division, add it to the
+		 * residue accumulator.
+		 */
+		req->length = uac2->p_pktsize;
+		uac2->p_residue += uac2->p_pktsize_residue;
+
+		/*
+		 * Whenever there are more bytes in the accumulator than we
+		 * need to add one more sample frame, increase this packet's
+		 * size and decrease the accumulator.
+		 */
+		if (uac2->p_residue / uac2->p_interval >= uac2->p_framesize) {
+			req->length += uac2->p_framesize;
+			uac2->p_residue -= uac2->p_framesize *
+					   uac2->p_interval;
+		}
+
 		req->actual = req->length;
+	}
 
 	pending = prm->hw_ptr % prm->period_size;
 	pending += req->actual;
@@ -346,6 +376,7 @@
 	c_srate = opts->c_srate;
 	p_chmask = opts->p_chmask;
 	c_chmask = opts->c_chmask;
+	uac2->p_residue = 0;
 
 	runtime->hw = uac2_pcm_hardware;
 
@@ -1077,7 +1108,7 @@
 	struct usb_request *req;
 	struct usb_ep *ep;
 	struct uac2_rtd_params *prm;
-	int i;
+	int req_len, i;
 
 	/* No i/f has more than 2 alt settings */
 	if (alt > 1) {
@@ -1099,11 +1130,41 @@
 		prm = &uac2->c_prm;
 		config_ep_by_speed(gadget, fn, ep);
 		agdev->as_out_alt = alt;
+		req_len = prm->max_psize;
 	} else if (intf == agdev->as_in_intf) {
+		struct f_uac2_opts *opts = agdev_to_uac2_opts(agdev);
+		unsigned int factor, rate;
+		struct usb_endpoint_descriptor *ep_desc;
+
 		ep = agdev->in_ep;
 		prm = &uac2->p_prm;
 		config_ep_by_speed(gadget, fn, ep);
 		agdev->as_in_alt = alt;
+
+		/* pre-calculate the playback endpoint's interval */
+		if (gadget->speed == USB_SPEED_FULL) {
+			ep_desc = &fs_epin_desc;
+			factor = 1000;
+		} else {
+			ep_desc = &hs_epin_desc;
+			factor = 125;
+		}
+
+		/* pre-compute some values for iso_complete() */
+		uac2->p_framesize = opts->p_ssize *
+				    num_channels(opts->p_chmask);
+		rate = opts->p_srate * uac2->p_framesize;
+		uac2->p_interval = (1 << (ep_desc->bInterval - 1)) * factor;
+		uac2->p_pktsize = min_t(unsigned int, rate / uac2->p_interval,
+					prm->max_psize);
+
+		if (uac2->p_pktsize < prm->max_psize)
+			uac2->p_pktsize_residue = rate % uac2->p_interval;
+		else
+			uac2->p_pktsize_residue = 0;
+
+		req_len = uac2->p_pktsize;
+		uac2->p_residue = 0;
 	} else {
 		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
 		return -EINVAL;
@@ -1128,9 +1189,9 @@
 
 			req->zero = 0;
 			req->context = &prm->ureq[i];
-			req->length = prm->max_psize;
+			req->length = req_len;
 			req->complete = agdev_iso_complete;
-			req->buf = prm->rbuf + i * req->length;
+			req->buf = prm->rbuf + i * prm->max_psize;
 		}
 
 		if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC))