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