Merge branch 'akpm' (patches from Andrew)
[cascardo/linux.git] / drivers / media / pci / tw686x / tw686x-video.c
index cdb16de..c3fafa9 100644 (file)
@@ -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;