camera: vfe32: Enable composite stats irq

All stats are generated at roughly the same time
once per frame. They can be composited together and notify
the user space at once.

Change-Id: I0bd828473fa705bef58ecdba8b18a365235bfca0
Signed-off-by: Shuzhen Wang <shuzhenw@codeaurora.org>
diff --git a/drivers/media/video/msm/msm.h b/drivers/media/video/msm/msm.h
index 22322b8..7cab16e 100644
--- a/drivers/media/video/msm/msm.h
+++ b/drivers/media/video/msm/msm.h
@@ -116,6 +116,7 @@
 	NOTIFY_ISP_MSG_EVT, /* arg = enum ISP_MESSAGE_ID */
 	NOTIFY_VFE_MSG_OUT, /* arg = struct isp_msg_output */
 	NOTIFY_VFE_MSG_STATS,  /* arg = struct isp_msg_stats */
+	NOTIFY_VFE_MSG_COMP_STATS, /* arg = struct msm_stats_buf */
 	NOTIFY_VFE_BUF_EVT, /* arg = struct msm_vfe_resp */
 	NOTIFY_ISPIF_STREAM, /* arg = enable parameter for s_stream */
 	NOTIFY_VPE_MSG_EVT,
diff --git a/drivers/media/video/msm/msm_isp.c b/drivers/media/video/msm/msm_isp.c
index 6b3aef7..0cffbbf 100644
--- a/drivers/media/video/msm/msm_isp.c
+++ b/drivers/media/video/msm/msm_isp.c
@@ -307,6 +307,36 @@
 		}
 		}
 		break;
+	case NOTIFY_VFE_MSG_COMP_STATS: {
+		struct msm_stats_buf *stats = (struct msm_stats_buf *)arg;
+		struct msm_stats_buf *stats_buf = NULL;
+
+		isp_event->isp_data.isp_msg.msg_id = MSG_ID_STATS_COMPOSITE;
+		stats->aec.buff = msm_pmem_stats_ptov_lookup(&pmctl->sync,
+					stats->aec.buff, &(stats->aec.fd));
+		stats->awb.buff = msm_pmem_stats_ptov_lookup(&pmctl->sync,
+					stats->awb.buff, &(stats->awb.fd));
+		stats->af.buff = msm_pmem_stats_ptov_lookup(&pmctl->sync,
+					stats->af.buff, &(stats->af.fd));
+		stats->ihist.buff = msm_pmem_stats_ptov_lookup(&pmctl->sync,
+					stats->ihist.buff, &(stats->ihist.fd));
+		stats->rs.buff = msm_pmem_stats_ptov_lookup(&pmctl->sync,
+					stats->rs.buff, &(stats->rs.fd));
+		stats->cs.buff = msm_pmem_stats_ptov_lookup(&pmctl->sync,
+					stats->cs.buff, &(stats->cs.fd));
+
+		stats_buf = kmalloc(sizeof(struct msm_stats_buf), GFP_ATOMIC);
+		if (!stats_buf) {
+			pr_err("%s: out of memory.\n", __func__);
+			rc = -ENOMEM;
+		} else {
+			*stats_buf = *stats;
+			isp_event->isp_data.isp_msg.len	=
+				sizeof(struct msm_stats_buf);
+			isp_event->isp_data.isp_msg.data = stats_buf;
+		}
+		}
+		break;
 	case NOTIFY_VFE_MSG_STATS: {
 		struct msm_stats_buf stats;
 		struct isp_msg_stats *isp_stats = (struct isp_msg_stats *)arg;
diff --git a/drivers/media/video/msm/msm_mctl.c b/drivers/media/video/msm/msm_mctl.c
index 2c4fbe4..13d1daf 100644
--- a/drivers/media/video/msm/msm_mctl.c
+++ b/drivers/media/video/msm/msm_mctl.c
@@ -241,6 +241,7 @@
 	case NOTIFY_ISP_MSG_EVT:
 	case NOTIFY_VFE_MSG_OUT:
 	case NOTIFY_VFE_MSG_STATS:
+	case NOTIFY_VFE_MSG_COMP_STATS:
 	case NOTIFY_VFE_BUF_EVT:
 	case NOTIFY_VFE_BUF_FREE_EVT:
 		if (p_mctl->isp_sdev && p_mctl->isp_sdev->isp_notify) {
diff --git a/drivers/media/video/msm/msm_vfe32.c b/drivers/media/video/msm/msm_vfe32.c
index 4b86bd8..e1a36a8 100644
--- a/drivers/media/video/msm/msm_vfe32.c
+++ b/drivers/media/video/msm/msm_vfe32.c
@@ -589,6 +589,7 @@
 
 	vfe32_ctrl->operation_mode = *p;
 	vfe32_ctrl->stats_comp = *(++p);
+	vfe32_ctrl->hfr_mode = *(++p);
 
 	msm_io_w(*(++p), vfe32_ctrl->vfebase + VFE_CFG);
 	msm_io_w(*(++p), vfe32_ctrl->vfebase + VFE_MODULE_CFG);
@@ -684,10 +685,16 @@
 
 static void vfe32_start_common(void)
 {
+	uint32_t irq_mask = 0x00E00021;
 	vfe32_ctrl->start_ack_pending = TRUE;
 	CDBG("VFE opertaion mode = 0x%x, output mode = 0x%x\n",
 		vfe32_ctrl->operation_mode, vfe32_ctrl->outpath.output_mode);
-	msm_io_w(0x00EFE021, vfe32_ctrl->vfebase + VFE_IRQ_MASK_0);
+	if (vfe32_ctrl->stats_comp)
+		irq_mask |= VFE_IRQ_STATUS0_STATS_COMPOSIT_MASK;
+	else
+		irq_mask |= 0x000FE000;
+
+	msm_io_w(irq_mask, vfe32_ctrl->vfebase + VFE_IRQ_MASK_0);
 	msm_io_w(VFE_IMASK_WHILE_STOPPING_1,
 		vfe32_ctrl->vfebase + VFE_IRQ_MASK_1);
 
@@ -2389,47 +2396,73 @@
 static void vfe32_stats_af_ack(struct vfe_cmd_stats_ack *pAck)
 {
 	unsigned long flags;
-	spin_lock_irqsave(&vfe32_ctrl->af_ack_lock, flags);
+	spinlock_t *lock = (vfe32_ctrl->stats_comp ?
+		&vfe32_ctrl->comp_stats_ack_lock :
+		&vfe32_ctrl->af_ack_lock);
+	spin_lock_irqsave(lock, flags);
 	vfe32_ctrl->afStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
 	vfe32_ctrl->afStatsControl.ackPending = FALSE;
-	spin_unlock_irqrestore(&vfe32_ctrl->af_ack_lock, flags);
+	spin_unlock_irqrestore(lock, flags);
 }
 
 static void vfe32_stats_awb_ack(struct vfe_cmd_stats_ack *pAck)
 {
 	unsigned long flags;
-	spin_lock_irqsave(&vfe32_ctrl->awb_ack_lock, flags);
+	spinlock_t *lock = (vfe32_ctrl->stats_comp ?
+		&vfe32_ctrl->comp_stats_ack_lock :
+		&vfe32_ctrl->awb_ack_lock);
+	spin_lock_irqsave(lock, flags);
 	vfe32_ctrl->awbStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
 	vfe32_ctrl->awbStatsControl.ackPending = FALSE;
-	spin_unlock_irqrestore(&vfe32_ctrl->awb_ack_lock, flags);
+	spin_unlock_irqrestore(lock, flags);
 }
 
 static void vfe32_stats_aec_ack(struct vfe_cmd_stats_ack *pAck)
 {
 	unsigned long flags;
-	spin_lock_irqsave(&vfe32_ctrl->aec_ack_lock, flags);
+	spinlock_t *lock = (vfe32_ctrl->stats_comp ?
+		&vfe32_ctrl->comp_stats_ack_lock :
+		&vfe32_ctrl->aec_ack_lock);
+	spin_lock_irqsave(lock, flags);
 	vfe32_ctrl->aecStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
 	vfe32_ctrl->aecStatsControl.ackPending = FALSE;
-	spin_unlock_irqrestore(&vfe32_ctrl->aec_ack_lock, flags);
+	spin_unlock_irqrestore(lock, flags);
 }
 
 static void vfe32_stats_ihist_ack(struct vfe_cmd_stats_ack *pAck)
 {
+	unsigned long flags;
+	spinlock_t *lock = (vfe32_ctrl->stats_comp ?
+		&vfe32_ctrl->comp_stats_ack_lock :
+		&vfe32_ctrl->ihist_ack_lock);
+	spin_lock_irqsave(lock, flags);
 	vfe32_ctrl->ihistStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
 	vfe32_ctrl->ihistStatsControl.ackPending = FALSE;
+	spin_unlock_irqrestore(lock, flags);
 }
 static void vfe32_stats_rs_ack(struct vfe_cmd_stats_ack *pAck)
 {
+	unsigned long flags;
+	spinlock_t *lock = (vfe32_ctrl->stats_comp ?
+		&vfe32_ctrl->comp_stats_ack_lock :
+		&vfe32_ctrl->rs_ack_lock);
+	spin_lock_irqsave(lock, flags);
 	vfe32_ctrl->rsStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
 	vfe32_ctrl->rsStatsControl.ackPending = FALSE;
+	spin_unlock_irqrestore(lock, flags);
 }
 static void vfe32_stats_cs_ack(struct vfe_cmd_stats_ack *pAck)
 {
+	unsigned long flags;
+	spinlock_t *lock = (vfe32_ctrl->stats_comp ?
+		&vfe32_ctrl->comp_stats_ack_lock :
+		&vfe32_ctrl->cs_ack_lock);
+	spin_lock_irqsave(lock, flags);
 	vfe32_ctrl->csStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
 	vfe32_ctrl->csStatsControl.ackPending = FALSE;
+	spin_unlock_irqrestore(lock, flags);
 }
 
-
 static inline void vfe32_read_irq_status(struct vfe32_irq_status *out)
 {
 	uint32_t *temp;
@@ -2654,6 +2687,13 @@
 			msm_io_w_mb(CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY,
 				vfe32_ctrl->vfebase + VFE_CAMIF_COMMAND);
 		}
+	} /* if raw snapshot mode. */
+	if ((vfe32_ctrl->hfr_mode != HFR_MODE_OFF) &&
+		(vfe32_ctrl->operation_mode == VFE_MODE_OF_OPERATION_VIDEO) &&
+		(vfe32_ctrl->vfeFrameId % vfe32_ctrl->hfr_mode != 0)) {
+		vfe32_ctrl->vfeFrameId++;
+		CDBG("Skip the SOF notification when HFR enabled\n");
+		return;
 	}
 	vfe32_ctrl->vfeFrameId++;
 	vfe32_send_isp_msg(vfe32_ctrl, MSG_ID_SOF_ACK);
@@ -2907,11 +2947,6 @@
 	}
 }
 
-static void vfe32_process_stats_comb_irq(uint32_t *irqstatus)
-{
-	return;
-}
-
 static uint32_t  vfe32_process_stats_irq_common(uint32_t statsNum,
 						uint32_t newAddr) {
 
@@ -2971,17 +3006,23 @@
 
 	case statsIhistNum: {
 		msgStats.id = MSG_ID_STATS_IHIST;
+		spin_lock_irqsave(&vfe32_ctrl->ihist_ack_lock, flags);
 		vfe32_ctrl->ihistStatsControl.ackPending = TRUE;
+		spin_unlock_irqrestore(&vfe32_ctrl->ihist_ack_lock, flags);
 		}
 		break;
 	case statsRsNum: {
 		msgStats.id = MSG_ID_STATS_RS;
+		spin_lock_irqsave(&vfe32_ctrl->rs_ack_lock, flags);
 		vfe32_ctrl->rsStatsControl.ackPending = TRUE;
+		spin_unlock_irqrestore(&vfe32_ctrl->rs_ack_lock, flags);
 		}
 		break;
 	case statsCsNum: {
 		msgStats.id = MSG_ID_STATS_CS;
+		spin_lock_irqsave(&vfe32_ctrl->cs_ack_lock, flags);
 		vfe32_ctrl->csStatsControl.ackPending = TRUE;
+		spin_unlock_irqrestore(&vfe32_ctrl->cs_ack_lock, flags);
 		}
 		break;
 
@@ -2997,6 +3038,30 @@
 	return;
 }
 
+static void vfe_send_comp_stats_msg(uint32_t status_bits)
+{
+	struct msm_stats_buf msgStats;
+	uint32_t temp;
+
+	msgStats.frame_id = vfe32_ctrl->vfeFrameId;
+	msgStats.status_bits = status_bits;
+
+	msgStats.aec.buff = vfe32_ctrl->aecStatsControl.bufToRender;
+	msgStats.awb.buff = vfe32_ctrl->awbStatsControl.bufToRender;
+	msgStats.af.buff = vfe32_ctrl->afStatsControl.bufToRender;
+
+	msgStats.ihist.buff = vfe32_ctrl->ihistStatsControl.bufToRender;
+	msgStats.rs.buff = vfe32_ctrl->rsStatsControl.bufToRender;
+	msgStats.cs.buff = vfe32_ctrl->csStatsControl.bufToRender;
+
+	temp = msm_io_r(vfe32_ctrl->vfebase + VFE_STATS_AWB_SGW_CFG);
+	msgStats.awb_ymin = (0xFF00 & temp) >> 8;
+
+	v4l2_subdev_notify(&vfe32_ctrl->subdev,
+				NOTIFY_VFE_MSG_COMP_STATS,
+				&msgStats);
+}
+
 static void vfe32_process_stats_ae_irq(void)
 {
 	unsigned long flags;
@@ -3105,6 +3170,126 @@
 	}
 }
 
+static void vfe32_process_stats(uint32_t status_bits)
+{
+	unsigned long flags;
+	int32_t process_stats = false;
+	CDBG("%s, stats = 0x%x\n", __func__, status_bits);
+
+	spin_lock_irqsave(&vfe32_ctrl->comp_stats_ack_lock, flags);
+	if (status_bits & VFE_IRQ_STATUS0_STATS_AEC) {
+		if (!vfe32_ctrl->aecStatsControl.ackPending) {
+			vfe32_ctrl->aecStatsControl.ackPending = TRUE;
+			vfe32_ctrl->aecStatsControl.bufToRender =
+				vfe32_process_stats_irq_common(statsAeNum,
+				vfe32_ctrl->aecStatsControl.nextFrameAddrBuf);
+			process_stats = true;
+		} else{
+			vfe32_ctrl->aecStatsControl.bufToRender = 0;
+			vfe32_ctrl->aecStatsControl.droppedStatsFrameCount++;
+		}
+	} else {
+		vfe32_ctrl->aecStatsControl.bufToRender = 0;
+	}
+
+	if (status_bits & VFE_IRQ_STATUS0_STATS_AWB) {
+		if (!vfe32_ctrl->awbStatsControl.ackPending) {
+			vfe32_ctrl->awbStatsControl.ackPending = TRUE;
+			vfe32_ctrl->awbStatsControl.bufToRender =
+				vfe32_process_stats_irq_common(statsAwbNum,
+				vfe32_ctrl->awbStatsControl.nextFrameAddrBuf);
+			process_stats = true;
+		} else{
+			vfe32_ctrl->awbStatsControl.droppedStatsFrameCount++;
+			vfe32_ctrl->awbStatsControl.bufToRender = 0;
+		}
+	} else {
+		vfe32_ctrl->awbStatsControl.bufToRender = 0;
+	}
+
+
+	if (status_bits & VFE_IRQ_STATUS0_STATS_AF) {
+		if (!vfe32_ctrl->afStatsControl.ackPending) {
+			vfe32_ctrl->afStatsControl.ackPending = TRUE;
+			vfe32_ctrl->afStatsControl.bufToRender =
+				vfe32_process_stats_irq_common(statsAfNum,
+				vfe32_ctrl->afStatsControl.nextFrameAddrBuf);
+			process_stats = true;
+		} else {
+			vfe32_ctrl->afStatsControl.bufToRender = 0;
+			vfe32_ctrl->afStatsControl.droppedStatsFrameCount++;
+		}
+	} else {
+		vfe32_ctrl->afStatsControl.bufToRender = 0;
+	}
+
+	if (status_bits & VFE_IRQ_STATUS0_STATS_IHIST) {
+		if (!vfe32_ctrl->ihistStatsControl.ackPending) {
+			vfe32_ctrl->ihistStatsControl.ackPending = TRUE;
+			vfe32_ctrl->ihistStatsControl.bufToRender =
+				vfe32_process_stats_irq_common(statsIhistNum,
+				vfe32_ctrl->ihistStatsControl.nextFrameAddrBuf);
+			process_stats = true;
+		} else {
+			vfe32_ctrl->ihistStatsControl.droppedStatsFrameCount++;
+			vfe32_ctrl->ihistStatsControl.bufToRender = 0;
+		}
+	} else {
+		vfe32_ctrl->ihistStatsControl.bufToRender = 0;
+	}
+
+	if (status_bits & VFE_IRQ_STATUS0_STATS_RS) {
+		if (!vfe32_ctrl->rsStatsControl.ackPending) {
+			vfe32_ctrl->rsStatsControl.ackPending = TRUE;
+			vfe32_ctrl->rsStatsControl.bufToRender =
+				vfe32_process_stats_irq_common(statsRsNum,
+				vfe32_ctrl->rsStatsControl.nextFrameAddrBuf);
+			process_stats = true;
+		} else {
+			vfe32_ctrl->rsStatsControl.droppedStatsFrameCount++;
+			vfe32_ctrl->rsStatsControl.bufToRender = 0;
+		}
+	} else {
+		vfe32_ctrl->rsStatsControl.bufToRender = 0;
+	}
+
+
+	if (status_bits & VFE_IRQ_STATUS0_STATS_CS) {
+		if (!vfe32_ctrl->csStatsControl.ackPending) {
+			vfe32_ctrl->csStatsControl.ackPending = TRUE;
+			vfe32_ctrl->csStatsControl.bufToRender =
+				vfe32_process_stats_irq_common(statsCsNum,
+				vfe32_ctrl->csStatsControl.nextFrameAddrBuf);
+			process_stats = true;
+		} else {
+			vfe32_ctrl->csStatsControl.droppedStatsFrameCount++;
+			vfe32_ctrl->csStatsControl.bufToRender = 0;
+		}
+	} else {
+		vfe32_ctrl->csStatsControl.bufToRender = 0;
+	}
+
+	spin_unlock_irqrestore(&vfe32_ctrl->comp_stats_ack_lock, flags);
+	if (process_stats)
+		vfe_send_comp_stats_msg(status_bits);
+
+	return;
+}
+
+static void vfe32_process_stats_irq(uint32_t *irqstatus)
+{
+	uint32_t status_bits = VFE_COM_STATUS & *irqstatus;
+
+	if ((vfe32_ctrl->hfr_mode != HFR_MODE_OFF) &&
+		(vfe32_ctrl->vfeFrameId % vfe32_ctrl->hfr_mode != 0)) {
+		CDBG("Skip the stats when HFR enabled\n");
+		return;
+	}
+
+	vfe32_process_stats(status_bits);
+	return;
+}
+
 static void vfe32_do_tasklet(unsigned long data)
 {
 	unsigned long flags;
@@ -3191,7 +3376,7 @@
 				if (qcmd->vfeInterruptStatus0 &
 					VFE_IRQ_STATUS0_STATS_COMPOSIT_MASK) {
 					CDBG("Stats composite irq occured.\n");
-					vfe32_process_stats_comb_irq(
+					vfe32_process_stats_irq(
 						&qcmd->vfeInterruptStatus0);
 				}
 			} else {
@@ -3618,6 +3803,10 @@
 	spin_lock_init(&vfe32_ctrl->aec_ack_lock);
 	spin_lock_init(&vfe32_ctrl->awb_ack_lock);
 	spin_lock_init(&vfe32_ctrl->af_ack_lock);
+	spin_lock_init(&vfe32_ctrl->ihist_ack_lock);
+	spin_lock_init(&vfe32_ctrl->rs_ack_lock);
+	spin_lock_init(&vfe32_ctrl->cs_ack_lock);
+	spin_lock_init(&vfe32_ctrl->comp_stats_ack_lock);
 	spin_lock_init(&vfe32_ctrl->sd_notify_lock);
 	INIT_LIST_HEAD(&vfe32_ctrl->tasklet_q);
 
@@ -3625,6 +3814,7 @@
 	vfe32_ctrl->update_rolloff = false;
 	vfe32_ctrl->update_la = false;
 	vfe32_ctrl->update_gamma = false;
+	vfe32_ctrl->hfr_mode = HFR_MODE_OFF;
 
 	enable_irq(vfe32_ctrl->vfeirq->start);
 
diff --git a/drivers/media/video/msm/msm_vfe32.h b/drivers/media/video/msm/msm_vfe32.h
index 1adfffd..c511b69 100644
--- a/drivers/media/video/msm/msm_vfe32.h
+++ b/drivers/media/video/msm/msm_vfe32.h
@@ -168,6 +168,8 @@
 #define VFE_AF_PINGPONG_STATUS_BIT       0x100
 #define VFE_AWB_PINGPONG_STATUS_BIT      0x200
 
+#define HFR_MODE_OFF 1
+
 enum VFE32_DMI_RAM_SEL {
 	NO_MEM_SELECTED          = 0,
 	BLACK_LUT_RAM_BANK0      = 0x1,
@@ -220,7 +222,7 @@
 #define V32_OUT_CLAMP_OFF         0x00000524
 #define V32_OUT_CLAMP_LEN         8
 
-#define V32_OPERATION_CFG_LEN     32
+#define V32_OPERATION_CFG_LEN     36
 
 #define V32_AXI_OUT_OFF           0x00000038
 #define V32_AXI_OUT_LEN           216
@@ -778,6 +780,8 @@
 #define VFE32_IMASK_STATS_SKIN_BHIST_BUS_OVFL (0x00000001<<21)
 #define VFE32_IMASK_AXI_ERROR                 (0x00000001<<22)
 
+#define VFE_COM_STATUS 0x000FE000
+
 struct vfe32_output_path {
 	uint16_t output_mode;     /* bitmask  */
 
@@ -864,6 +868,7 @@
 #define VFE_CLAMP_MIN                   0x00000528
 #define VFE_REALIGN_BUF                 0x0000052C
 #define VFE_STATS_CFG                   0x00000530
+#define VFE_STATS_AWB_SGW_CFG           0x00000554
 #define VFE_DMI_CFG                     0x00000598
 #define VFE_DMI_ADDR                    0x0000059C
 #define VFE_DMI_DATA_LO                 0x000005A4
@@ -906,6 +911,10 @@
 	spinlock_t  aec_ack_lock;
 	spinlock_t  awb_ack_lock;
 	spinlock_t  af_ack_lock;
+	spinlock_t  ihist_ack_lock;
+	spinlock_t  rs_ack_lock;
+	spinlock_t  cs_ack_lock;
+	spinlock_t  comp_stats_ack_lock;
 
 	uint32_t extlen;
 	void *extdata;
@@ -958,6 +967,7 @@
 	struct platform_device *pdev;
 	struct clk *vfe_clk[3];
 	spinlock_t  sd_notify_lock;
+	uint32_t hfr_mode;
 };
 
 #define statsAeNum      0
diff --git a/include/media/msm_isp.h b/include/media/msm_isp.h
index 7caafb6..5dd1445 100644
--- a/include/media/msm_isp.h
+++ b/include/media/msm_isp.h
@@ -58,6 +58,7 @@
 #define MSG_ID_STATS_AWB_AEC            39
 #define MSG_ID_OUTPUT_PRIMARY           40
 #define MSG_ID_OUTPUT_SECONDARY         41
+#define MSG_ID_STATS_COMPOSITE          42
 
 /* ISP command IDs */
 #define VFE_CMD_DUMMY_0                                 0