Re: [PATCH] usb: gadget: f_uac2: modulate playback data rate

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux