Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

media: video-i2c: more precise intervals between frames

MLX90640 should ideally be working without a frame skip.
In short, if a frame is skipped, then half of a frame loses correction
information, having no way to retrieve its original compensation.

This patch improves the timing in three ways:

1) Replaced schedule_timeout_interruptible() to usleep_range()
The former "only ensures that it will sleep for at least
schedule_delay (if not interrupted)", as pointed out by mchehab.
As a result, the frame rate could lag behind than the actual capability
of the hardware
(Raspberry Pi would show a few Hz slower than set value)

2) Calculation based on us, not jiffies
Jiffies usually has resolution of 100Hz, and possibly even cruder.
MLX90640 can go up to 64Hz frame rate, which does not make sense to
calculate the interval with aforementioned resolution.

3) Interval calculation based on the last frame's end time
Using the start time of the current frame will probably make tiny bit
of drift every time. This made more sense when I didn't realize 1),
but it still makes sense without adding virtually any complexity,
so this stays in.

Signed-off-by: Seongyong Park <euphoriccatface@gmail.com>
Acked-by: Matt Ranostay <matt.ranostay@konsulko.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>

authored by

Seongyong Park and committed by
Mauro Carvalho Chehab
439b87fc 1e153520

+12 -9
+12 -9
drivers/media/i2c/video-i2c.c
··· 441 441 static int video_i2c_thread_vid_cap(void *priv) 442 442 { 443 443 struct video_i2c_data *data = priv; 444 - unsigned int delay = mult_frac(HZ, data->frame_interval.numerator, 445 - data->frame_interval.denominator); 444 + u32 delay = mult_frac(1000000UL, data->frame_interval.numerator, 445 + data->frame_interval.denominator); 446 + s64 end_us = ktime_to_us(ktime_get()); 446 447 447 448 set_freezable(); 448 449 449 450 do { 450 - unsigned long start_jiffies = jiffies; 451 451 struct video_i2c_buffer *vid_cap_buf = NULL; 452 + s64 current_us; 452 453 int schedule_delay; 453 454 454 455 try_to_freeze(); ··· 476 475 VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); 477 476 } 478 477 479 - schedule_delay = delay - (jiffies - start_jiffies); 480 - 481 - if (time_after(jiffies, start_jiffies + delay)) 482 - schedule_delay = delay; 483 - 484 - schedule_timeout_interruptible(schedule_delay); 478 + end_us += delay; 479 + current_us = ktime_to_us(ktime_get()); 480 + if (current_us < end_us) { 481 + schedule_delay = end_us - current_us; 482 + usleep_range(schedule_delay * 3 / 4, schedule_delay); 483 + } else { 484 + end_us = current_us; 485 + } 485 486 } while (!kthread_should_stop()); 486 487 487 488 return 0;