[media] v4l: omap4iss: csi2: Perform real frame number propagation

Compute the pipeline frame number from the frame number sent by the
sensor instead of incrementing the frame number in software. This
improves dropped frames detection.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c
index cc1dfad..d6534f5 100644
--- a/drivers/staging/media/omap4iss/iss.c
+++ b/drivers/staging/media/omap4iss/iss.c
@@ -612,7 +612,12 @@
 		ret = v4l2_subdev_call(subdev, video, s_stream, mode);
 		if (ret < 0 && ret != -ENOIOCTLCMD)
 			return ret;
+
+		if (subdev == &iss->csi2a.subdev ||
+		    subdev == &iss->csi2b.subdev)
+			pipe->do_propagation = true;
 	}
+
 	iss_print_status(pipe->output->iss);
 	return 0;
 }
diff --git a/drivers/staging/media/omap4iss/iss_csi2.c b/drivers/staging/media/omap4iss/iss_csi2.c
index 21971c6..2d96fb3 100644
--- a/drivers/staging/media/omap4iss/iss_csi2.c
+++ b/drivers/staging/media/omap4iss/iss_csi2.c
@@ -319,6 +319,8 @@
 {
 	u32 reg = 0;
 
+	ctx->frame = 0;
+
 	/* Set up CSI2_CTx_CTRL1 */
 	if (ctx->eof_enabled)
 		reg = CSI2_CTX_CTRL1_EOF_EN;
@@ -396,21 +398,18 @@
  */
 static void csi2_irq_ctx_set(struct iss_csi2_device *csi2, int enable)
 {
-	u32 reg = CSI2_CTX_IRQ_FE;
+	const u32 mask = CSI2_CTX_IRQ_FE | CSI2_CTX_IRQ_FS;
 	int i;
 
-	if (csi2->use_fs_irq)
-		reg |= CSI2_CTX_IRQ_FS;
-
 	for (i = 0; i < 8; i++) {
 		iss_reg_write(csi2->iss, csi2->regs1, CSI2_CTX_IRQSTATUS(i),
-			      reg);
+			      mask);
 		if (enable)
 			iss_reg_set(csi2->iss, csi2->regs1,
-				    CSI2_CTX_IRQENABLE(i), reg);
+				    CSI2_CTX_IRQENABLE(i), mask);
 		else
 			iss_reg_clr(csi2->iss, csi2->regs1,
-				    CSI2_CTX_IRQENABLE(i), reg);
+				    CSI2_CTX_IRQENABLE(i), mask);
 	}
 }
 
@@ -679,8 +678,34 @@
 	if (status & CSI2_CTX_IRQ_FS) {
 		struct iss_pipeline *pipe =
 				     to_iss_pipeline(&csi2->subdev.entity);
-		if (pipe->do_propagation)
+		u16 frame;
+		u16 delta;
+
+		frame = iss_reg_read(csi2->iss, csi2->regs1,
+				     CSI2_CTX_CTRL2(ctx->ctxnum))
+		      >> CSI2_CTX_CTRL2_FRAME_SHIFT;
+
+		if (frame == 0) {
+			/* A zero value means that the counter isn't implemented
+			 * by the source. Increment the frame number in software
+			 * in that case.
+			 */
 			atomic_inc(&pipe->frame_number);
+		} else {
+			/* Extend the 16 bit frame number to 32 bits by
+			 * computing the delta between two consecutive CSI2
+			 * frame numbers and adding it to the software frame
+			 * number. The hardware counter starts at 1 and wraps
+			 * from 0xffff to 1 without going through 0, so subtract
+			 * 1 when the counter wraps.
+			 */
+			delta = frame - ctx->frame;
+			if (frame < ctx->frame)
+				delta--;
+			ctx->frame = frame;
+
+			atomic_add(delta, &pipe->frame_number);
+		}
 	}
 
 	if (!(status & CSI2_CTX_IRQ_FE))
@@ -1039,7 +1064,6 @@
 {
 	struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
 	struct iss_device *iss = csi2->iss;
-	struct iss_pipeline *pipe = to_iss_pipeline(&csi2->subdev.entity);
 	struct iss_video *video_out = &csi2->video_out;
 	int ret = 0;
 
@@ -1058,7 +1082,6 @@
 
 		if (omap4iss_csiphy_acquire(csi2->phy) < 0)
 			return -ENODEV;
-		csi2->use_fs_irq = pipe->do_propagation;
 		csi2_configure(csi2);
 		csi2_print_status(csi2);
 
diff --git a/drivers/staging/media/omap4iss/iss_csi2.h b/drivers/staging/media/omap4iss/iss_csi2.h
index 971aa7b..3b37978 100644
--- a/drivers/staging/media/omap4iss/iss_csi2.h
+++ b/drivers/staging/media/omap4iss/iss_csi2.h
@@ -82,6 +82,7 @@
 	u8 virtual_id;
 	u16 format_id;		/* as in CSI2_CTx_CTRL2[9:0] */
 	u8 dpcm_predictor;	/* 1: simple, 0: advanced */
+	u16 frame;
 
 	/* Fields in CSI2_CTx_CTRL1/3 - Shadowed */
 	u16 alpha;
@@ -137,7 +138,6 @@
 	u32 output; /* output to IPIPEIF, memory or both? */
 	bool dpcm_decompress;
 	unsigned int frame_skip;
-	bool use_fs_irq;
 
 	struct iss_csiphy *phy;
 	struct iss_csi2_ctx_cfg contexts[ISS_CSI2_MAX_CTX_NUM + 1];
diff --git a/drivers/staging/media/omap4iss/iss_regs.h b/drivers/staging/media/omap4iss/iss_regs.h
index efd0291..d2b6b6a 100644
--- a/drivers/staging/media/omap4iss/iss_regs.h
+++ b/drivers/staging/media/omap4iss/iss_regs.h
@@ -215,6 +215,8 @@
 #define CSI2_CTX_CTRL1_CTX_EN				(1 << 0)
 
 #define CSI2_CTX_CTRL2(i)				(0x74 + (0x20 * i))
+#define CSI2_CTX_CTRL2_FRAME_MASK			(0xffff << 16)
+#define CSI2_CTX_CTRL2_FRAME_SHIFT			16
 #define CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT		13
 #define CSI2_CTX_CTRL2_USER_DEF_MAP_MASK		\
 		(0x3 << CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT)