On Fri, Aug 29, 2014 at 11:43 AM, Jassi Brar <jaswinder.singh@xxxxxxxxxx> wrote: > 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. > > We need data rate to match, as accurately as possible, the sampling rate > expressed by the UAC2 topology to the Host. > > We do this by sending packets of varying length (1 sample more than usual) > in a pattern so that we get the desired data rate over a period of a > second or sooner. The payload pattern is calculated only once (using > "Alan's Algo"), when the Host enables the interface, and saved in an > array so that the 2 ping-pong usb_requests directly index into the > pattern array to figure out the payload length they are supposed to > transfer next. Note that the increased overhead in agdev_iso_complete() > is almost zero. > > Signed-off-by: Jassi Brar <jaswinder.singh@xxxxxxxxxx> > --- > drivers/usb/gadget/function/f_uac2.c | 70 ++++++++++++++++++++++++++++++++++-- > 1 file changed, 67 insertions(+), 3 deletions(-) > > diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c > index 246a778..84fd3b0 100644 > --- a/drivers/usb/gadget/function/f_uac2.c > +++ b/drivers/usb/gadget/function/f_uac2.c > @@ -59,8 +59,15 @@ const char *uac2_name = "snd_uac2"; > struct uac2_req { > struct uac2_rtd_params *pp; /* parent param */ > struct usb_request *req; > + unsigned idx; /* current element of length-pattern loop */ > }; > > +/* > + * 5512.5Hz is going to need the maximum number of elements (80), > + * in the length-pattern loop, among standard ALSA supported rates. > + */ > +#define MAX_LOOP_LEN 80 > + > struct uac2_rtd_params { > struct snd_uac2_chip *uac2; /* parent chip */ > bool ep_enabled; /* if the ep is enabled */ > @@ -80,6 +87,9 @@ struct uac2_rtd_params { > unsigned max_psize; > struct uac2_req ureq[USB_XFERS]; > > + unsigned pattern[MAX_LOOP_LEN]; > + unsigned plen; /* valid entries in pattern[] */ > + > spinlock_t lock; > }; > > @@ -191,8 +201,12 @@ agdev_iso_complete(struct usb_ep *ep, struct usb_request *req) > > spin_lock_irqsave(&prm->lock, flags); > > - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) > + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { > + /* Update length for next payload */ > + ur->idx = (ur->idx + USB_XFERS) % prm->plen; > + req->length = prm->pattern[ur->idx]; > req->actual = req->length; > + } > > pending = prm->hw_ptr % prm->period_size; > pending += req->actual; > @@ -1066,6 +1080,31 @@ err: > return -EINVAL; > } > > +/* > + * Find optimal pattern of payloads for a given number > + * of samples and maximum sync period (in ms) over which > + * we have to distribute them uniformly. > + */ > +static unsigned > +get_pattern(unsigned samples, unsigned sync, unsigned *pt) > +{ > + unsigned n, x = 0, i = 0, p = samples % sync; > + > + do { > + x += p; > + n = samples / sync; > + if (x >= sync) { > + n += 1; > + x -= sync; > + } > + if (pt) > + pt[i] = n; > + i++; > + } while (x); > + > + return i; > +} > + > static int > afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt) > { > @@ -1097,11 +1136,35 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt) > if (intf == agdev->as_out_intf) { > ep = agdev->out_ep; > prm = &uac2->c_prm; > + prm->plen = 1; > + prm->pattern[0] = prm->max_psize; > config_ep_by_speed(gadget, fn, ep); > agdev->as_out_alt = alt; > } else if (intf == agdev->as_in_intf) { > + struct f_uac2_opts *opts = agdev_to_uac2_opts(agdev); > + unsigned intvl, rate; > + > + if (gadget->speed == USB_SPEED_FULL) > + intvl = (1 << (fs_epin_desc.bInterval - 1)) * 1000; > + else > + intvl = (1 << (hs_epin_desc.bInterval - 1)) * 125; > + > + rate = opts->p_srate; > + if (rate == 5512) { /* which implies 5512.5 practically */ > + rate = 55125; > + intvl *= 10; > + } > + > ep = agdev->in_ep; > prm = &uac2->p_prm; > + prm->plen = get_pattern(rate, intvl, NULL); /* dry run */ > + /* We try to support arbitrary rates too */ > + if (prm->plen > MAX_LOOP_LEN) { > + prm->plen = 1; > + prm->pattern[0] = rate / intvl; > + } else { > + prm->plen = get_pattern(rate, intvl, prm->pattern); > We need to multiply every element with framesize as well. Sorry I missed. Other than that I think its all ready (just found my BB doesn't boot anymore after ~2yrs of gathering dust). -Jassi -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html