summaryrefslogtreecommitdiff
path: root/drivers/media/pci/tw686x/tw686x-video.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/pci/tw686x/tw686x-video.c')
-rw-r--r--drivers/media/pci/tw686x/tw686x-video.c182
1 files changed, 121 insertions, 61 deletions
diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c
index cdb16de770fe..c3fafa97b2d0 100644
--- a/drivers/media/pci/tw686x/tw686x-video.c
+++ b/drivers/media/pci/tw686x/tw686x-video.c
@@ -577,7 +577,7 @@ static int tw686x_buf_prepare(struct vb2_buffer *vb)
return 0;
}
-static struct vb2_ops tw686x_video_qops = {
+static const struct vb2_ops tw686x_video_qops = {
.queue_setup = tw686x_queue_setup,
.buf_queue = tw686x_buf_queue,
.buf_prepare = tw686x_buf_prepare,
@@ -672,30 +672,20 @@ static int tw686x_try_fmt_vid_cap(struct file *file, void *priv,
return 0;
}
-static int tw686x_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
+static int tw686x_set_format(struct tw686x_video_channel *vc,
+ unsigned int pixelformat, unsigned int width,
+ unsigned int height, bool realloc)
{
- struct tw686x_video_channel *vc = video_drvdata(file);
struct tw686x_dev *dev = vc->dev;
- u32 val, width, line_width, height;
- unsigned long bitsperframe;
+ u32 val, dma_width, dma_height, dma_line_width;
int err, pb;
- if (vb2_is_busy(&vc->vidq))
- return -EBUSY;
-
- bitsperframe = vc->width * vc->height * vc->format->depth;
- err = tw686x_try_fmt_vid_cap(file, priv, f);
- if (err)
- return err;
-
- vc->format = format_by_fourcc(f->fmt.pix.pixelformat);
- vc->width = f->fmt.pix.width;
- vc->height = f->fmt.pix.height;
+ vc->format = format_by_fourcc(pixelformat);
+ vc->width = width;
+ vc->height = height;
/* We need new DMA buffers if the framesize has changed */
- if (dev->dma_ops->alloc &&
- bitsperframe != vc->width * vc->height * vc->format->depth) {
+ if (dev->dma_ops->alloc && realloc) {
for (pb = 0; pb < 2; pb++)
dev->dma_ops->free(vc, pb);
@@ -739,14 +729,36 @@ static int tw686x_s_fmt_vid_cap(struct file *file, void *priv,
reg_write(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch], val);
/* Program the DMA frame size */
- width = (vc->width * 2) & 0x7ff;
- height = vc->height / 2;
- line_width = (vc->width * 2) & 0x7ff;
- val = (height << 22) | (line_width << 11) | width;
+ dma_width = (vc->width * 2) & 0x7ff;
+ dma_height = vc->height / 2;
+ dma_line_width = (vc->width * 2) & 0x7ff;
+ val = (dma_height << 22) | (dma_line_width << 11) | dma_width;
reg_write(vc->dev, VDMA_WHP[vc->ch], val);
return 0;
}
+static int tw686x_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct tw686x_video_channel *vc = video_drvdata(file);
+ unsigned long area;
+ bool realloc;
+ int err;
+
+ if (vb2_is_busy(&vc->vidq))
+ return -EBUSY;
+
+ area = vc->width * vc->height;
+ err = tw686x_try_fmt_vid_cap(file, priv, f);
+ if (err)
+ return err;
+
+ realloc = area != (f->fmt.pix.width * f->fmt.pix.height);
+ return tw686x_set_format(vc, f->fmt.pix.pixelformat,
+ f->fmt.pix.width, f->fmt.pix.height,
+ realloc);
+}
+
static int tw686x_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
@@ -763,17 +775,9 @@ static int tw686x_querycap(struct file *file, void *priv,
return 0;
}
-static int tw686x_s_std(struct file *file, void *priv, v4l2_std_id id)
+static int tw686x_set_standard(struct tw686x_video_channel *vc, v4l2_std_id id)
{
- struct tw686x_video_channel *vc = video_drvdata(file);
- struct v4l2_format f;
- u32 val, ret;
-
- if (vc->video_standard == id)
- return 0;
-
- if (vb2_is_busy(&vc->vidq))
- return -EBUSY;
+ u32 val;
if (id & V4L2_STD_NTSC)
val = 0;
@@ -802,14 +806,31 @@ static int tw686x_s_std(struct file *file, void *priv, v4l2_std_id id)
val |= (1 << (SYS_MODE_DMA_SHIFT + vc->ch));
reg_write(vc->dev, VIDEO_CONTROL1, val);
+ return 0;
+}
+
+static int tw686x_s_std(struct file *file, void *priv, v4l2_std_id id)
+{
+ struct tw686x_video_channel *vc = video_drvdata(file);
+ struct v4l2_format f;
+ int ret;
+
+ if (vc->video_standard == id)
+ return 0;
+
+ if (vb2_is_busy(&vc->vidq))
+ return -EBUSY;
+
+ ret = tw686x_set_standard(vc, id);
+ if (ret)
+ return ret;
/*
* Adjust format after V4L2_STD_525_60/V4L2_STD_625_50 change,
* calling g_fmt and s_fmt will sanitize the height
* according to the standard.
*/
- ret = tw686x_g_fmt_vid_cap(file, priv, &f);
- if (!ret)
- tw686x_s_fmt_vid_cap(file, priv, &f);
+ tw686x_g_fmt_vid_cap(file, priv, &f);
+ tw686x_s_fmt_vid_cap(file, priv, &f);
/*
* Frame decimation depends on the chosen standard,
@@ -885,6 +906,42 @@ static int tw686x_g_std(struct file *file, void *priv, v4l2_std_id *id)
return 0;
}
+static int tw686x_enum_framesizes(struct file *file, void *priv,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct tw686x_video_channel *vc = video_drvdata(file);
+
+ if (fsize->index)
+ return -EINVAL;
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise.max_width = TW686X_VIDEO_WIDTH;
+ fsize->stepwise.min_width = fsize->stepwise.max_width / 2;
+ fsize->stepwise.step_width = fsize->stepwise.min_width;
+ fsize->stepwise.max_height = TW686X_VIDEO_HEIGHT(vc->video_standard);
+ fsize->stepwise.min_height = fsize->stepwise.max_height / 2;
+ fsize->stepwise.step_height = fsize->stepwise.min_height;
+ return 0;
+}
+
+static int tw686x_enum_frameintervals(struct file *file, void *priv,
+ struct v4l2_frmivalenum *ival)
+{
+ struct tw686x_video_channel *vc = video_drvdata(file);
+ int max_fps = TW686X_MAX_FPS(vc->video_standard);
+ int max_rates = DIV_ROUND_UP(max_fps, 2);
+
+ if (ival->index >= max_rates)
+ return -EINVAL;
+
+ ival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+ ival->discrete.numerator = 1;
+ if (ival->index < (max_rates - 1))
+ ival->discrete.denominator = (ival->index + 1) * 2;
+ else
+ ival->discrete.denominator = max_fps;
+ return 0;
+}
+
static int tw686x_g_parm(struct file *file, void *priv,
struct v4l2_streamparm *sp)
{
@@ -928,10 +985,21 @@ static int tw686x_enum_fmt_vid_cap(struct file *file, void *priv,
return 0;
}
+static void tw686x_set_input(struct tw686x_video_channel *vc, unsigned int i)
+{
+ u32 val;
+
+ vc->input = i;
+
+ val = reg_read(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch]);
+ val &= ~(0x3 << 30);
+ val |= i << 30;
+ reg_write(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch], val);
+}
+
static int tw686x_s_input(struct file *file, void *priv, unsigned int i)
{
struct tw686x_video_channel *vc = video_drvdata(file);
- u32 val;
if (i >= TW686X_INPUTS_PER_CH)
return -EINVAL;
@@ -943,12 +1011,7 @@ static int tw686x_s_input(struct file *file, void *priv, unsigned int i)
if (vb2_is_busy(&vc->vidq))
return -EBUSY;
- vc->input = i;
-
- val = reg_read(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch]);
- val &= ~(0x3 << 30);
- val |= i << 30;
- reg_write(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch], val);
+ tw686x_set_input(vc, i);
return 0;
}
@@ -1007,6 +1070,8 @@ static const struct v4l2_ioctl_ops tw686x_video_ioctl_ops = {
.vidioc_g_parm = tw686x_g_parm,
.vidioc_s_parm = tw686x_s_parm,
+ .vidioc_enum_framesizes = tw686x_enum_framesizes,
+ .vidioc_enum_frameintervals = tw686x_enum_frameintervals,
.vidioc_enum_input = tw686x_enum_input,
.vidioc_g_input = tw686x_g_input,
@@ -1093,8 +1158,7 @@ void tw686x_video_free(struct tw686x_dev *dev)
for (ch = 0; ch < max_channels(dev); ch++) {
struct tw686x_video_channel *vc = &dev->video_channels[ch];
- if (vc->device)
- video_unregister_device(vc->device);
+ video_unregister_device(vc->device);
if (dev->dma_ops->free)
for (pb = 0; pb < 2; pb++)
@@ -1104,7 +1168,7 @@ void tw686x_video_free(struct tw686x_dev *dev)
int tw686x_video_init(struct tw686x_dev *dev)
{
- unsigned int ch, val, pb;
+ unsigned int ch, val;
int err;
if (dev->dma_mode == TW686X_DMA_MODE_MEMCPY)
@@ -1138,27 +1202,23 @@ int tw686x_video_init(struct tw686x_dev *dev)
vc->ch = ch;
/* default settings */
- vc->format = &formats[0];
- vc->video_standard = V4L2_STD_NTSC;
- vc->width = TW686X_VIDEO_WIDTH;
- vc->height = TW686X_VIDEO_HEIGHT(vc->video_standard);
- vc->input = 0;
+ err = tw686x_set_standard(vc, V4L2_STD_NTSC);
+ if (err)
+ goto error;
- reg_write(vc->dev, SDT[ch], 0);
- tw686x_set_framerate(vc, 30);
+ err = tw686x_set_format(vc, formats[0].fourcc,
+ TW686X_VIDEO_WIDTH,
+ TW686X_VIDEO_HEIGHT(vc->video_standard),
+ true);
+ if (err)
+ goto error;
+ tw686x_set_input(vc, 0);
+ tw686x_set_framerate(vc, 30);
reg_write(dev, VDELAY_LO[ch], 0x14);
reg_write(dev, HACTIVE_LO[ch], 0xd0);
reg_write(dev, VIDEO_SIZE[ch], 0);
- if (dev->dma_ops->alloc) {
- for (pb = 0; pb < 2; pb++) {
- err = dev->dma_ops->alloc(vc, pb);
- if (err)
- goto error;
- }
- }
-
vc->vidq.io_modes = VB2_READ | VB2_MMAP | VB2_DMABUF;
vc->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vc->vidq.drv_priv = vc;