/* 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)
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;
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;
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;
}
/* 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",
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);
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:
switch (pix->field) {
case V4L2_FIELD_TOP:
case V4L2_FIELD_BOTTOM:
+ case V4L2_FIELD_ALTERNATE:
pix->height /= 2;
source->height /= 2;
break;