Commit 3315c59a authored by Hans Verkuil's avatar Hans Verkuil Committed by Mauro Carvalho Chehab

[media] hdpvr: add dv_timings support

Signed-off-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 8f69da95
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/videodev2.h> #include <linux/videodev2.h>
#include <linux/v4l2-dv-timings.h>
#include <media/v4l2-dev.h> #include <media/v4l2-dev.h>
#include <media/v4l2-common.h> #include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h> #include <media/v4l2-ioctl.h>
...@@ -36,6 +37,25 @@ ...@@ -36,6 +37,25 @@
list_size(&dev->free_buff_list), \ list_size(&dev->free_buff_list), \
list_size(&dev->rec_buff_list)); } list_size(&dev->rec_buff_list)); }
static const struct v4l2_dv_timings hdpvr_dv_timings[] = {
V4L2_DV_BT_CEA_720X480I59_94,
V4L2_DV_BT_CEA_720X576I50,
V4L2_DV_BT_CEA_720X480P59_94,
V4L2_DV_BT_CEA_720X576P50,
V4L2_DV_BT_CEA_1280X720P50,
V4L2_DV_BT_CEA_1280X720P60,
V4L2_DV_BT_CEA_1920X1080I50,
V4L2_DV_BT_CEA_1920X1080I60,
};
/* Use 480i59 as the default timings */
#define HDPVR_DEF_DV_TIMINGS_IDX (0)
struct hdpvr_fh {
struct v4l2_fh fh;
bool legacy_mode;
};
static uint list_size(struct list_head *list) static uint list_size(struct list_head *list)
{ {
struct list_head *tmp; struct list_head *tmp;
...@@ -355,13 +375,23 @@ static int hdpvr_stop_streaming(struct hdpvr_device *dev) ...@@ -355,13 +375,23 @@ static int hdpvr_stop_streaming(struct hdpvr_device *dev)
* video 4 linux 2 file operations * video 4 linux 2 file operations
*/ */
static int hdpvr_open(struct file *file)
{
struct hdpvr_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL);
if (fh == NULL)
return -ENOMEM;
fh->legacy_mode = true;
v4l2_fh_init(&fh->fh, video_devdata(file));
v4l2_fh_add(&fh->fh);
file->private_data = fh;
return 0;
}
static int hdpvr_release(struct file *file) static int hdpvr_release(struct file *file)
{ {
struct hdpvr_device *dev = video_drvdata(file); struct hdpvr_device *dev = video_drvdata(file);
if (!dev)
return -ENODEV;
mutex_lock(&dev->io_mutex); mutex_lock(&dev->io_mutex);
if (file->private_data == dev->owner) { if (file->private_data == dev->owner) {
hdpvr_stop_streaming(dev); hdpvr_stop_streaming(dev);
...@@ -388,9 +418,6 @@ static ssize_t hdpvr_read(struct file *file, char __user *buffer, size_t count, ...@@ -388,9 +418,6 @@ static ssize_t hdpvr_read(struct file *file, char __user *buffer, size_t count,
if (*pos) if (*pos)
return -ESPIPE; return -ESPIPE;
if (!dev)
return -ENODEV;
mutex_lock(&dev->io_mutex); mutex_lock(&dev->io_mutex);
if (dev->status == STATUS_IDLE) { if (dev->status == STATUS_IDLE) {
if (hdpvr_start_streaming(dev)) { if (hdpvr_start_streaming(dev)) {
...@@ -518,7 +545,7 @@ static unsigned int hdpvr_poll(struct file *filp, poll_table *wait) ...@@ -518,7 +545,7 @@ static unsigned int hdpvr_poll(struct file *filp, poll_table *wait)
static const struct v4l2_file_operations hdpvr_fops = { static const struct v4l2_file_operations hdpvr_fops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.open = v4l2_fh_open, .open = hdpvr_open,
.release = hdpvr_release, .release = hdpvr_release,
.read = hdpvr_read, .read = hdpvr_read,
.poll = hdpvr_poll, .poll = hdpvr_poll,
...@@ -594,6 +621,121 @@ static int vidioc_querystd(struct file *file, void *fh, v4l2_std_id *a) ...@@ -594,6 +621,121 @@ static int vidioc_querystd(struct file *file, void *fh, v4l2_std_id *a)
return 0; return 0;
} }
static int vidioc_s_dv_timings(struct file *file, void *_fh,
struct v4l2_dv_timings *timings)
{
struct hdpvr_device *dev = video_drvdata(file);
struct hdpvr_fh *fh = _fh;
int i;
fh->legacy_mode = false;
if (dev->options.video_input)
return -ENODATA;
if (dev->status != STATUS_IDLE)
return -EBUSY;
for (i = 0; i < ARRAY_SIZE(hdpvr_dv_timings); i++)
if (v4l_match_dv_timings(timings, hdpvr_dv_timings + i, 0))
break;
if (i == ARRAY_SIZE(hdpvr_dv_timings))
return -EINVAL;
dev->cur_dv_timings = hdpvr_dv_timings[i];
dev->width = hdpvr_dv_timings[i].bt.width;
dev->height = hdpvr_dv_timings[i].bt.height;
return 0;
}
static int vidioc_g_dv_timings(struct file *file, void *_fh,
struct v4l2_dv_timings *timings)
{
struct hdpvr_device *dev = video_drvdata(file);
struct hdpvr_fh *fh = _fh;
fh->legacy_mode = false;
if (dev->options.video_input)
return -ENODATA;
*timings = dev->cur_dv_timings;
return 0;
}
static int vidioc_query_dv_timings(struct file *file, void *_fh,
struct v4l2_dv_timings *timings)
{
struct hdpvr_device *dev = video_drvdata(file);
struct hdpvr_fh *fh = _fh;
struct hdpvr_video_info *vid_info;
bool interlaced;
int ret = 0;
int i;
fh->legacy_mode = false;
if (dev->options.video_input)
return -ENODATA;
vid_info = get_video_info(dev);
if (vid_info == NULL)
return -ENOLCK;
interlaced = vid_info->fps <= 30;
for (i = 0; i < ARRAY_SIZE(hdpvr_dv_timings); i++) {
const struct v4l2_bt_timings *bt = &hdpvr_dv_timings[i].bt;
unsigned hsize;
unsigned vsize;
unsigned fps;
hsize = bt->hfrontporch + bt->hsync + bt->hbackporch + bt->width;
vsize = bt->vfrontporch + bt->vsync + bt->vbackporch +
bt->il_vfrontporch + bt->il_vsync + bt->il_vbackporch +
bt->height;
fps = (unsigned)bt->pixelclock / (hsize * vsize);
if (bt->width != vid_info->width ||
bt->height != vid_info->height ||
bt->interlaced != interlaced ||
(fps != vid_info->fps && fps + 1 != vid_info->fps))
continue;
*timings = hdpvr_dv_timings[i];
break;
}
if (i == ARRAY_SIZE(hdpvr_dv_timings))
ret = -ERANGE;
kfree(vid_info);
return ret;
}
static int vidioc_enum_dv_timings(struct file *file, void *_fh,
struct v4l2_enum_dv_timings *timings)
{
struct hdpvr_device *dev = video_drvdata(file);
struct hdpvr_fh *fh = _fh;
fh->legacy_mode = false;
memset(timings->reserved, 0, sizeof(timings->reserved));
if (dev->options.video_input)
return -ENODATA;
if (timings->index >= ARRAY_SIZE(hdpvr_dv_timings))
return -EINVAL;
timings->timings = hdpvr_dv_timings[timings->index];
return 0;
}
static int vidioc_dv_timings_cap(struct file *file, void *_fh,
struct v4l2_dv_timings_cap *cap)
{
struct hdpvr_device *dev = video_drvdata(file);
struct hdpvr_fh *fh = _fh;
fh->legacy_mode = false;
if (dev->options.video_input)
return -ENODATA;
cap->type = V4L2_DV_BT_656_1120;
cap->bt.min_width = 720;
cap->bt.max_width = 1920;
cap->bt.min_height = 480;
cap->bt.max_height = 1080;
cap->bt.min_pixelclock = 27000000;
cap->bt.max_pixelclock = 74250000;
cap->bt.standards = V4L2_DV_BT_STD_CEA861;
cap->bt.capabilities = V4L2_DV_BT_CAP_INTERLACED | V4L2_DV_BT_CAP_PROGRESSIVE;
return 0;
}
static const char *iname[] = { static const char *iname[] = {
[HDPVR_COMPONENT] = "Component", [HDPVR_COMPONENT] = "Component",
[HDPVR_SVIDEO] = "S-Video", [HDPVR_SVIDEO] = "S-Video",
...@@ -827,29 +969,48 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *private_data, ...@@ -827,29 +969,48 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *private_data,
return 0; return 0;
} }
static int vidioc_g_fmt_vid_cap(struct file *file, void *private_data, static int vidioc_g_fmt_vid_cap(struct file *file, void *_fh,
struct v4l2_format *f) struct v4l2_format *f)
{ {
struct hdpvr_device *dev = video_drvdata(file); struct hdpvr_device *dev = video_drvdata(file);
struct hdpvr_fh *fh = _fh;
/*
* The original driver would always returns the current detected
* resolution as the format (and EFAULT if it couldn't be detected).
* With the introduction of VIDIOC_QUERY_DV_TIMINGS there is now a
* better way of doing this, but to stay compatible with existing
* applications we assume legacy mode every time an application opens
* the device. Only if one of the new DV_TIMINGS ioctls is called
* will the filehandle go into 'normal' mode where g_fmt returns the
* last set format.
*/
if (fh->legacy_mode) {
struct hdpvr_video_info *vid_info; struct hdpvr_video_info *vid_info;
if (!dev)
return -ENODEV;
vid_info = get_video_info(dev); vid_info = get_video_info(dev);
if (!vid_info) if (!vid_info)
return -EFAULT; return -EFAULT;
f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
f->fmt.pix.width = vid_info->width; f->fmt.pix.width = vid_info->width;
f->fmt.pix.height = vid_info->height; f->fmt.pix.height = vid_info->height;
kfree(vid_info);
} else {
f->fmt.pix.width = dev->width;
f->fmt.pix.height = dev->height;
}
f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
f->fmt.pix.sizeimage = dev->bulk_in_size; f->fmt.pix.sizeimage = dev->bulk_in_size;
f->fmt.pix.colorspace = 0;
f->fmt.pix.bytesperline = 0; f->fmt.pix.bytesperline = 0;
f->fmt.pix.field = V4L2_FIELD_ANY; f->fmt.pix.priv = 0;
if (f->fmt.pix.width == 720) {
kfree(vid_info); /* SDTV formats */
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
f->fmt.pix.field = V4L2_FIELD_INTERLACED;
} else {
/* HDTV formats */
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE240M;
f->fmt.pix.field = V4L2_FIELD_NONE;
}
return 0; return 0;
} }
...@@ -916,6 +1077,11 @@ static const struct v4l2_ioctl_ops hdpvr_ioctl_ops = { ...@@ -916,6 +1077,11 @@ static const struct v4l2_ioctl_ops hdpvr_ioctl_ops = {
.vidioc_s_std = vidioc_s_std, .vidioc_s_std = vidioc_s_std,
.vidioc_g_std = vidioc_g_std, .vidioc_g_std = vidioc_g_std,
.vidioc_querystd = vidioc_querystd, .vidioc_querystd = vidioc_querystd,
.vidioc_s_dv_timings = vidioc_s_dv_timings,
.vidioc_g_dv_timings = vidioc_g_dv_timings,
.vidioc_query_dv_timings= vidioc_query_dv_timings,
.vidioc_enum_dv_timings = vidioc_enum_dv_timings,
.vidioc_dv_timings_cap = vidioc_dv_timings_cap,
.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,
...@@ -924,6 +1090,8 @@ static const struct v4l2_ioctl_ops hdpvr_ioctl_ops = { ...@@ -924,6 +1090,8 @@ static const struct v4l2_ioctl_ops hdpvr_ioctl_ops = {
.vidioc_s_audio = vidioc_s_audio, .vidioc_s_audio = vidioc_s_audio,
.vidioc_enum_fmt_vid_cap= vidioc_enum_fmt_vid_cap, .vidioc_enum_fmt_vid_cap= vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_encoder_cmd = vidioc_encoder_cmd, .vidioc_encoder_cmd = vidioc_encoder_cmd,
.vidioc_try_encoder_cmd = vidioc_try_encoder_cmd, .vidioc_try_encoder_cmd = vidioc_try_encoder_cmd,
.vidioc_log_status = v4l2_ctrl_log_status, .vidioc_log_status = v4l2_ctrl_log_status,
...@@ -975,6 +1143,7 @@ int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent, ...@@ -975,6 +1143,7 @@ int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent,
dev->cur_std = V4L2_STD_525_60; dev->cur_std = V4L2_STD_525_60;
dev->width = 720; dev->width = 720;
dev->height = 480; dev->height = 480;
dev->cur_dv_timings = hdpvr_dv_timings[HDPVR_DEF_DV_TIMINGS_IDX];
v4l2_ctrl_handler_init(hdl, 11); v4l2_ctrl_handler_init(hdl, 11);
if (dev->fw_ver > 0x15) { if (dev->fw_ver > 0x15) {
v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops, v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
......
...@@ -92,6 +92,7 @@ struct hdpvr_device { ...@@ -92,6 +92,7 @@ struct hdpvr_device {
/* holds the current set options */ /* holds the current set options */
struct hdpvr_options options; struct hdpvr_options options;
v4l2_std_id cur_std; v4l2_std_id cur_std;
struct v4l2_dv_timings cur_dv_timings;
uint flags; uint flags;
......
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