Commit fe0e990b authored by Kirill Smelkov's avatar Kirill Smelkov Committed by Mauro Carvalho Chehab

[media] vivi: Teach it to tune FPS

I was testing my video-over-ethernet subsystem recently, and vivi
seemed to be perfect video source for testing when one don't have lots
of capture boards and cameras. Only its framerate was hardcoded to
NTSC's 30fps, while in my country we usually use PAL (25 fps) and I
needed that to precisely simulate bandwidth.
That's why here is this patch with ->enum_frameintervals() and
->{g,s}_parm() implemented as suggested by Hans Verkuil which passes
v4l2-compliance and manual testing through v4l2-ctl -P / -p <fps>.

Regarding newly introduced __get_format(u32 pixelformat) I decided not
to convert original get_format() to operate on fourcc codes, since >= 3
places in driver need to deal with v4l2_format and otherwise it won't be
handy.

[mchehab@redhat.com: Some CodingStyle fixes]
Signed-off-by: default avatarKirill Smelkov <kirr@mns.spb.ru>
Acked-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent cb412a8d
...@@ -36,9 +36,17 @@ ...@@ -36,9 +36,17 @@
#define VIVI_MODULE_NAME "vivi" #define VIVI_MODULE_NAME "vivi"
/* Wake up at about 30 fps */ /* Maximum allowed frame rate
#define WAKE_NUMERATOR 30 *
#define WAKE_DENOMINATOR 1001 * Vivi will allow setting timeperframe in [1/FPS_MAX - FPS_MAX/1] range.
*
* Ideally FPS_MAX should be infinity, i.e. practically UINT_MAX, but that
* might hit application errors when they manipulate these values.
*
* Besides, for tpf < 1ms image-generation logic should be changed, to avoid
* producing frames with equal content.
*/
#define FPS_MAX 1000
#define MAX_WIDTH 1920 #define MAX_WIDTH 1920
#define MAX_HEIGHT 1200 #define MAX_HEIGHT 1200
...@@ -69,6 +77,12 @@ MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); ...@@ -69,6 +77,12 @@ MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
/* Global font descriptor */ /* Global font descriptor */
static const u8 *font8x16; static const u8 *font8x16;
/* timeperframe: min/max and default */
static const struct v4l2_fract
tpf_min = {.numerator = 1, .denominator = FPS_MAX},
tpf_max = {.numerator = FPS_MAX, .denominator = 1},
tpf_default = {.numerator = 1001, .denominator = 30000}; /* NTSC */
#define dprintk(dev, level, fmt, arg...) \ #define dprintk(dev, level, fmt, arg...) \
v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ## arg) v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ## arg)
...@@ -150,14 +164,14 @@ static struct vivi_fmt formats[] = { ...@@ -150,14 +164,14 @@ static struct vivi_fmt formats[] = {
}, },
}; };
static struct vivi_fmt *get_format(struct v4l2_format *f) static struct vivi_fmt *__get_format(u32 pixelformat)
{ {
struct vivi_fmt *fmt; struct vivi_fmt *fmt;
unsigned int k; unsigned int k;
for (k = 0; k < ARRAY_SIZE(formats); k++) { for (k = 0; k < ARRAY_SIZE(formats); k++) {
fmt = &formats[k]; fmt = &formats[k];
if (fmt->fourcc == f->fmt.pix.pixelformat) if (fmt->fourcc == pixelformat)
break; break;
} }
...@@ -167,6 +181,11 @@ static struct vivi_fmt *get_format(struct v4l2_format *f) ...@@ -167,6 +181,11 @@ static struct vivi_fmt *get_format(struct v4l2_format *f)
return &formats[k]; return &formats[k];
} }
static struct vivi_fmt *get_format(struct v4l2_format *f)
{
return __get_format(f->fmt.pix.pixelformat);
}
/* buffer for one video frame */ /* buffer for one video frame */
struct vivi_buffer { struct vivi_buffer {
/* common v4l buffer stuff -- must be first */ /* common v4l buffer stuff -- must be first */
...@@ -232,6 +251,7 @@ struct vivi_dev { ...@@ -232,6 +251,7 @@ struct vivi_dev {
/* video capture */ /* video capture */
struct vivi_fmt *fmt; struct vivi_fmt *fmt;
struct v4l2_fract timeperframe;
unsigned int width, height; unsigned int width, height;
struct vb2_queue vb_vidq; struct vb2_queue vb_vidq;
unsigned int field_count; unsigned int field_count;
...@@ -689,8 +709,8 @@ static void vivi_thread_tick(struct vivi_dev *dev) ...@@ -689,8 +709,8 @@ static void vivi_thread_tick(struct vivi_dev *dev)
dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index); dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index);
} }
#define frames_to_ms(frames) \ #define frames_to_ms(dev, frames) \
((frames * WAKE_NUMERATOR * 1000) / WAKE_DENOMINATOR) ((frames * dev->timeperframe.numerator * 1000) / dev->timeperframe.denominator)
static void vivi_sleep(struct vivi_dev *dev) static void vivi_sleep(struct vivi_dev *dev)
{ {
...@@ -706,7 +726,7 @@ static void vivi_sleep(struct vivi_dev *dev) ...@@ -706,7 +726,7 @@ static void vivi_sleep(struct vivi_dev *dev)
goto stop_task; goto stop_task;
/* Calculate time to wake up */ /* Calculate time to wake up */
timeout = msecs_to_jiffies(frames_to_ms(1)); timeout = msecs_to_jiffies(frames_to_ms(dev, 1));
vivi_thread_tick(dev); vivi_thread_tick(dev);
...@@ -1078,6 +1098,70 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) ...@@ -1078,6 +1098,70 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
return 0; return 0;
} }
/* timeperframe is arbitrary and continous */
static int vidioc_enum_frameintervals(struct file *file, void *priv,
struct v4l2_frmivalenum *fival)
{
struct vivi_fmt *fmt;
if (fival->index)
return -EINVAL;
fmt = __get_format(fival->pixel_format);
if (!fmt)
return -EINVAL;
/* regarding width & height - we support any */
fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS;
/* fill in stepwise (step=1.0 is requred by V4L2 spec) */
fival->stepwise.min = tpf_min;
fival->stepwise.max = tpf_max;
fival->stepwise.step = (struct v4l2_fract) {1, 1};
return 0;
}
static int vidioc_g_parm(struct file *file, void *priv,
struct v4l2_streamparm *parm)
{
struct vivi_dev *dev = video_drvdata(file);
if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
parm->parm.capture.timeperframe = dev->timeperframe;
parm->parm.capture.readbuffers = 1;
return 0;
}
#define FRACT_CMP(a, OP, b) \
((u64)(a).numerator * (b).denominator OP (u64)(b).numerator * (a).denominator)
static int vidioc_s_parm(struct file *file, void *priv,
struct v4l2_streamparm *parm)
{
struct vivi_dev *dev = video_drvdata(file);
struct v4l2_fract tpf;
if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
tpf = parm->parm.capture.timeperframe;
/* tpf: {*, 0} resets timing; clip to [min, max]*/
tpf = tpf.denominator ? tpf : tpf_default;
tpf = FRACT_CMP(tpf, <, tpf_min) ? tpf_min : tpf;
tpf = FRACT_CMP(tpf, >, tpf_max) ? tpf_max : tpf;
dev->timeperframe = tpf;
parm->parm.capture.timeperframe = tpf;
parm->parm.capture.readbuffers = 1;
return 0;
}
/* --- controls ---------------------------------------------- */ /* --- controls ---------------------------------------------- */
static int vivi_g_volatile_ctrl(struct v4l2_ctrl *ctrl) static int vivi_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
...@@ -1236,6 +1320,9 @@ static const struct v4l2_ioctl_ops vivi_ioctl_ops = { ...@@ -1236,6 +1320,9 @@ static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
.vidioc_enum_input = vidioc_enum_input, .vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input, .vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input, .vidioc_s_input = vidioc_s_input,
.vidioc_enum_frameintervals = vidioc_enum_frameintervals,
.vidioc_g_parm = vidioc_g_parm,
.vidioc_s_parm = vidioc_s_parm,
.vidioc_streamon = vb2_ioctl_streamon, .vidioc_streamon = vb2_ioctl_streamon,
.vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_streamoff = vb2_ioctl_streamoff,
.vidioc_log_status = v4l2_ctrl_log_status, .vidioc_log_status = v4l2_ctrl_log_status,
...@@ -1294,6 +1381,7 @@ static int __init vivi_create_instance(int inst) ...@@ -1294,6 +1381,7 @@ static int __init vivi_create_instance(int inst)
goto free_dev; goto free_dev;
dev->fmt = &formats[0]; dev->fmt = &formats[0];
dev->timeperframe = tpf_default;
dev->width = 640; dev->width = 640;
dev->height = 480; dev->height = 480;
dev->pixelsize = dev->fmt->depth / 8; dev->pixelsize = dev->fmt->depth / 8;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment