V4L/DVB (13827): uvcvideo: Switch to a monotonic clock for V4L2 buffers timestamps
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Thu, 10 Dec 2009 01:57:48 +0000 (22:57 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Fri, 26 Feb 2010 18:10:24 +0000 (15:10 -0300)
The realtime clock provided by do_gettimeofday() is affected by time
jumps caused by NTP or DST. Furthermore, preliminary investigation
showed that SMP systems the realtime clock is based on the CPU TSC,
and those could get slightly out of sync, resulting in jitter in the
timestamps depending on which processor handles the USB interrupts.

Instead of the realtime clock, use a monotonic high resolution clock to
timestamp the buffer. As this could in theory introduce a regression
with some userspace applications expecting a realtime clock timestamp,
add a module parameter to switch back to the realtime clock.

Thanks to Paulo Assis for pointing out and investigating the issue.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/uvc/uvc_driver.c
drivers/media/video/uvc/uvc_queue.c
drivers/media/video/uvc/uvc_video.c
drivers/media/video/uvc/uvcvideo.h

index 391cccca7ffcb6f31bb7dca1a13521d1391b2299..05ac2774d3b9bc502a9fbd44b3754c4fa1d0302c 100644 (file)
@@ -43,6 +43,7 @@
 #define DRIVER_VERSION         "v0.1.0"
 #endif
 
+unsigned int uvc_clock_param = CLOCK_MONOTONIC;
 unsigned int uvc_no_drop_param;
 static unsigned int uvc_quirks_param;
 unsigned int uvc_trace_param;
@@ -1891,6 +1892,45 @@ static int uvc_reset_resume(struct usb_interface *intf)
        return __uvc_resume(intf, 1);
 }
 
+/* ------------------------------------------------------------------------
+ * Module parameters
+ */
+
+static int uvc_clock_param_get(char *buffer, struct kernel_param *kp)
+{
+       if (uvc_clock_param == CLOCK_MONOTONIC)
+               return sprintf(buffer, "CLOCK_MONOTONIC");
+       else
+               return sprintf(buffer, "CLOCK_REALTIME");
+}
+
+static int uvc_clock_param_set(const char *val, struct kernel_param *kp)
+{
+       if (strncasecmp(val, "clock_", strlen("clock_")) == 0)
+               val += strlen("clock_");
+
+       if (strcasecmp(val, "monotonic") == 0)
+               uvc_clock_param = CLOCK_MONOTONIC;
+       else if (strcasecmp(val, "realtime") == 0)
+               uvc_clock_param = CLOCK_REALTIME;
+       else
+               return -EINVAL;
+
+       return 0;
+}
+
+module_param_call(clock, uvc_clock_param_set, uvc_clock_param_get,
+                 &uvc_clock_param, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(clock, "Video buffers timestamp clock");
+module_param_named(nodrop, uvc_no_drop_param, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames");
+module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(quirks, "Forced device quirks");
+module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(trace, "Trace level bitmask");
+module_param_named(timeout, uvc_timeout_param, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(timeout, "Streaming control requests timeout");
+
 /* ------------------------------------------------------------------------
  * Driver initialization and cleanup
  */
@@ -2197,15 +2237,6 @@ static void __exit uvc_cleanup(void)
 module_init(uvc_init);
 module_exit(uvc_cleanup);
 
-module_param_named(nodrop, uvc_no_drop_param, uint, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames");
-module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(quirks, "Forced device quirks");
-module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(trace, "Trace level bitmask");
-module_param_named(timeout, uvc_timeout_param, uint, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(timeout, "Streaming control requests timeout");
-
 MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
index ea11839cba4a439f92461d899910171d1c9f41bb..4a925a31b0e0cb5bcaebe6d654dc2eabebd27482 100644 (file)
@@ -502,7 +502,6 @@ struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
        spin_unlock_irqrestore(&queue->irqlock, flags);
 
        buf->buf.sequence = queue->sequence++;
-       do_gettimeofday(&buf->buf.timestamp);
 
        wake_up(&buf->wait);
        return nextbuf;
index 7dcf534a0cf350c28844a766335a3f121b0476c2..6b0666be370fdb6e6cc23398f2939b5865fa828d 100644 (file)
@@ -410,6 +410,8 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
         * when the EOF bit is set to force synchronisation on the next packet.
         */
        if (buf->state != UVC_BUF_STATE_ACTIVE) {
+               struct timespec ts;
+
                if (fid == stream->last_fid) {
                        uvc_trace(UVC_TRACE_FRAME, "Dropping payload (out of "
                                "sync).\n");
@@ -419,6 +421,14 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
                        return -ENODATA;
                }
 
+               if (uvc_clock_param == CLOCK_MONOTONIC)
+                       ktime_get_ts(&ts);
+               else
+                       ktime_get_real_ts(&ts);
+
+               buf->buf.timestamp.tv_sec = ts.tv_sec;
+               buf->buf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
+
                /* TODO: Handle PTS and SCR. */
                buf->state = UVC_BUF_STATE_ACTIVE;
        }
index 2337585001ea01be3d31c4300c9ec303e9580031..adcba31008beb943c311add7dbca583a6289d8ea 100644 (file)
@@ -533,6 +533,7 @@ struct uvc_driver {
 #define UVC_WARN_MINMAX                0
 #define UVC_WARN_PROBE_DEF     1
 
+extern unsigned int uvc_clock_param;
 extern unsigned int uvc_no_drop_param;
 extern unsigned int uvc_trace_param;
 extern unsigned int uvc_timeout_param;