]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/commitdiff
media: uvcvideo: Support multiple frame descriptors with the same dimensions
authorPhilipp Zabel <philipp.zabel@gmail.com>
Thu, 4 Jan 2018 22:51:29 +0000 (17:51 -0500)
committerMauro Carvalho Chehab <mchehab@s-opensource.com>
Fri, 23 Feb 2018 07:10:41 +0000 (02:10 -0500)
The Microsoft HoloLens Sensors device has two separate frame descriptors
with the same dimensions, each with a single different frame interval:

      VideoStreaming Interface Descriptor:
        bLength                            30
        bDescriptorType                    36
        bDescriptorSubtype                  5 (FRAME_UNCOMPRESSED)
        bFrameIndex                         1
        bmCapabilities                   0x00
          Still image unsupported
        wWidth                           1280
        wHeight                           481
        dwMinBitRate                147763200
        dwMaxBitRate                147763200
        dwMaxVideoFrameBufferSize      615680
        dwDefaultFrameInterval         333333
        bFrameIntervalType                  1
        dwFrameInterval( 0)            333333
      VideoStreaming Interface Descriptor:
        bLength                            30
        bDescriptorType                    36
        bDescriptorSubtype                  5 (FRAME_UNCOMPRESSED)
        bFrameIndex                         2
        bmCapabilities                   0x00
          Still image unsupported
        wWidth                           1280
        wHeight                           481
        dwMinBitRate                443289600
        dwMaxBitRate                443289600
        dwMaxVideoFrameBufferSize      615680
        dwDefaultFrameInterval         111111
        bFrameIntervalType                  1
        dwFrameInterval( 0)            111111

Skip duplicate dimensions in enum_framesizes, let enum_frameintervals list
the intervals from both frame descriptors. Change set_streamparm to switch
to the correct frame index when changing the interval. This enables 90 fps
capture on a Lenovo Explorer Windows Mixed Reality headset.

[laurent.pinchart@ideasonboard.com: Renamed tmp_ival to ival]

Signed-off-by: Philipp Zabel <philipp.zabel@gmail.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
drivers/media/usb/uvc/uvc_v4l2.c

index a13ad4e178be5a289638a89c3023c11569d1d9eb..5ff6ca1fad2d7b1dbfba5879d439cd2c957f6cb4 100644 (file)
@@ -373,7 +373,10 @@ static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream,
 {
        struct uvc_streaming_control probe;
        struct v4l2_fract timeperframe;
-       uint32_t interval;
+       struct uvc_format *format;
+       struct uvc_frame *frame;
+       __u32 interval, maxd;
+       unsigned int i;
        int ret;
 
        if (parm->type != stream->type)
@@ -396,9 +399,33 @@ static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream,
                return -EBUSY;
        }
 
+       format = stream->cur_format;
+       frame = stream->cur_frame;
        probe = stream->ctrl;
-       probe.dwFrameInterval =
-               uvc_try_frame_interval(stream->cur_frame, interval);
+       probe.dwFrameInterval = uvc_try_frame_interval(frame, interval);
+       maxd = abs((__s32)probe.dwFrameInterval - interval);
+
+       /* Try frames with matching size to find the best frame interval. */
+       for (i = 0; i < format->nframes && maxd != 0; i++) {
+               __u32 d, ival;
+
+               if (&format->frame[i] == stream->cur_frame)
+                       continue;
+
+               if (format->frame[i].wWidth != stream->cur_frame->wWidth ||
+                   format->frame[i].wHeight != stream->cur_frame->wHeight)
+                       continue;
+
+               ival = uvc_try_frame_interval(&format->frame[i], interval);
+               d = abs((__s32)ival - interval);
+               if (d >= maxd)
+                       continue;
+
+               frame = &format->frame[i];
+               probe.bFrameIndex = frame->bFrameIndex;
+               probe.dwFrameInterval = ival;
+               maxd = d;
+       }
 
        /* Probe the device with the new settings. */
        ret = uvc_probe_video(stream, &probe);
@@ -408,6 +435,7 @@ static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream,
        }
 
        stream->ctrl = probe;
+       stream->cur_frame = frame;
        mutex_unlock(&stream->mutex);
 
        /* Return the actual frame period. */
@@ -1146,7 +1174,8 @@ static int uvc_ioctl_enum_framesizes(struct file *file, void *fh,
        struct uvc_streaming *stream = handle->stream;
        struct uvc_format *format = NULL;
        struct uvc_frame *frame;
-       int i;
+       unsigned int index;
+       unsigned int i;
 
        /* Look for the given pixel format */
        for (i = 0; i < stream->nformats; i++) {
@@ -1158,10 +1187,20 @@ static int uvc_ioctl_enum_framesizes(struct file *file, void *fh,
        if (format == NULL)
                return -EINVAL;
 
-       if (fsize->index >= format->nframes)
+       /* Skip duplicate frame sizes */
+       for (i = 0, index = 0; i < format->nframes; i++) {
+               if (i && frame->wWidth == format->frame[i].wWidth &&
+                   frame->wHeight == format->frame[i].wHeight)
+                       continue;
+               frame = &format->frame[i];
+               if (index == fsize->index)
+                       break;
+               index++;
+       }
+
+       if (i == format->nframes)
                return -EINVAL;
 
-       frame = &format->frame[fsize->index];
        fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
        fsize->discrete.width = frame->wWidth;
        fsize->discrete.height = frame->wHeight;
@@ -1175,7 +1214,9 @@ static int uvc_ioctl_enum_frameintervals(struct file *file, void *fh,
        struct uvc_streaming *stream = handle->stream;
        struct uvc_format *format = NULL;
        struct uvc_frame *frame = NULL;
-       int i;
+       unsigned int nintervals;
+       unsigned int index;
+       unsigned int i;
 
        /* Look for the given pixel format and frame size */
        for (i = 0; i < stream->nformats; i++) {
@@ -1187,30 +1228,28 @@ static int uvc_ioctl_enum_frameintervals(struct file *file, void *fh,
        if (format == NULL)
                return -EINVAL;
 
+       index = fival->index;
        for (i = 0; i < format->nframes; i++) {
                if (format->frame[i].wWidth == fival->width &&
                    format->frame[i].wHeight == fival->height) {
                        frame = &format->frame[i];
-                       break;
+                       nintervals = frame->bFrameIntervalType ?: 1;
+                       if (index < nintervals)
+                               break;
+                       index -= nintervals;
                }
        }
-       if (frame == NULL)
+       if (i == format->nframes)
                return -EINVAL;
 
        if (frame->bFrameIntervalType) {
-               if (fival->index >= frame->bFrameIntervalType)
-                       return -EINVAL;
-
                fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
                fival->discrete.numerator =
-                       frame->dwFrameInterval[fival->index];
+                       frame->dwFrameInterval[index];
                fival->discrete.denominator = 10000000;
                uvc_simplify_fraction(&fival->discrete.numerator,
                        &fival->discrete.denominator, 8, 333);
        } else {
-               if (fival->index)
-                       return -EINVAL;
-
                fival->type = V4L2_FRMIVAL_TYPE_STEPWISE;
                fival->stepwise.min.numerator = frame->dwFrameInterval[0];
                fival->stepwise.min.denominator = 10000000;