Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b6f556cb authored by Niklas Söderlund's avatar Niklas Söderlund Committed by Mauro Carvalho Chehab
Browse files

[media] media: rcar-vin: add support for V4L2_FIELD_ALTERNATE



The HW can capture both ODD and EVEN fields in separate buffers so it's
possible to support V4L2_FIELD_ALTERNATE. This patch add support for
this mode.

At probe time and when S_STD is called the driver will default to use
V4L2_FIELD_INTERLACED if the subdevice reports V4L2_FIELD_ALTERNATE. The
driver will only change the field type if the subdevice implements
G_STD, if not it will keep the default at V4L2_FIELD_ALTERNATE.

The user can always explicitly ask for V4L2_FIELD_ALTERNATE in S_FMT and
the driver will use that field format.

Signed-off-by: default avatarNiklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Signed-off-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@s-opensource.com>
parent c6b3d8fc
Loading
Loading
Loading
Loading
+20 −6
Original line number Diff line number Diff line
@@ -95,6 +95,7 @@
/* Video n Module Status Register bits */
#define VNMS_FBS_MASK		(3 << 3)
#define VNMS_FBS_SHIFT		3
#define VNMS_FS			(1 << 2)
#define VNMS_AV			(1 << 1)
#define VNMS_CA			(1 << 0)

@@ -156,6 +157,7 @@ static int rvin_setup(struct rvin_dev *vin)
	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;
@@ -329,15 +331,26 @@ 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)
static int rvin_get_active_slot(struct rvin_dev *vin, u32 vnms)
{
	if (vin->continuous)
		return (rvin_read(vin, VNMS_REG) & VNMS_FBS_MASK)
			>> VNMS_FBS_SHIFT;
		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;
@@ -878,7 +891,7 @@ static bool rvin_fill_hw(struct rvin_dev *vin)
static irqreturn_t rvin_irq(int irq, void *data)
{
	struct rvin_dev *vin = data;
	u32 int_status;
	u32 int_status, vnms;
	int slot;
	unsigned int sequence, handled = 0;
	unsigned long flags;
@@ -905,7 +918,8 @@ static irqreturn_t rvin_irq(int irq, void *data)
	}

	/* Prepare for capture and update state */
	slot = rvin_get_active_slot(vin);
	vnms = rvin_read(vin, VNMS_REG);
	slot = rvin_get_active_slot(vin, vnms);
	sequence = vin->sequence++;

	vin_dbg(vin, "IRQ %02d: %d\tbuf0: %c buf1: %c buf2: %c\tmore: %d\n",
@@ -920,7 +934,7 @@ static irqreturn_t rvin_irq(int irq, void *data)
		goto done;

	/* Capture frame */
	vin->queue_buf[slot]->field = vin->format.field;
	vin->queue_buf[slot]->field = rvin_get_active_field(vin, vnms);
	vin->queue_buf[slot]->sequence = sequence;
	vin->queue_buf[slot]->vb2_buf.timestamp = ktime_get_ns();
	vb2_buffer_done(&vin->queue_buf[slot]->vb2_buf, VB2_BUF_STATE_DONE);
+13 −0
Original line number Diff line number Diff line
@@ -122,9 +122,21 @@ static int rvin_reset_format(struct rvin_dev *vin)
	vin->format.colorspace	= mf->colorspace;
	vin->format.field	= mf->field;

	/*
	 * If the subdevice uses ALTERNATE field mode and G_STD is
	 * implemented use the VIN HW to combine the two fields to
	 * one INTERLACED frame. The ALTERNATE field mode can still
	 * be requested in S_FMT and be respected, this is just the
	 * default which is applied at probing or when S_STD is called.
	 */
	if (vin->format.field == V4L2_FIELD_ALTERNATE &&
	    v4l2_subdev_has_op(vin_to_source(vin), video, g_std))
		vin->format.field = V4L2_FIELD_INTERLACED;

	switch (vin->format.field) {
	case V4L2_FIELD_TOP:
	case V4L2_FIELD_BOTTOM:
	case V4L2_FIELD_ALTERNATE:
		vin->format.height /= 2;
		break;
	case V4L2_FIELD_NONE:
@@ -225,6 +237,7 @@ static int __rvin_try_format(struct rvin_dev *vin,
	switch (pix->field) {
	case V4L2_FIELD_TOP:
	case V4L2_FIELD_BOTTOM:
	case V4L2_FIELD_ALTERNATE:
		pix->height /= 2;
		source->height /= 2;
		break;