[PATCH v10 6/6] media: uvcvideo: Fix hw timestamp handling for slow FPS
From: Ricardo Ribalda
Date: Sat Mar 23 2024 - 06:49:31 EST
In UVC 1.5 we get a single clock value per frame. With the current
buffer size of 32, FPS slowers than 32 might roll-over twice.
The current code cannot handle two roll-over and provide invalid
timestamps.
Remove all the samples from the circular buffer that are more than two
rollovers old, so the algorithm always provides good timestamps.
Note that we are removing values that are more than one second old,
which means that there is enough distance between the two points that
we use for the interpolation to provide good values.
Tested-by: HungNien Chen <hn.chen@xxxxxxxxxxxxx>
Reviewed-by: Sergey Senozhatsky <senozhatsky@xxxxxxxxxxxx>
Signed-off-by: Ricardo Ribalda <ribalda@xxxxxxxxxxxx>
---
drivers/media/usb/uvc/uvc_video.c | 24 ++++++++++++++++++++++++
drivers/media/usb/uvc/uvcvideo.h | 1 +
2 files changed, 25 insertions(+)
diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index 5df8f61d39cd1..900b57afac93a 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -471,8 +471,31 @@ static void uvc_video_clock_add_sample(struct uvc_clock *clock,
{
unsigned long flags;
+ /*
+ * If we write new data on the position where we had the last
+ * overflow, remove the overflow pointer. There is no overflow
+ * on the whole circular buffer.
+ */
+ if (clock->head == clock->last_sof_overflow)
+ clock->last_sof_overflow = -1;
+
spin_lock_irqsave(&clock->lock, flags);
+ /* Handle overflows */
+ if (clock->count > 0 && clock->last_sof > sample->dev_sof) {
+ /*
+ * Remove data from the circular buffer that is older than the
+ * last overflow. We only support one overflow per circular
+ * buffer.
+ */
+ if (clock->last_sof_overflow != -1) {
+ clock->count = (clock->head - clock->last_sof_overflow
+ + clock->count) % clock->count;
+ }
+ clock->last_sof_overflow = clock->head;
+ }
+
+ /* Add sample */
clock->samples[clock->head] = *sample;
clock->head = (clock->head + 1) % clock->size;
clock->count = min(clock->count + 1, clock->size);
@@ -616,6 +639,7 @@ static void uvc_video_clock_reset(struct uvc_clock *clock)
clock->head = 0;
clock->count = 0;
clock->last_sof = -1;
+ clock->last_sof_overflow = -1;
clock->sof_offset = -1;
}
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index cb9dd50bba8ac..fb9f9771131ac 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -499,6 +499,7 @@ struct uvc_streaming {
unsigned int head;
unsigned int count;
unsigned int size;
+ unsigned int last_sof_overflow;
u16 last_sof;
u16 sof_offset;
--
2.44.0.396.g6e790dbe36-goog