return ioread32(vin->base + offset);
 }
 
-static int rvin_setup(struct rvin_dev *vin)
-{
-       u32 vnmc, dmr, dmr2, interrupts;
-       v4l2_std_id std;
-       bool progressive = false, output_is_yuv = false, input_is_yuv = false;
-
-       switch (vin->format.field) {
-       case V4L2_FIELD_TOP:
-               vnmc = VNMC_IM_ODD;
-               break;
-       case V4L2_FIELD_BOTTOM:
-               vnmc = VNMC_IM_EVEN;
-               break;
-       case V4L2_FIELD_INTERLACED:
-               /* Default to TB */
-               vnmc = VNMC_IM_FULL;
-               /* Use BT if video standard can be read and is 60 Hz format */
-               if (!v4l2_subdev_call(vin_to_source(vin), video, g_std, &std)) {
-                       if (std & V4L2_STD_525_60)
-                               vnmc = VNMC_IM_FULL | VNMC_FOC;
-               }
-               break;
-       case V4L2_FIELD_INTERLACED_TB:
-               vnmc = VNMC_IM_FULL;
-               break;
-       case V4L2_FIELD_INTERLACED_BT:
-               vnmc = VNMC_IM_FULL | VNMC_FOC;
-               break;
-       case V4L2_FIELD_ALTERNATE:
-       case V4L2_FIELD_NONE:
-               vnmc = VNMC_IM_ODD_EVEN;
-               progressive = true;
-               break;
-       default:
-               vnmc = VNMC_IM_ODD;
-               break;
-       }
-
-       /*
-        * Input interface
-        */
-       switch (vin->digital->code) {
-       case MEDIA_BUS_FMT_YUYV8_1X16:
-               /* BT.601/BT.1358 16bit YCbCr422 */
-               vnmc |= VNMC_INF_YUV16;
-               input_is_yuv = true;
-               break;
-       case MEDIA_BUS_FMT_UYVY8_2X8:
-               /* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */
-               vnmc |= vin->digital->mbus_cfg.type == V4L2_MBUS_BT656 ?
-                       VNMC_INF_YUV8_BT656 : VNMC_INF_YUV8_BT601;
-               input_is_yuv = true;
-               break;
-       case MEDIA_BUS_FMT_RGB888_1X24:
-               vnmc |= VNMC_INF_RGB888;
-               break;
-       case MEDIA_BUS_FMT_UYVY10_2X10:
-               /* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */
-               vnmc |= vin->digital->mbus_cfg.type == V4L2_MBUS_BT656 ?
-                       VNMC_INF_YUV10_BT656 : VNMC_INF_YUV10_BT601;
-               input_is_yuv = true;
-               break;
-       default:
-               break;
-       }
-
-       /* Enable VSYNC Field Toogle mode after one VSYNC input */
-       dmr2 = VNDMR2_FTEV | VNDMR2_VLV(1);
-
-       /* Hsync Signal Polarity Select */
-       if (!(vin->digital->mbus_cfg.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW))
-               dmr2 |= VNDMR2_HPS;
-
-       /* Vsync Signal Polarity Select */
-       if (!(vin->digital->mbus_cfg.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW))
-               dmr2 |= VNDMR2_VPS;
-
-       /*
-        * Output format
-        */
-       switch (vin->format.pixelformat) {
-       case V4L2_PIX_FMT_NV16:
-               rvin_write(vin,
-                          ALIGN(vin->format.width * vin->format.height, 0x80),
-                          VNUVAOF_REG);
-               dmr = VNDMR_DTMD_YCSEP;
-               output_is_yuv = true;
-               break;
-       case V4L2_PIX_FMT_YUYV:
-               dmr = VNDMR_BPSM;
-               output_is_yuv = true;
-               break;
-       case V4L2_PIX_FMT_UYVY:
-               dmr = 0;
-               output_is_yuv = true;
-               break;
-       case V4L2_PIX_FMT_XRGB555:
-               dmr = VNDMR_DTMD_ARGB1555;
-               break;
-       case V4L2_PIX_FMT_RGB565:
-               dmr = 0;
-               break;
-       case V4L2_PIX_FMT_XBGR32:
-               /* Note: not supported on M1 */
-               dmr = VNDMR_EXRGB;
-               break;
-       default:
-               vin_err(vin, "Invalid pixelformat (0x%x)\n",
-                       vin->format.pixelformat);
-               return -EINVAL;
-       }
-
-       /* Always update on field change */
-       vnmc |= VNMC_VUP;
-
-       /* If input and output use the same colorspace, use bypass mode */
-       if (input_is_yuv == output_is_yuv)
-               vnmc |= VNMC_BPS;
-
-       /* Progressive or interlaced mode */
-       interrupts = progressive ? VNIE_FIE : VNIE_EFE;
-
-       /* Ack interrupts */
-       rvin_write(vin, interrupts, VNINTS_REG);
-       /* Enable interrupts */
-       rvin_write(vin, interrupts, VNIE_REG);
-       /* Start capturing */
-       rvin_write(vin, dmr, VNDMR_REG);
-       rvin_write(vin, dmr2, VNDMR2_REG);
-
-       /* Enable module */
-       rvin_write(vin, vnmc | VNMC_ME, VNMC_REG);
-
-       return 0;
-}
-
-static void rvin_disable_interrupts(struct rvin_dev *vin)
-{
-       rvin_write(vin, 0, VNIE_REG);
-}
-
-static u32 rvin_get_interrupt_status(struct rvin_dev *vin)
-{
-       return rvin_read(vin, VNINTS_REG);
-}
-
-static void rvin_ack_interrupt(struct rvin_dev *vin)
-{
-       rvin_write(vin, rvin_read(vin, VNINTS_REG), VNINTS_REG);
-}
-
-static bool rvin_capture_active(struct rvin_dev *vin)
-{
-       return rvin_read(vin, VNMS_REG) & VNMS_CA;
-}
-
-static enum v4l2_field rvin_get_active_field(struct rvin_dev *vin, u32 vnms)
-{
-       if (vin->format.field == V4L2_FIELD_ALTERNATE) {
-               /* If FS is set it's a Even field */
-               if (vnms & VNMS_FS)
-                       return V4L2_FIELD_BOTTOM;
-               return V4L2_FIELD_TOP;
-       }
-
-       return vin->format.field;
-}
-
-static void rvin_set_slot_addr(struct rvin_dev *vin, int slot, dma_addr_t addr)
-{
-       const struct rvin_video_format *fmt;
-       int offsetx, offsety;
-       dma_addr_t offset;
-
-       fmt = rvin_format_from_pixel(vin->format.pixelformat);
-
-       /*
-        * There is no HW support for composition do the beast we can
-        * by modifying the buffer offset
-        */
-       offsetx = vin->compose.left * fmt->bpp;
-       offsety = vin->compose.top * vin->format.bytesperline;
-       offset = addr + offsetx + offsety;
-
-       /*
-        * The address needs to be 128 bytes aligned. Driver should never accept
-        * settings that do not satisfy this in the first place...
-        */
-       if (WARN_ON((offsetx | offsety | offset) & HW_BUFFER_MASK))
-               return;
-
-       rvin_write(vin, offset, VNMB_REG(slot));
-}
-
-/*
- * Moves a buffer from the queue to the HW slot. If no buffer is
- * available use the scratch buffer. The scratch buffer is never
- * returned to userspace, its only function is to enable the capture
- * loop to keep running.
- */
-static void rvin_fill_hw_slot(struct rvin_dev *vin, int slot)
-{
-       struct rvin_buffer *buf;
-       struct vb2_v4l2_buffer *vbuf;
-       dma_addr_t phys_addr;
-
-       /* A already populated slot shall never be overwritten. */
-       if (WARN_ON(vin->queue_buf[slot] != NULL))
-               return;
-
-       vin_dbg(vin, "Filling HW slot: %d\n", slot);
-
-       if (list_empty(&vin->buf_list)) {
-               vin->queue_buf[slot] = NULL;
-               phys_addr = vin->scratch_phys;
-       } else {
-               /* Keep track of buffer we give to HW */
-               buf = list_entry(vin->buf_list.next, struct rvin_buffer, list);
-               vbuf = &buf->vb;
-               list_del_init(to_buf_list(vbuf));
-               vin->queue_buf[slot] = vbuf;
-
-               /* Setup DMA */
-               phys_addr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0);
-       }
-
-       rvin_set_slot_addr(vin, slot, phys_addr);
-}
-
-static int rvin_capture_start(struct rvin_dev *vin)
-{
-       int slot, ret;
-
-       for (slot = 0; slot < HW_BUFFER_NUM; slot++)
-               rvin_fill_hw_slot(vin, slot);
-
-       rvin_crop_scale_comp(vin);
-
-       ret = rvin_setup(vin);
-       if (ret)
-               return ret;
-
-       vin_dbg(vin, "Starting to capture\n");
-
-       /* Continuous Frame Capture Mode */
-       rvin_write(vin, VNFC_C_FRAME, VNFC_REG);
-
-       vin->state = RUNNING;
-
-       return 0;
-}
-
-static void rvin_capture_stop(struct rvin_dev *vin)
-{
-       /* Set continuous & single transfer off */
-       rvin_write(vin, 0, VNFC_REG);
-
-       /* Disable module */
-       rvin_write(vin, rvin_read(vin, VNMC_REG) & ~VNMC_ME, VNMC_REG);
-}
-
 /* -----------------------------------------------------------------------------
  * Crop and Scaling Gen2
  */
        const struct vin_coeff *p_prev_set = NULL;
        const struct vin_coeff *p_set = NULL;
 
-       /* Look for suitable coefficient values */
-       for (i = 0; i < ARRAY_SIZE(vin_coeff_set); i++) {
-               p_prev_set = p_set;
-               p_set = &vin_coeff_set[i];
+       /* Look for suitable coefficient values */
+       for (i = 0; i < ARRAY_SIZE(vin_coeff_set); i++) {
+               p_prev_set = p_set;
+               p_set = &vin_coeff_set[i];
+
+               if (xs < p_set->xs_value)
+                       break;
+       }
+
+       /* Use previous value if its XS value is closer */
+       if (p_prev_set && p_set &&
+           xs - p_prev_set->xs_value < p_set->xs_value - xs)
+               p_set = p_prev_set;
+
+       /* Set coefficient registers */
+       rvin_write(vin, p_set->coeff_set[0], VNC1A_REG);
+       rvin_write(vin, p_set->coeff_set[1], VNC1B_REG);
+       rvin_write(vin, p_set->coeff_set[2], VNC1C_REG);
+
+       rvin_write(vin, p_set->coeff_set[3], VNC2A_REG);
+       rvin_write(vin, p_set->coeff_set[4], VNC2B_REG);
+       rvin_write(vin, p_set->coeff_set[5], VNC2C_REG);
+
+       rvin_write(vin, p_set->coeff_set[6], VNC3A_REG);
+       rvin_write(vin, p_set->coeff_set[7], VNC3B_REG);
+       rvin_write(vin, p_set->coeff_set[8], VNC3C_REG);
+
+       rvin_write(vin, p_set->coeff_set[9], VNC4A_REG);
+       rvin_write(vin, p_set->coeff_set[10], VNC4B_REG);
+       rvin_write(vin, p_set->coeff_set[11], VNC4C_REG);
+
+       rvin_write(vin, p_set->coeff_set[12], VNC5A_REG);
+       rvin_write(vin, p_set->coeff_set[13], VNC5B_REG);
+       rvin_write(vin, p_set->coeff_set[14], VNC5C_REG);
+
+       rvin_write(vin, p_set->coeff_set[15], VNC6A_REG);
+       rvin_write(vin, p_set->coeff_set[16], VNC6B_REG);
+       rvin_write(vin, p_set->coeff_set[17], VNC6C_REG);
+
+       rvin_write(vin, p_set->coeff_set[18], VNC7A_REG);
+       rvin_write(vin, p_set->coeff_set[19], VNC7B_REG);
+       rvin_write(vin, p_set->coeff_set[20], VNC7C_REG);
+
+       rvin_write(vin, p_set->coeff_set[21], VNC8A_REG);
+       rvin_write(vin, p_set->coeff_set[22], VNC8B_REG);
+       rvin_write(vin, p_set->coeff_set[23], VNC8C_REG);
+}
+
+void rvin_crop_scale_comp(struct rvin_dev *vin)
+{
+       u32 xs, ys;
+
+       /* Set Start/End Pixel/Line Pre-Clip */
+       rvin_write(vin, vin->crop.left, VNSPPRC_REG);
+       rvin_write(vin, vin->crop.left + vin->crop.width - 1, VNEPPRC_REG);
+       switch (vin->format.field) {
+       case V4L2_FIELD_INTERLACED:
+       case V4L2_FIELD_INTERLACED_TB:
+       case V4L2_FIELD_INTERLACED_BT:
+               rvin_write(vin, vin->crop.top / 2, VNSLPRC_REG);
+               rvin_write(vin, (vin->crop.top + vin->crop.height) / 2 - 1,
+                          VNELPRC_REG);
+               break;
+       default:
+               rvin_write(vin, vin->crop.top, VNSLPRC_REG);
+               rvin_write(vin, vin->crop.top + vin->crop.height - 1,
+                          VNELPRC_REG);
+               break;
+       }
+
+       /* Set scaling coefficient */
+       ys = 0;
+       if (vin->crop.height != vin->compose.height)
+               ys = (4096 * vin->crop.height) / vin->compose.height;
+       rvin_write(vin, ys, VNYS_REG);
+
+       xs = 0;
+       if (vin->crop.width != vin->compose.width)
+               xs = (4096 * vin->crop.width) / vin->compose.width;
+
+       /* Horizontal upscaling is up to double size */
+       if (xs > 0 && xs < 2048)
+               xs = 2048;
+
+       rvin_write(vin, xs, VNXS_REG);
+
+       /* Horizontal upscaling is done out by scaling down from double size */
+       if (xs < 4096)
+               xs *= 2;
+
+       rvin_set_coeff(vin, xs);
+
+       /* Set Start/End Pixel/Line Post-Clip */
+       rvin_write(vin, 0, VNSPPOC_REG);
+       rvin_write(vin, 0, VNSLPOC_REG);
+       rvin_write(vin, vin->format.width - 1, VNEPPOC_REG);
+       switch (vin->format.field) {
+       case V4L2_FIELD_INTERLACED:
+       case V4L2_FIELD_INTERLACED_TB:
+       case V4L2_FIELD_INTERLACED_BT:
+               rvin_write(vin, vin->format.height / 2 - 1, VNELPOC_REG);
+               break;
+       default:
+               rvin_write(vin, vin->format.height - 1, VNELPOC_REG);
+               break;
+       }
+
+       if (vin->format.pixelformat == V4L2_PIX_FMT_NV16)
+               rvin_write(vin, ALIGN(vin->format.width, 0x20), VNIS_REG);
+       else
+               rvin_write(vin, ALIGN(vin->format.width, 0x10), VNIS_REG);
+
+       vin_dbg(vin,
+               "Pre-Clip: %ux%u@%u:%u YS: %d XS: %d Post-Clip: %ux%u@%u:%u\n",
+               vin->crop.width, vin->crop.height, vin->crop.left,
+               vin->crop.top, ys, xs, vin->format.width, vin->format.height,
+               0, 0);
+}
+
+void rvin_scale_try(struct rvin_dev *vin, struct v4l2_pix_format *pix,
+                   u32 width, u32 height)
+{
+       /* All VIN channels on Gen2 have scalers */
+       pix->width = width;
+       pix->height = height;
+}
+
+/* -----------------------------------------------------------------------------
+ * Hardware setup
+ */
+
+static int rvin_setup(struct rvin_dev *vin)
+{
+       u32 vnmc, dmr, dmr2, interrupts;
+       v4l2_std_id std;
+       bool progressive = false, output_is_yuv = false, input_is_yuv = false;
+
+       switch (vin->format.field) {
+       case V4L2_FIELD_TOP:
+               vnmc = VNMC_IM_ODD;
+               break;
+       case V4L2_FIELD_BOTTOM:
+               vnmc = VNMC_IM_EVEN;
+               break;
+       case V4L2_FIELD_INTERLACED:
+               /* Default to TB */
+               vnmc = VNMC_IM_FULL;
+               /* Use BT if video standard can be read and is 60 Hz format */
+               if (!v4l2_subdev_call(vin_to_source(vin), video, g_std, &std)) {
+                       if (std & V4L2_STD_525_60)
+                               vnmc = VNMC_IM_FULL | VNMC_FOC;
+               }
+               break;
+       case V4L2_FIELD_INTERLACED_TB:
+               vnmc = VNMC_IM_FULL;
+               break;
+       case V4L2_FIELD_INTERLACED_BT:
+               vnmc = VNMC_IM_FULL | VNMC_FOC;
+               break;
+       case V4L2_FIELD_ALTERNATE:
+       case V4L2_FIELD_NONE:
+               vnmc = VNMC_IM_ODD_EVEN;
+               progressive = true;
+               break;
+       default:
+               vnmc = VNMC_IM_ODD;
+               break;
+       }
+
+       /*
+        * Input interface
+        */
+       switch (vin->digital->code) {
+       case MEDIA_BUS_FMT_YUYV8_1X16:
+               /* BT.601/BT.1358 16bit YCbCr422 */
+               vnmc |= VNMC_INF_YUV16;
+               input_is_yuv = true;
+               break;
+       case MEDIA_BUS_FMT_UYVY8_2X8:
+               /* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */
+               vnmc |= vin->digital->mbus_cfg.type == V4L2_MBUS_BT656 ?
+                       VNMC_INF_YUV8_BT656 : VNMC_INF_YUV8_BT601;
+               input_is_yuv = true;
+               break;
+       case MEDIA_BUS_FMT_RGB888_1X24:
+               vnmc |= VNMC_INF_RGB888;
+               break;
+       case MEDIA_BUS_FMT_UYVY10_2X10:
+               /* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */
+               vnmc |= vin->digital->mbus_cfg.type == V4L2_MBUS_BT656 ?
+                       VNMC_INF_YUV10_BT656 : VNMC_INF_YUV10_BT601;
+               input_is_yuv = true;
+               break;
+       default:
+               break;
+       }
+
+       /* Enable VSYNC Field Toogle mode after one VSYNC input */
+       dmr2 = VNDMR2_FTEV | VNDMR2_VLV(1);
+
+       /* Hsync Signal Polarity Select */
+       if (!(vin->digital->mbus_cfg.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW))
+               dmr2 |= VNDMR2_HPS;
+
+       /* Vsync Signal Polarity Select */
+       if (!(vin->digital->mbus_cfg.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW))
+               dmr2 |= VNDMR2_VPS;
 
-               if (xs < p_set->xs_value)
-                       break;
+       /*
+        * Output format
+        */
+       switch (vin->format.pixelformat) {
+       case V4L2_PIX_FMT_NV16:
+               rvin_write(vin,
+                          ALIGN(vin->format.width * vin->format.height, 0x80),
+                          VNUVAOF_REG);
+               dmr = VNDMR_DTMD_YCSEP;
+               output_is_yuv = true;
+               break;
+       case V4L2_PIX_FMT_YUYV:
+               dmr = VNDMR_BPSM;
+               output_is_yuv = true;
+               break;
+       case V4L2_PIX_FMT_UYVY:
+               dmr = 0;
+               output_is_yuv = true;
+               break;
+       case V4L2_PIX_FMT_XRGB555:
+               dmr = VNDMR_DTMD_ARGB1555;
+               break;
+       case V4L2_PIX_FMT_RGB565:
+               dmr = 0;
+               break;
+       case V4L2_PIX_FMT_XBGR32:
+               /* Note: not supported on M1 */
+               dmr = VNDMR_EXRGB;
+               break;
+       default:
+               vin_err(vin, "Invalid pixelformat (0x%x)\n",
+                       vin->format.pixelformat);
+               return -EINVAL;
        }
 
-       /* Use previous value if its XS value is closer */
-       if (p_prev_set && p_set &&
-           xs - p_prev_set->xs_value < p_set->xs_value - xs)
-               p_set = p_prev_set;
+       /* Always update on field change */
+       vnmc |= VNMC_VUP;
 
-       /* Set coefficient registers */
-       rvin_write(vin, p_set->coeff_set[0], VNC1A_REG);
-       rvin_write(vin, p_set->coeff_set[1], VNC1B_REG);
-       rvin_write(vin, p_set->coeff_set[2], VNC1C_REG);
+       /* If input and output use the same colorspace, use bypass mode */
+       if (input_is_yuv == output_is_yuv)
+               vnmc |= VNMC_BPS;
 
-       rvin_write(vin, p_set->coeff_set[3], VNC2A_REG);
-       rvin_write(vin, p_set->coeff_set[4], VNC2B_REG);
-       rvin_write(vin, p_set->coeff_set[5], VNC2C_REG);
+       /* Progressive or interlaced mode */
+       interrupts = progressive ? VNIE_FIE : VNIE_EFE;
 
-       rvin_write(vin, p_set->coeff_set[6], VNC3A_REG);
-       rvin_write(vin, p_set->coeff_set[7], VNC3B_REG);
-       rvin_write(vin, p_set->coeff_set[8], VNC3C_REG);
+       /* Ack interrupts */
+       rvin_write(vin, interrupts, VNINTS_REG);
+       /* Enable interrupts */
+       rvin_write(vin, interrupts, VNIE_REG);
+       /* Start capturing */
+       rvin_write(vin, dmr, VNDMR_REG);
+       rvin_write(vin, dmr2, VNDMR2_REG);
 
-       rvin_write(vin, p_set->coeff_set[9], VNC4A_REG);
-       rvin_write(vin, p_set->coeff_set[10], VNC4B_REG);
-       rvin_write(vin, p_set->coeff_set[11], VNC4C_REG);
+       /* Enable module */
+       rvin_write(vin, vnmc | VNMC_ME, VNMC_REG);
 
-       rvin_write(vin, p_set->coeff_set[12], VNC5A_REG);
-       rvin_write(vin, p_set->coeff_set[13], VNC5B_REG);
-       rvin_write(vin, p_set->coeff_set[14], VNC5C_REG);
+       return 0;
+}
 
-       rvin_write(vin, p_set->coeff_set[15], VNC6A_REG);
-       rvin_write(vin, p_set->coeff_set[16], VNC6B_REG);
-       rvin_write(vin, p_set->coeff_set[17], VNC6C_REG);
+static void rvin_disable_interrupts(struct rvin_dev *vin)
+{
+       rvin_write(vin, 0, VNIE_REG);
+}
 
-       rvin_write(vin, p_set->coeff_set[18], VNC7A_REG);
-       rvin_write(vin, p_set->coeff_set[19], VNC7B_REG);
-       rvin_write(vin, p_set->coeff_set[20], VNC7C_REG);
+static u32 rvin_get_interrupt_status(struct rvin_dev *vin)
+{
+       return rvin_read(vin, VNINTS_REG);
+}
 
-       rvin_write(vin, p_set->coeff_set[21], VNC8A_REG);
-       rvin_write(vin, p_set->coeff_set[22], VNC8B_REG);
-       rvin_write(vin, p_set->coeff_set[23], VNC8C_REG);
+static void rvin_ack_interrupt(struct rvin_dev *vin)
+{
+       rvin_write(vin, rvin_read(vin, VNINTS_REG), VNINTS_REG);
 }
 
-void rvin_crop_scale_comp(struct rvin_dev *vin)
+static bool rvin_capture_active(struct rvin_dev *vin)
 {
-       u32 xs, ys;
+       return rvin_read(vin, VNMS_REG) & VNMS_CA;
+}
 
-       /* Set Start/End Pixel/Line Pre-Clip */
-       rvin_write(vin, vin->crop.left, VNSPPRC_REG);
-       rvin_write(vin, vin->crop.left + vin->crop.width - 1, VNEPPRC_REG);
-       switch (vin->format.field) {
-       case V4L2_FIELD_INTERLACED:
-       case V4L2_FIELD_INTERLACED_TB:
-       case V4L2_FIELD_INTERLACED_BT:
-               rvin_write(vin, vin->crop.top / 2, VNSLPRC_REG);
-               rvin_write(vin, (vin->crop.top + vin->crop.height) / 2 - 1,
-                          VNELPRC_REG);
-               break;
-       default:
-               rvin_write(vin, vin->crop.top, VNSLPRC_REG);
-               rvin_write(vin, vin->crop.top + vin->crop.height - 1,
-                          VNELPRC_REG);
-               break;
+static enum v4l2_field rvin_get_active_field(struct rvin_dev *vin, u32 vnms)
+{
+       if (vin->format.field == V4L2_FIELD_ALTERNATE) {
+               /* If FS is set it's a Even field */
+               if (vnms & VNMS_FS)
+                       return V4L2_FIELD_BOTTOM;
+               return V4L2_FIELD_TOP;
        }
 
-       /* Set scaling coefficient */
-       ys = 0;
-       if (vin->crop.height != vin->compose.height)
-               ys = (4096 * vin->crop.height) / vin->compose.height;
-       rvin_write(vin, ys, VNYS_REG);
+       return vin->format.field;
+}
 
-       xs = 0;
-       if (vin->crop.width != vin->compose.width)
-               xs = (4096 * vin->crop.width) / vin->compose.width;
+static void rvin_set_slot_addr(struct rvin_dev *vin, int slot, dma_addr_t addr)
+{
+       const struct rvin_video_format *fmt;
+       int offsetx, offsety;
+       dma_addr_t offset;
 
-       /* Horizontal upscaling is up to double size */
-       if (xs > 0 && xs < 2048)
-               xs = 2048;
+       fmt = rvin_format_from_pixel(vin->format.pixelformat);
 
-       rvin_write(vin, xs, VNXS_REG);
+       /*
+        * There is no HW support for composition do the beast we can
+        * by modifying the buffer offset
+        */
+       offsetx = vin->compose.left * fmt->bpp;
+       offsety = vin->compose.top * vin->format.bytesperline;
+       offset = addr + offsetx + offsety;
 
-       /* Horizontal upscaling is done out by scaling down from double size */
-       if (xs < 4096)
-               xs *= 2;
+       /*
+        * The address needs to be 128 bytes aligned. Driver should never accept
+        * settings that do not satisfy this in the first place...
+        */
+       if (WARN_ON((offsetx | offsety | offset) & HW_BUFFER_MASK))
+               return;
 
-       rvin_set_coeff(vin, xs);
+       rvin_write(vin, offset, VNMB_REG(slot));
+}
 
-       /* Set Start/End Pixel/Line Post-Clip */
-       rvin_write(vin, 0, VNSPPOC_REG);
-       rvin_write(vin, 0, VNSLPOC_REG);
-       rvin_write(vin, vin->format.width - 1, VNEPPOC_REG);
-       switch (vin->format.field) {
-       case V4L2_FIELD_INTERLACED:
-       case V4L2_FIELD_INTERLACED_TB:
-       case V4L2_FIELD_INTERLACED_BT:
-               rvin_write(vin, vin->format.height / 2 - 1, VNELPOC_REG);
-               break;
-       default:
-               rvin_write(vin, vin->format.height - 1, VNELPOC_REG);
-               break;
+/*
+ * Moves a buffer from the queue to the HW slot. If no buffer is
+ * available use the scratch buffer. The scratch buffer is never
+ * returned to userspace, its only function is to enable the capture
+ * loop to keep running.
+ */
+static void rvin_fill_hw_slot(struct rvin_dev *vin, int slot)
+{
+       struct rvin_buffer *buf;
+       struct vb2_v4l2_buffer *vbuf;
+       dma_addr_t phys_addr;
+
+       /* A already populated slot shall never be overwritten. */
+       if (WARN_ON(vin->queue_buf[slot] != NULL))
+               return;
+
+       vin_dbg(vin, "Filling HW slot: %d\n", slot);
+
+       if (list_empty(&vin->buf_list)) {
+               vin->queue_buf[slot] = NULL;
+               phys_addr = vin->scratch_phys;
+       } else {
+               /* Keep track of buffer we give to HW */
+               buf = list_entry(vin->buf_list.next, struct rvin_buffer, list);
+               vbuf = &buf->vb;
+               list_del_init(to_buf_list(vbuf));
+               vin->queue_buf[slot] = vbuf;
+
+               /* Setup DMA */
+               phys_addr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0);
        }
 
-       if (vin->format.pixelformat == V4L2_PIX_FMT_NV16)
-               rvin_write(vin, ALIGN(vin->format.width, 0x20), VNIS_REG);
-       else
-               rvin_write(vin, ALIGN(vin->format.width, 0x10), VNIS_REG);
+       rvin_set_slot_addr(vin, slot, phys_addr);
+}
 
-       vin_dbg(vin,
-               "Pre-Clip: %ux%u@%u:%u YS: %d XS: %d Post-Clip: %ux%u@%u:%u\n",
-               vin->crop.width, vin->crop.height, vin->crop.left,
-               vin->crop.top, ys, xs, vin->format.width, vin->format.height,
-               0, 0);
+static int rvin_capture_start(struct rvin_dev *vin)
+{
+       int slot, ret;
+
+       for (slot = 0; slot < HW_BUFFER_NUM; slot++)
+               rvin_fill_hw_slot(vin, slot);
+
+       rvin_crop_scale_comp(vin);
+
+       ret = rvin_setup(vin);
+       if (ret)
+               return ret;
+
+       vin_dbg(vin, "Starting to capture\n");
+
+       /* Continuous Frame Capture Mode */
+       rvin_write(vin, VNFC_C_FRAME, VNFC_REG);
+
+       vin->state = RUNNING;
+
+       return 0;
 }
 
-void rvin_scale_try(struct rvin_dev *vin, struct v4l2_pix_format *pix,
-                   u32 width, u32 height)
+static void rvin_capture_stop(struct rvin_dev *vin)
 {
-       /* All VIN channels on Gen2 have scalers */
-       pix->width = width;
-       pix->height = height;
+       /* Set continuous & single transfer off */
+       rvin_write(vin, 0, VNFC_REG);
+
+       /* Disable module */
+       rvin_write(vin, rvin_read(vin, VNMC_REG) & ~VNMC_ME, VNMC_REG);
 }
 
 /* -----------------------------------------------------------------------------