| From b4621d5af324ee9aeaaf6a8f0a439018ee64d56b Mon Sep 17 00:00:00 2001 |
| From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= |
| <niklas.soderlund+renesas@ragnatech.se> |
| Date: Sat, 14 Apr 2018 07:57:02 -0400 |
| Subject: [PATCH 1288/1795] media: rcar-vin: move functions regarding scaling |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| 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+renesas@ragnatech.se> |
| Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> |
| Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com> |
| Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com> |
| (cherry picked from commit 0f4b33783389c7daaa67f9b0b7164a51fabe3644) |
| Signed-off-by: Simon Horman <horms+renesas@verge.net.au> |
| Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> |
| --- |
| drivers/media/platform/rcar-vin/rcar-dma.c | 718 +++++++++++---------- |
| 1 file changed, 361 insertions(+), 357 deletions(-) |
| |
| diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c |
| index 2aae3ca54eab..23120901b0a0 100644 |
| --- a/drivers/media/platform/rcar-vin/rcar-dma.c |
| +++ b/drivers/media/platform/rcar-vin/rcar-dma.c |
| @@ -138,267 +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: |
| - 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 |
| */ |
| @@ -727,131 +466,396 @@ static void rvin_set_coeff(struct rvin_dev *vin, unsigned short xs) |
| 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); |
| } |
| |
| /* ----------------------------------------------------------------------------- |
| -- |
| 2.19.0 |
| |