diff options
Diffstat (limited to 'drivers/media/platform/renesas/rcar-vin/rcar-dma.c')
-rw-r--r-- | drivers/media/platform/renesas/rcar-vin/rcar-dma.c | 259 |
1 files changed, 67 insertions, 192 deletions
diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c index 8de871240440..b619d1436a41 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c @@ -2,18 +2,18 @@ /* * Driver for Renesas R-Car VIN * + * Copyright (C) 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se> * Copyright (C) 2016 Renesas Electronics Corp. * Copyright (C) 2011-2013 Renesas Solutions Corp. * Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com> * Copyright (C) 2008 Magnus Damm - * - * Based on the soc-camera rcar_vin driver */ #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/pm_runtime.h> +#include <media/v4l2-event.h> #include <media/videobuf2-dma-contig.h> #include "rcar-vin.h" @@ -94,6 +94,7 @@ #define VNMC_INF_YUV16 (5 << 16) #define VNMC_INF_RGB888 (6 << 16) #define VNMC_INF_RGB666 (7 << 16) +#define VNMC_EXINF_RAW8 (1 << 12) /* Gen4 specific */ #define VNMC_VUP (1 << 10) #define VNMC_IM_ODD (0 << 3) #define VNMC_IM_ODD_EVEN (1 << 3) @@ -114,11 +115,16 @@ #define VNFC_S_FRAME (1 << 0) /* Video n Interrupt Enable Register bits */ -#define VNIE_FIE (1 << 4) -#define VNIE_EFE (1 << 1) +#define VNIE_VFE BIT(17) +#define VNIE_VRE BIT(16) +#define VNIE_FIE BIT(4) +#define VNIE_EFE BIT(1) /* Video n Interrupt Status Register bits */ -#define VNINTS_FIS (1 << 4) +#define VNINTS_VFS BIT(17) +#define VNINTS_VRS BIT(16) +#define VNINTS_FIS BIT(4) +#define VNINTS_EFS BIT(1) /* Video n Data Mode Register bits */ #define VNDMR_A8BIT(n) (((n) & 0xff) << 24) @@ -554,17 +560,12 @@ static void rvin_set_coeff(struct rvin_dev *vin, unsigned short xs) void rvin_scaler_gen2(struct rvin_dev *vin) { - unsigned int crop_height; u32 xs, ys; /* Set scaling coefficient */ - crop_height = vin->crop.height; - if (V4L2_FIELD_HAS_BOTH(vin->format.field)) - crop_height *= 2; - ys = 0; - if (crop_height != vin->compose.height) - ys = (4096 * crop_height) / vin->compose.height; + if (vin->crop.height != vin->compose.height) + ys = (4096 * vin->crop.height) / vin->compose.height; rvin_write(vin, ys, VNYS_REG); xs = 0; @@ -642,8 +643,6 @@ void rvin_scaler_gen3(struct rvin_dev *vin) case V4L2_FIELD_INTERLACED_TB: case V4L2_FIELD_INTERLACED_BT: case V4L2_FIELD_INTERLACED: - case V4L2_FIELD_SEQ_TB: - case V4L2_FIELD_SEQ_BT: clip_size |= vin->compose.height / 2; break; default: @@ -679,22 +678,6 @@ void rvin_crop_scale_comp(struct rvin_dev *vin) fmt = rvin_format_from_pixel(vin, vin->format.pixelformat); stride = vin->format.bytesperline / fmt->bpp; - - /* For RAW8 format bpp is 1, but the hardware process RAW8 - * format in 2 pixel unit hence configure VNIS_REG as stride / 2. - */ - switch (vin->format.pixelformat) { - case V4L2_PIX_FMT_SBGGR8: - case V4L2_PIX_FMT_SGBRG8: - case V4L2_PIX_FMT_SGRBG8: - case V4L2_PIX_FMT_SRGGB8: - case V4L2_PIX_FMT_GREY: - stride /= 2; - break; - default: - break; - } - rvin_write(vin, stride, VNIS_REG); } @@ -717,9 +700,6 @@ static int rvin_setup(struct rvin_dev *vin) 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 (!vin->info->use_mc && vin->std & V4L2_STD_525_60) - vnmc = VNMC_IM_FULL | VNMC_FOC; break; case V4L2_FIELD_INTERLACED_TB: vnmc = VNMC_IM_FULL; @@ -727,8 +707,6 @@ static int rvin_setup(struct rvin_dev *vin) case V4L2_FIELD_INTERLACED_BT: vnmc = VNMC_IM_FULL | VNMC_FOC; break; - case V4L2_FIELD_SEQ_TB: - case V4L2_FIELD_SEQ_BT: case V4L2_FIELD_NONE: case V4L2_FIELD_ALTERNATE: vnmc = VNMC_IM_ODD_EVEN; @@ -791,6 +769,8 @@ static int rvin_setup(struct rvin_dev *vin) case MEDIA_BUS_FMT_SRGGB8_1X8: case MEDIA_BUS_FMT_Y8_1X8: vnmc |= VNMC_INF_RAW8; + if (vin->info->model == RCAR_GEN4) + vnmc |= VNMC_EXINF_RAW8; break; case MEDIA_BUS_FMT_SBGGR10_1X10: case MEDIA_BUS_FMT_SGBRG10_1X10: @@ -802,31 +782,8 @@ static int rvin_setup(struct rvin_dev *vin) break; } - /* Make sure input interface and input format is valid. */ - if (vin->info->model == RCAR_GEN3) { - switch (vnmc & VNMC_INF_MASK) { - case VNMC_INF_YUV8_BT656: - case VNMC_INF_YUV10_BT656: - case VNMC_INF_YUV16: - case VNMC_INF_RGB666: - if (vin->is_csi) { - vin_err(vin, "Invalid setting in MIPI CSI2\n"); - return -EINVAL; - } - break; - case VNMC_INF_RAW8: - if (!vin->is_csi) { - vin_err(vin, "Invalid setting in Digital Pins\n"); - return -EINVAL; - } - break; - default: - break; - } - } - /* Enable VSYNC Field Toggle mode after one VSYNC input */ - if (vin->info->model == RCAR_GEN3) + if (vin->info->model == RCAR_GEN3 || vin->info->model == RCAR_GEN4) dmr2 = VNDMR2_FTEV; else dmr2 = VNDMR2_FTEV | VNDMR2_VLV(1); @@ -910,7 +867,7 @@ static int rvin_setup(struct rvin_dev *vin) case V4L2_PIX_FMT_SGBRG10: case V4L2_PIX_FMT_SGRBG10: case V4L2_PIX_FMT_SRGGB10: - dmr = VNDMR_RMODE_RAW10 | VNDMR_YC_THR; + dmr = VNDMR_RMODE_RAW10; break; default: vin_err(vin, "Invalid pixelformat (0x%x)\n", @@ -926,7 +883,7 @@ static int rvin_setup(struct rvin_dev *vin) if (input_is_yuv == output_is_yuv) vnmc |= VNMC_BPS; - if (vin->info->model == RCAR_GEN3) { + if (vin->info->model == RCAR_GEN3 || vin->info->model == RCAR_GEN4) { /* Select between CSI-2 and parallel input */ if (vin->is_csi) vnmc &= ~VNMC_DPINE; @@ -937,6 +894,8 @@ static int rvin_setup(struct rvin_dev *vin) /* Progressive or interlaced mode */ interrupts = progressive ? VNIE_FIE : VNIE_EFE; + /* Enable VSYNC Rising Edge Detection. */ + interrupts |= VNIE_VRE; /* Ack interrupts */ rvin_write(vin, interrupts, VNINTS_REG); @@ -952,21 +911,6 @@ static int rvin_setup(struct rvin_dev *vin) 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; @@ -1021,33 +965,13 @@ 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; - int prev; /* A already populated slot shall never be overwritten. */ if (WARN_ON(vin->buf_hw[slot].buffer)) return; - prev = (slot == 0 ? HW_BUFFER_NUM : slot) - 1; - - if (vin->buf_hw[prev].type == HALF_TOP) { - vbuf = vin->buf_hw[prev].buffer; - vin->buf_hw[slot].buffer = vbuf; - vin->buf_hw[slot].type = HALF_BOTTOM; - switch (vin->format.pixelformat) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV16: - phys_addr = vin->buf_hw[prev].phys + - vin->format.sizeimage / 4; - break; - default: - phys_addr = vin->buf_hw[prev].phys + - vin->format.sizeimage / 2; - break; - } - } else if ((vin->state != STOPPED && vin->state != RUNNING) || - list_empty(&vin->buf_list)) { + if (list_empty(&vin->buf_list)) { vin->buf_hw[slot].buffer = NULL; - vin->buf_hw[slot].type = FULL; phys_addr = vin->scratch_phys; } else { /* Keep track of buffer we give to HW */ @@ -1056,16 +980,12 @@ static void rvin_fill_hw_slot(struct rvin_dev *vin, int slot) list_del_init(to_buf_list(vbuf)); vin->buf_hw[slot].buffer = vbuf; - vin->buf_hw[slot].type = - V4L2_FIELD_IS_SEQUENTIAL(vin->format.field) ? - HALF_TOP : FULL; - /* Setup DMA */ phys_addr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); } - vin_dbg(vin, "Filling HW slot: %d type: %d buffer: %p\n", - slot, vin->buf_hw[slot].type, vin->buf_hw[slot].buffer); + vin_dbg(vin, "Filling HW slot: %d buffer: %p\n", + slot, vin->buf_hw[slot].buffer); vin->buf_hw[slot].phys = phys_addr; rvin_set_slot_addr(vin, slot, phys_addr); @@ -1073,15 +993,12 @@ static void rvin_fill_hw_slot(struct rvin_dev *vin, int slot) static int rvin_capture_start(struct rvin_dev *vin) { - int slot, ret; + int ret; - for (slot = 0; slot < HW_BUFFER_NUM; slot++) { + for (unsigned int slot = 0; slot < HW_BUFFER_NUM; slot++) { vin->buf_hw[slot].buffer = NULL; - vin->buf_hw[slot].type = FULL; - } - - for (slot = 0; slot < HW_BUFFER_NUM; slot++) rvin_fill_hw_slot(vin, slot); + } ret = rvin_setup(vin); if (ret) @@ -1094,8 +1011,6 @@ static int rvin_capture_start(struct rvin_dev *vin) /* Continuous Frame Capture Mode */ rvin_write(vin, VNFC_C_FRAME, VNFC_REG); - vin->state = STARTING; - return 0; } @@ -1118,27 +1033,40 @@ static void rvin_capture_stop(struct rvin_dev *vin) static irqreturn_t rvin_irq(int irq, void *data) { struct rvin_dev *vin = data; - u32 int_status, vnms; + u32 capture, status, vnms; int slot; unsigned int handled = 0; unsigned long flags; spin_lock_irqsave(&vin->qlock, flags); - int_status = rvin_get_interrupt_status(vin); - if (!int_status) + status = rvin_read(vin, VNINTS_REG); + if (!status) goto done; - rvin_ack_interrupt(vin); + rvin_write(vin, status, VNINTS_REG); handled = 1; + /* Signal Start of Frame. */ + if (status & VNINTS_VRS) { + struct v4l2_event event = { + .type = V4L2_EVENT_FRAME_SYNC, + .u.frame_sync.frame_sequence = vin->sequence, + }; + + v4l2_event_queue(&vin->vdev, &event); + } + /* Nothing to do if nothing was captured. */ - if (!(int_status & VNINTS_FIS)) + capture = vin->format.field == V4L2_FIELD_NONE || + vin->format.field == V4L2_FIELD_ALTERNATE ? + VNINTS_FIS : VNINTS_EFS; + if (!(status & capture)) goto done; - /* Nothing to do if capture status is 'STOPPED' */ - if (vin->state == STOPPED) { - vin_dbg(vin, "IRQ while state stopped\n"); + /* Nothing to do if not running. */ + if (!vin->running) { + vin_dbg(vin, "IRQ while not running, ignoring\n"); goto done; } @@ -1150,28 +1078,17 @@ static irqreturn_t rvin_irq(int irq, void *data) * To hand buffers back in a known order to userspace start * to capture first from slot 0. */ - if (vin->state == STARTING) { + if (!vin->sequence) { if (slot != 0) { vin_dbg(vin, "Starting sync slot: %d\n", slot); goto done; } vin_dbg(vin, "Capture start synced!\n"); - vin->state = RUNNING; } /* Capture frame */ if (vin->buf_hw[slot].buffer) { - /* - * Nothing to do but refill the hardware slot if - * capture only filled first half of vb2 buffer. - */ - if (vin->buf_hw[slot].type == HALF_TOP) { - vin->buf_hw[slot].buffer = NULL; - rvin_fill_hw_slot(vin, slot); - goto done; - } - vin->buf_hw[slot].buffer->field = rvin_get_active_field(vin, vnms); vin->buf_hw[slot].buffer->sequence = vin->sequence; @@ -1322,8 +1239,6 @@ static int rvin_mc_validate_format(struct rvin_dev *vin, struct v4l2_subdev *sd, case V4L2_FIELD_INTERLACED_TB: case V4L2_FIELD_INTERLACED_BT: case V4L2_FIELD_INTERLACED: - case V4L2_FIELD_SEQ_TB: - case V4L2_FIELD_SEQ_BT: /* Supported natively */ break; case V4L2_FIELD_ALTERNATE: @@ -1336,8 +1251,6 @@ static int rvin_mc_validate_format(struct rvin_dev *vin, struct v4l2_subdev *sd, case V4L2_FIELD_INTERLACED_TB: case V4L2_FIELD_INTERLACED_BT: case V4L2_FIELD_INTERLACED: - case V4L2_FIELD_SEQ_TB: - case V4L2_FIELD_SEQ_BT: /* Use VIN hardware to combine the two fields */ fmt.format.height *= 2; break; @@ -1351,7 +1264,7 @@ static int rvin_mc_validate_format(struct rvin_dev *vin, struct v4l2_subdev *sd, if (rvin_scaler_needed(vin)) { /* Gen3 can't scale NV12 */ - if (vin->info->model == RCAR_GEN3 && + if ((vin->info->model == RCAR_GEN3 || vin->info->model == RCAR_GEN4) && vin->format.pixelformat == V4L2_PIX_FMT_NV12) return -EPIPE; @@ -1381,14 +1294,6 @@ static int rvin_set_stream(struct rvin_dev *vin, int on) struct media_pad *pad; int ret; - /* No media controller used, simply pass operation to subdevice. */ - if (!vin->info->use_mc) { - ret = v4l2_subdev_call(vin->parallel.subdev, video, s_stream, - on); - - return ret == -ENOIOCTLCMD ? 0 : ret; - } - pad = media_pad_remote_pad_first(&vin->pad); if (!pad) return -EPIPE; @@ -1434,6 +1339,8 @@ int rvin_start_streaming(struct rvin_dev *vin) if (ret) rvin_set_stream(vin, 0); + vin->running = true; + spin_unlock_irqrestore(&vin->qlock, flags); return ret; @@ -1466,44 +1373,21 @@ err_scratch: void rvin_stop_streaming(struct rvin_dev *vin) { - unsigned int i, retries; unsigned long flags; - bool buffersFreed; spin_lock_irqsave(&vin->qlock, flags); - if (vin->state == STOPPED) { + if (!vin->running) { spin_unlock_irqrestore(&vin->qlock, flags); return; } - vin->state = STOPPING; - - /* Wait until only scratch buffer is used, max 3 interrupts. */ - retries = 0; - while (retries++ < RVIN_RETRIES) { - buffersFreed = true; - for (i = 0; i < HW_BUFFER_NUM; i++) - if (vin->buf_hw[i].buffer) - buffersFreed = false; - - if (buffersFreed) - break; - - spin_unlock_irqrestore(&vin->qlock, flags); - msleep(RVIN_TIMEOUT_MS); - spin_lock_irqsave(&vin->qlock, flags); - } - /* Wait for streaming to stop */ - retries = 0; - while (retries++ < RVIN_RETRIES) { - + for (unsigned int i = 0; i < RVIN_RETRIES; i++) { rvin_capture_stop(vin); /* Check if HW is stopped */ if (!rvin_capture_active(vin)) { - vin->state = STOPPED; break; } @@ -1512,32 +1396,25 @@ void rvin_stop_streaming(struct rvin_dev *vin) spin_lock_irqsave(&vin->qlock, flags); } - if (!buffersFreed || vin->state != STOPPED) { - /* - * If this happens something have gone horribly wrong. - * Set state to stopped to prevent the interrupt handler - * to make things worse... - */ - vin_err(vin, "Failed stop HW, something is seriously broken\n"); - vin->state = STOPPED; - } + if (rvin_capture_active(vin)) + vin_err(vin, "Hardware did not stop\n"); - spin_unlock_irqrestore(&vin->qlock, flags); + vin->running = false; - /* If something went wrong, free buffers with an error. */ - if (!buffersFreed) { - return_unused_buffers(vin, VB2_BUF_STATE_ERROR); - for (i = 0; i < HW_BUFFER_NUM; i++) { - if (vin->buf_hw[i].buffer) - vb2_buffer_done(&vin->buf_hw[i].buffer->vb2_buf, - VB2_BUF_STATE_ERROR); - } - } + spin_unlock_irqrestore(&vin->qlock, flags); rvin_set_stream(vin, 0); /* disable interrupts */ - rvin_disable_interrupts(vin); + rvin_write(vin, 0, VNIE_REG); + + /* Return unprocessed buffers from hardware. */ + for (unsigned int i = 0; i < HW_BUFFER_NUM; i++) { + if (vin->buf_hw[i].buffer) + vb2_buffer_done(&vin->buf_hw[i].buffer->vb2_buf, + VB2_BUF_STATE_ERROR); + } + } static void rvin_stop_streaming_vq(struct vb2_queue *vq) @@ -1583,8 +1460,6 @@ int rvin_dma_register(struct rvin_dev *vin, int irq) spin_lock_init(&vin->qlock); - vin->state = STOPPED; - for (i = 0; i < HW_BUFFER_NUM; i++) vin->buf_hw[i].buffer = NULL; @@ -1687,7 +1562,7 @@ void rvin_set_alpha(struct rvin_dev *vin, unsigned int alpha) vin->alpha = alpha; - if (vin->state == STOPPED) + if (!vin->running) goto out; switch (vin->format.pixelformat) { |