]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
usb: gadget: uvc: set req_size and n_requests based on the frame interval
authorMichael Grzeschik <m.grzeschik@pengutronix.de>
Wed, 16 Oct 2024 13:58:10 +0000 (15:58 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 17 Oct 2024 06:42:22 +0000 (08:42 +0200)
This patch is removing the initial imprecise and limited calculation of
requests needed to be used from the queue_setup callback. It instead
introduces the uvc_video_prep_requests function which is called
immediately before the request allocation.

With the information of the usb frame interval length it is possible to
calculate the number of requests needed during one frame duration.

Based on the calculated number of requests and the imagesize we
calculate the actual size per request. This calculation has the benefit
that the frame data is equally distributed over all allocated requests.

When the req_size is not in the range for the actually configured
max_req_size configured for the overall bandwidth we fallback
to use the max_req_size instead.

Since this calculations are only important for isoc transfers we just
use max_request_size for bulk and skip it.

As video->req_size will be recalculated on every video_enable resetting
it to 0 is not necessary anymore.

Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
Link: https://lore.kernel.org/r/20240403-uvc_request_length_by_interval-v7-5-e224bb1035f0@pengutronix.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/gadget/function/uvc_queue.c
drivers/usb/gadget/function/uvc_video.c

index 0aa3d7e1f3cc32c696e422196cf1d1e9ab9a34de..731e3b9d21accc6d9379630f0764c7090e656c48 100644 (file)
@@ -44,8 +44,6 @@ static int uvc_queue_setup(struct vb2_queue *vq,
 {
        struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
        struct uvc_video *video = container_of(queue, struct uvc_video, queue);
-       unsigned int req_size;
-       unsigned int nreq;
 
        if (*nbuffers > UVC_MAX_VIDEO_BUFFERS)
                *nbuffers = UVC_MAX_VIDEO_BUFFERS;
@@ -54,17 +52,6 @@ static int uvc_queue_setup(struct vb2_queue *vq,
 
        sizes[0] = video->imagesize;
 
-       req_size = video->ep->maxpacket
-                * max_t(unsigned int, video->ep->maxburst, 1)
-                * (video->ep->mult);
-
-       /* We divide by two, to increase the chance to run
-        * into fewer requests for smaller framesizes.
-        */
-       nreq = DIV_ROUND_UP(DIV_ROUND_UP(sizes[0], 2), req_size);
-       nreq = clamp(nreq, 4U, 64U);
-       video->uvc_num_requests = nreq;
-
        return 0;
 }
 
index ee7326029d6f9a42a6f5b2dbae5fc40b6246f433..ecb32a3e0376054a8c9bc0ba70e5cc5781dc6b2c 100644 (file)
@@ -486,23 +486,70 @@ uvc_video_free_requests(struct uvc_video *video)
        INIT_LIST_HEAD(&video->ureqs);
        INIT_LIST_HEAD(&video->req_free);
        INIT_LIST_HEAD(&video->req_ready);
-       video->req_size = 0;
        return 0;
 }
 
+static void
+uvc_video_prep_requests(struct uvc_video *video)
+{
+       struct uvc_device *uvc = container_of(video, struct uvc_device, video);
+       struct usb_composite_dev *cdev = uvc->func.config->cdev;
+       unsigned int interval_duration = video->ep->desc->bInterval * 1250;
+       unsigned int max_req_size, req_size, header_size;
+       unsigned int nreq;
+
+       max_req_size = video->ep->maxpacket
+                * max_t(unsigned int, video->ep->maxburst, 1)
+                * (video->ep->mult);
+
+       if (!usb_endpoint_xfer_isoc(video->ep->desc)) {
+               video->req_size = max_req_size;
+               video->uvc_num_requests =
+                       DIV_ROUND_UP(video->imagesize, max_req_size);
+
+               return;
+       }
+
+       if (cdev->gadget->speed < USB_SPEED_HIGH)
+               interval_duration = video->ep->desc->bInterval * 10000;
+
+       nreq = DIV_ROUND_UP(video->interval, interval_duration);
+
+       header_size = nreq * UVCG_REQUEST_HEADER_LEN;
+
+       req_size = DIV_ROUND_UP(video->imagesize + header_size, nreq);
+
+       if (req_size > max_req_size) {
+               /* The prepared interval length and expected buffer size
+                * is not possible to stream with the currently configured
+                * isoc bandwidth. Fallback to the maximum.
+                */
+               req_size = max_req_size;
+       }
+       video->req_size = req_size;
+
+       /* We need to compensate the amount of requests to be
+        * allocated with the maximum amount of zero length requests.
+        * Since it is possible that hw_submit will initially
+        * enqueue some zero length requests and we then will not be
+        * able to fully encode one frame.
+        */
+       video->uvc_num_requests = nreq + UVCG_REQ_MAX_ZERO_COUNT;
+}
+
 static int
 uvc_video_alloc_requests(struct uvc_video *video)
 {
        struct uvc_request *ureq;
-       unsigned int req_size;
        unsigned int i;
        int ret = -ENOMEM;
 
-       BUG_ON(video->req_size);
-
-       req_size = video->ep->maxpacket
-                * max_t(unsigned int, video->ep->maxburst, 1)
-                * (video->ep->mult);
+       /*
+        * calculate in uvc_video_prep_requests
+        * - video->uvc_num_requests
+        * - video->req_size
+        */
+       uvc_video_prep_requests(video);
 
        for (i = 0; i < video->uvc_num_requests; i++) {
                ureq = kzalloc(sizeof(struct uvc_request), GFP_KERNEL);
@@ -513,7 +560,7 @@ uvc_video_alloc_requests(struct uvc_video *video)
 
                list_add_tail(&ureq->list, &video->ureqs);
 
-               ureq->req_buffer = kmalloc(req_size, GFP_KERNEL);
+               ureq->req_buffer = kmalloc(video->req_size, GFP_KERNEL);
                if (ureq->req_buffer == NULL)
                        goto error;
 
@@ -531,12 +578,10 @@ uvc_video_alloc_requests(struct uvc_video *video)
                list_add_tail(&ureq->req->list, &video->req_free);
                /* req_size/PAGE_SIZE + 1 for overruns and + 1 for header */
                sg_alloc_table(&ureq->sgt,
-                              DIV_ROUND_UP(req_size - UVCG_REQUEST_HEADER_LEN,
+                              DIV_ROUND_UP(video->req_size - UVCG_REQUEST_HEADER_LEN,
                                            PAGE_SIZE) + 2, GFP_KERNEL);
        }
 
-       video->req_size = req_size;
-
        return 0;
 
 error:
@@ -689,7 +734,6 @@ uvcg_video_disable(struct uvc_video *video)
        INIT_LIST_HEAD(&video->ureqs);
        INIT_LIST_HEAD(&video->req_free);
        INIT_LIST_HEAD(&video->req_ready);
-       video->req_size = 0;
        spin_unlock_irqrestore(&video->req_lock, flags);
 
        /*