Hi Niklas,

Thank you for the patch.

On Friday, 8 December 2017 03:08:22 EET Niklas Söderlund wrote:
> In preparation of refactoring the scaling code move the code regarding
> scaling to to the top of the file to avoid the need to add forward
> declarations. No code is changed in this commit only whole functions
> moved inside the same file.
> 
> Signed-off-by: Niklas Söderlund <niklas.soderlund+rene...@ragnatech.se>
> Reviewed-by: Hans Verkuil <hans.verk...@cisco.com>

The patch is awful to review from the e-mail as git has done a very bad job 
formatting it. If you have to resend it, use --patience for this patch, it 
will help a lot.

Reviewed-by: Laurent Pinchart <laurent.pinch...@ideasonboard.com>

> ---
>  drivers/media/platform/rcar-vin/rcar-dma.c | 806 ++++++++++++++------------
>  1 file changed, 405 insertions(+), 401 deletions(-)
> 
> diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c
> b/drivers/media/platform/rcar-vin/rcar-dma.c index
> d701b52d198243b5..a7cda3922cb74baa 100644
> --- a/drivers/media/platform/rcar-vin/rcar-dma.c
> +++ b/drivers/media/platform/rcar-vin/rcar-dma.c
> @@ -138,305 +138,6 @@ static u32 rvin_read(struct rvin_dev *vin, u32 offset)
> 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:
> -             if (vin->continuous) {
> -                     vnmc = VNMC_IM_ODD_EVEN;
> -                     progressive = true;
> -             } else {
> -                     vnmc = VNMC_IM_ODD;
> -             }
> -             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 int rvin_get_active_slot(struct rvin_dev *vin, u32 vnms)
> -{
> -     if (vin->continuous)
> -             return (vnms & VNMS_FBS_MASK) >> VNMS_FBS_SHIFT;
> -
> -     return 0;
> -}
> -
> -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 slots */
> -static bool rvin_fill_hw_slot(struct rvin_dev *vin, int slot)
> -{
> -     struct rvin_buffer *buf;
> -     struct vb2_v4l2_buffer *vbuf;
> -     dma_addr_t phys_addr_top;
> -
> -     if (vin->queue_buf[slot] != NULL)
> -             return true;
> -
> -     if (list_empty(&vin->buf_list))
> -             return false;
> -
> -     vin_dbg(vin, "Filling HW slot: %d\n", slot);
> -
> -     /* 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_top = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0);
> -     rvin_set_slot_addr(vin, slot, phys_addr_top);
> -
> -     return true;
> -}
> -
> -static bool rvin_fill_hw(struct rvin_dev *vin)
> -{
> -     int slot, limit;
> -
> -     limit = vin->continuous ? HW_BUFFER_NUM : 1;
> -
> -     for (slot = 0; slot < limit; slot++)
> -             if (!rvin_fill_hw_slot(vin, slot))
> -                     return false;
> -     return true;
> -}
> -
> -static void rvin_capture_on(struct rvin_dev *vin)
> -{
> -     vin_dbg(vin, "Capture on in %s mode\n",
> -             vin->continuous ? "continuous" : "single");
> -
> -     if (vin->continuous)
> -             /* Continuous Frame Capture Mode */
> -             rvin_write(vin, VNFC_C_FRAME, VNFC_REG);
> -     else
> -             /* Single Frame Capture Mode */
> -             rvin_write(vin, VNFC_S_FRAME, VNFC_REG);
> -}
> -
> -static int rvin_capture_start(struct rvin_dev *vin)
> -{
> -     struct rvin_buffer *buf, *node;
> -     int bufs, ret;
> -
> -     /* Count number of free buffers */
> -     bufs = 0;
> -     list_for_each_entry_safe(buf, node, &vin->buf_list, list)
> -             bufs++;
> -
> -     /* Continuous capture requires more buffers then there are HW slots */
> -     vin->continuous = bufs > HW_BUFFER_NUM;
> -
> -     if (!rvin_fill_hw(vin)) {
> -             vin_err(vin, "HW not ready to start, not enough buffers 
> available\n");
> -             return -EINVAL;
> -     }
> -
> -     rvin_crop_scale_comp(vin);
> -
> -     ret = rvin_setup(vin);
> -     if (ret)
> -             return ret;
> -
> -     rvin_capture_on(vin);
> -
> -     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
>   */
> @@ -757,139 +458,442 @@ static const struct vin_coeff vin_coeff_set[] = {
>                         0x0370e83b, 0x0310d439, 0x03a0f83d,
>                         0x0370e83c, 0x0300d438, 0x03b0fc3c },
>       }
> -};
> +};
> +
> +static void rvin_set_coeff(struct rvin_dev *vin, unsigned short xs)
> +{
> +     int i;
> +     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];
> +
> +             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:
> +             if (vin->continuous) {
> +                     vnmc = VNMC_IM_ODD_EVEN;
> +                     progressive = true;
> +             } else {
> +                     vnmc = VNMC_IM_ODD;
> +             }
> +             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;
> +     }
> 
> -static void rvin_set_coeff(struct rvin_dev *vin, unsigned short xs)
> +     /* 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)
>  {
> -     int i;
> -     const struct vin_coeff *p_prev_set = NULL;
> -     const struct vin_coeff *p_set = NULL;
> +     rvin_write(vin, 0, VNIE_REG);
> +}
> 
> -     /* 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];
> +static u32 rvin_get_interrupt_status(struct rvin_dev *vin)
> +{
> +     return rvin_read(vin, VNINTS_REG);
> +}
> 
> -             if (xs < p_set->xs_value)
> -                     break;
> +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 int rvin_get_active_slot(struct rvin_dev *vin, u32 vnms)
> +{
> +     if (vin->continuous)
> +             return (vnms & VNMS_FBS_MASK) >> VNMS_FBS_SHIFT;
> +
> +     return 0;
> +}
> +
> +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;
>       }
> 
> -     /* 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;
> +     return vin->format.field;
> +}
> 
> -     /* 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);
> +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;
> 
> -     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);
> +     fmt = rvin_format_from_pixel(vin->format.pixelformat);
> 
> -     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);
> +     /*
> +      * 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;
> 
> -     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);
> +     /*
> +      * 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, 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, offset, VNMB_REG(slot));
> +}
> 
> -     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);
> +/* Moves a buffer from the queue to the HW slots */
> +static bool rvin_fill_hw_slot(struct rvin_dev *vin, int slot)
> +{
> +     struct rvin_buffer *buf;
> +     struct vb2_v4l2_buffer *vbuf;
> +     dma_addr_t phys_addr_top;
> 
> -     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);
> +     if (vin->queue_buf[slot] != NULL)
> +             return true;
> 
> -     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);
> +     if (list_empty(&vin->buf_list))
> +             return false;
> +
> +     vin_dbg(vin, "Filling HW slot: %d\n", slot);
> +
> +     /* 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_top = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0);
> +     rvin_set_slot_addr(vin, slot, phys_addr_top);
> +
> +     return true;
>  }
> 
> -void rvin_crop_scale_comp(struct rvin_dev *vin)
> +static bool rvin_fill_hw(struct rvin_dev *vin)
>  {
> -     u32 xs, ys;
> +     int slot, limit;
> 
> -     /* 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;
> -     }
> +     limit = vin->continuous ? HW_BUFFER_NUM : 1;
> 
> -     /* 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);
> +     for (slot = 0; slot < limit; slot++)
> +             if (!rvin_fill_hw_slot(vin, slot))
> +                     return false;
> +     return true;
> +}
> 
> -     xs = 0;
> -     if (vin->crop.width != vin->compose.width)
> -             xs = (4096 * vin->crop.width) / vin->compose.width;
> +static void rvin_capture_on(struct rvin_dev *vin)
> +{
> +     vin_dbg(vin, "Capture on in %s mode\n",
> +             vin->continuous ? "continuous" : "single");
> 
> -     /* Horizontal upscaling is up to double size */
> -     if (xs > 0 && xs < 2048)
> -             xs = 2048;
> +     if (vin->continuous)
> +             /* Continuous Frame Capture Mode */
> +             rvin_write(vin, VNFC_C_FRAME, VNFC_REG);
> +     else
> +             /* Single Frame Capture Mode */
> +             rvin_write(vin, VNFC_S_FRAME, VNFC_REG);
> +}
> 
> -     rvin_write(vin, xs, VNXS_REG);
> +static int rvin_capture_start(struct rvin_dev *vin)
> +{
> +     struct rvin_buffer *buf, *node;
> +     int bufs, ret;
> 
> -     /* Horizontal upscaling is done out by scaling down from double size */
> -     if (xs < 4096)
> -             xs *= 2;
> +     /* Count number of free buffers */
> +     bufs = 0;
> +     list_for_each_entry_safe(buf, node, &vin->buf_list, list)
> +             bufs++;
> 
> -     rvin_set_coeff(vin, xs);
> +     /* Continuous capture requires more buffers then there are HW slots */
> +     vin->continuous = bufs > HW_BUFFER_NUM;
> 
> -     /* 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 (!rvin_fill_hw(vin)) {
> +             vin_err(vin, "HW not ready to start, not enough buffers 
> available\n");
> +             return -EINVAL;
>       }
> 
> -     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_crop_scale_comp(vin);
> 
> -     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);
> +     ret = rvin_setup(vin);
> +     if (ret)
> +             return ret;
> +
> +     rvin_capture_on(vin);
> +
> +     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);
>  }
> 
>  /*
> ---------------------------------------------------------------------------
> --


-- 
Regards,

Laurent Pinchart

Reply via email to