Merge "msm_fb: display: add mdp overlay1 writeback" into msm-3.0
diff --git a/drivers/video/msm/Kconfig b/drivers/video/msm/Kconfig
index de6bf1a..448194a 100644
--- a/drivers/video/msm/Kconfig
+++ b/drivers/video/msm/Kconfig
@@ -301,6 +301,13 @@
 	---help---
 	  Support for MDP4 OVERLAY0 write back mode
 
+
+config FB_MSM_OVERLAY1_WRITEBACK
+        depends on FB_MSM_OVERLAY
+        bool "MDP overlay1 write back mode enable"
+        ---help---
+          Support for MDP4 OVERLAY1 write back mode
+
 config FB_MSM_WRITEBACK_MSM_PANEL
 	depends on FB_MSM_OVERLAY
         bool "MDP overlay write back panel enable"
diff --git a/drivers/video/msm/mdp4.h b/drivers/video/msm/mdp4.h
index 1a4ce91..7b5f464 100644
--- a/drivers/video/msm/mdp4.h
+++ b/drivers/video/msm/mdp4.h
@@ -320,6 +320,7 @@
 	uint32 blt_cnt;
 	uint32 ov_cnt;
 	uint32 dmap_cnt;
+	uint32 dmae_cnt;
 	uint32 blt_end;
 	uint32 luma_align_size;
 	struct mdp4_hsic_regs hsic_regs;
@@ -409,8 +410,6 @@
 uint32 mdp4_overlay_op_mode(struct mdp4_overlay_pipe *pipe);
 void mdp4_lcdc_overlay(struct msm_fb_data_type *mfd);
 #ifdef CONFIG_FB_MSM_DTV
-void mdp4_overlay_dtv_vsync_push(struct msm_fb_data_type *mfd,
-			struct mdp4_overlay_pipe *pipe);
 void mdp4_overlay_dtv_ov_done_push(struct msm_fb_data_type *mfd,
 			struct mdp4_overlay_pipe *pipe);
 void mdp4_overlay_dtv_wait_for_ov(struct msm_fb_data_type *mfd,
@@ -420,11 +419,6 @@
 int mdp4_overlay_dtv_unset(struct msm_fb_data_type *mfd,
 			struct mdp4_overlay_pipe *pipe);
 #else
-static inline void mdp4_overlay_dtv_vsync_push(struct msm_fb_data_type *mfd,
-	struct mdp4_overlay_pipe *pipe)
-{
-	/* empty */
-}
 static inline void  mdp4_overlay_dtv_ov_done_push(struct msm_fb_data_type *mfd,
 			struct mdp4_overlay_pipe *pipe)
 {
@@ -612,6 +606,8 @@
 					struct msmfb_overlay_blt *req);
 void mdp4_lcdc_overlay_blt_start(struct msm_fb_data_type *mfd);
 void mdp4_lcdc_overlay_blt_stop(struct msm_fb_data_type *mfd);
+void mdp4_dtv_overlay_blt_start(struct msm_fb_data_type *mfd);
+void mdp4_dtv_overlay_blt_stop(struct msm_fb_data_type *mfd);
 
 int mdp4_mddi_overlay_blt_offset(int *off);
 void mdp4_mddi_overlay_blt(ulong addr);
@@ -687,6 +683,7 @@
 		struct mdp4_overlay_pipe *pipe);
 void mdp4_writeback_dma_busy_wait(struct msm_fb_data_type *mfd);
 void mdp4_overlay1_done_writeback(struct mdp_dma_data *dma);
+void mdp4_dma_e_done_dtv(void);
 
 int mdp4_writeback_start(struct fb_info *info);
 int mdp4_writeback_stop(struct fb_info *info);
diff --git a/drivers/video/msm/mdp4_overlay.c b/drivers/video/msm/mdp4_overlay.c
index ecb0a24..1fd0766 100644
--- a/drivers/video/msm/mdp4_overlay.c
+++ b/drivers/video/msm/mdp4_overlay.c
@@ -239,13 +239,27 @@
 
 	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
 
-	/* dma_p source */
 	MDP_OUTP(MDP_BASE + 0xb0004,
 			(pipe->src_height << 16 | pipe->src_width));
-	MDP_OUTP(MDP_BASE + 0xb0008, pipe->srcp0_addr);
-	MDP_OUTP(MDP_BASE + 0xb000c, pipe->srcp0_ystride);
-
-	/* dma_p dest */
+	if (pipe->blt_addr) {
+		uint32 off, bpp;
+#ifdef BLT_RGB565
+		bpp = 2; /* overlay ouput is RGB565 */
+#else
+		bpp = 3; /* overlay ouput is RGB888 */
+#endif
+		off = 0;
+		if (pipe->ov_cnt & 0x01)
+			off = pipe->src_height * pipe->src_width * bpp;
+		MDP_OUTP(MDP_BASE + 0xb0008, pipe->blt_addr + off);
+		/* RGB888, output of overlay blending */
+		MDP_OUTP(MDP_BASE + 0xb000c, pipe->src_width * bpp);
+	} else {
+		/* dma_e source */
+		MDP_OUTP(MDP_BASE + 0xb0008, pipe->srcp0_addr);
+		MDP_OUTP(MDP_BASE + 0xb000c, pipe->srcp0_ystride);
+	}
+	/* dma_e dest */
 	MDP_OUTP(MDP_BASE + 0xb0010, (pipe->dst_y << 16 | pipe->dst_x));
 
 	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
@@ -1114,7 +1128,7 @@
 		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
 
 	/*
-	 * BLT only siupport at primary display
+	 * BLT support both primary and external external
 	 */
 	if (pipe->blt_addr) {
 		int off, bpp;
@@ -1127,7 +1141,8 @@
 		data <<= 16;
 		data |= pipe->src_width;
 		outpdw(overlay_base + 0x0008, data); /* ROI, height + width */
-		if (pipe->mixer_num == MDP4_MIXER0) {
+		if (pipe->mixer_num == MDP4_MIXER0 ||
+		    pipe->mixer_num == MDP4_MIXER1) {
 			off = 0;
 			if (pipe->ov_cnt & 0x01)
 				off = pipe->src_height * pipe->src_width * bpp;
diff --git a/drivers/video/msm/mdp4_overlay_dtv.c b/drivers/video/msm/mdp4_overlay_dtv.c
index f2065fd..f454d89 100644
--- a/drivers/video/msm/mdp4_overlay_dtv.c
+++ b/drivers/video/msm/mdp4_overlay_dtv.c
@@ -264,36 +264,6 @@
 	return ret;
 }
 
-static void mdp4_overlay_dtv_wait4vsync(struct msm_fb_data_type *mfd)
-{
-	unsigned long flag;
-
-	/* enable irq */
-	spin_lock_irqsave(&mdp_spin_lock, flag);
-	mdp_enable_irq(MDP_OVERLAY1_TERM);
-	INIT_COMPLETION(dtv_pipe->comp);
-	mfd->dma->waiting = TRUE;
-	outp32(MDP_INTR_CLEAR, INTR_EXTERNAL_VSYNC);
-	mdp_intr_mask |= INTR_EXTERNAL_VSYNC;
-	outp32(MDP_INTR_ENABLE, mdp_intr_mask);
-	spin_unlock_irqrestore(&mdp_spin_lock, flag);
-	wait_for_completion_killable(&dtv_pipe->comp);
-	mdp_disable_irq(MDP_OVERLAY1_TERM);
-}
-
-void mdp4_overlay_dtv_vsync_push(struct msm_fb_data_type *mfd,
-			struct mdp4_overlay_pipe *pipe)
-{
-	mdp4_overlay_reg_flush(pipe, 1);
-	if (pipe->flags & MDP_OV_PLAY_NOWAIT)
-		return;
-
-	mdp4_overlay_dtv_wait4vsync(mfd);
-
-	/* change mdp clk while mdp is idle` */
-	mdp4_set_perf_level();
-}
-
 static void mdp4_overlay_dtv_alloc_pipe(struct msm_fb_data_type *mfd,
 		int32 ptype)
 {
@@ -406,6 +376,54 @@
 	return result;
 }
 
+static void mdp4_dtv_blt_ov_update(struct mdp4_overlay_pipe *pipe)
+{
+	uint32 off, addr;
+	int bpp;
+	char *overlay_base;
+
+	if (pipe->blt_addr == 0)
+		return;
+#ifdef BLT_RGB565
+	bpp = 2; /* overlay ouput is RGB565 */
+#else
+	bpp = 3; /* overlay ouput is RGB888 */
+#endif
+	off = (pipe->ov_cnt & 0x01) ?
+		pipe->src_height * pipe->src_width * bpp : 0;
+
+	addr = pipe->blt_addr + off;
+	pr_debug("%s overlay addr 0x%x\n", __func__, addr);
+	/* overlay 1 */
+	overlay_base = MDP_BASE + MDP4_OVERLAYPROC1_BASE;/* 0x18000 */
+	outpdw(overlay_base + 0x000c, addr);
+	outpdw(overlay_base + 0x001c, addr);
+}
+
+static inline void mdp4_dtv_blt_dmae_update(struct mdp4_overlay_pipe *pipe)
+{
+	uint32 off, addr;
+	int bpp;
+
+	if (pipe->blt_addr == 0)
+		return;
+
+#ifdef BLT_RGB565
+	bpp = 2; /* overlay ouput is RGB565 */
+#else
+	bpp = 3; /* overlay ouput is RGB888 */
+#endif
+	off =  (pipe->dmae_cnt & 0x01) ?
+		pipe->src_height * pipe->src_width * bpp : 0;
+	addr = pipe->blt_addr + off;
+	MDP_OUTP(MDP_BASE + 0xb0008, addr);
+}
+
+static inline void mdp4_overlay_dtv_ov_kick_start(void)
+{
+	outpdw(MDP_BASE + 0x0008, 0);
+}
+
 static void mdp4_overlay_dtv_ov_start(struct msm_fb_data_type *mfd)
 {
 	unsigned long flag;
@@ -414,6 +432,12 @@
 	if (mfd->ov_start)
 		return;
 
+	if (dtv_pipe->blt_addr) {
+		mdp4_dtv_blt_ov_update(dtv_pipe);
+		dtv_pipe->ov_cnt++;
+		mdp4_overlay_dtv_ov_kick_start();
+	}
+
 	spin_lock_irqsave(&mdp_spin_lock, flag);
 	mdp_enable_irq(MDP_OVERLAY1_TERM);
 	INIT_COMPLETION(dtv_pipe->comp);
@@ -444,6 +468,7 @@
 void mdp4_overlay_dtv_ov_done_push(struct msm_fb_data_type *mfd,
 			struct mdp4_overlay_pipe *pipe)
 {
+
 	mdp4_overlay_reg_flush(pipe, 1);
 	mdp4_overlay_dtv_ov_start(mfd);
 
@@ -463,6 +488,11 @@
 	mdp4_set_perf_level();
 }
 
+void mdp4_dma_e_done_dtv()
+{
+	complete(&dtv_pipe->comp);
+}
+
 void mdp4_external_vsync_dtv()
 {
 	complete_all(&dtv_pipe->comp);
@@ -473,6 +503,10 @@
  */
 void mdp4_overlay1_done_dtv()
 {
+	if (dtv_pipe->blt_addr) {
+		mdp4_dtv_blt_dmae_update(dtv_pipe);
+		dtv_pipe->dmae_cnt++;
+	}
 	complete_all(&dtv_pipe->comp);
 }
 
@@ -506,6 +540,70 @@
 }
 #endif
 
+static void mdp4_overlay_dtv_wait4dmae(struct msm_fb_data_type *mfd)
+{
+	unsigned long flag;
+
+	/* enable irq */
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	mdp_enable_irq(MDP_DMA_E_TERM);
+	INIT_COMPLETION(dtv_pipe->comp);
+	mfd->dma->waiting = TRUE;
+	outp32(MDP_INTR_CLEAR, INTR_DMA_E_DONE);
+	mdp_intr_mask |= INTR_DMA_E_DONE;
+	outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+	wait_for_completion_killable(&dtv_pipe->comp);
+	mdp_disable_irq(MDP_DMA_E_TERM);
+}
+
+static void mdp4_dtv_do_blt(struct msm_fb_data_type *mfd, int enable)
+{
+	unsigned long flag;
+	int change = 0;
+
+	if (!mfd->ov1_wb_buf->phys_addr) {
+		pr_debug("%s: no writeback buf assigned\n", __func__);
+		return;
+	}
+
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	if (enable && dtv_pipe->blt_addr == 0) {
+		dtv_pipe->blt_addr = mfd->ov1_wb_buf->phys_addr;
+		change++;
+		dtv_pipe->ov_cnt = 0;
+		dtv_pipe->dmae_cnt = 0;
+	} else if (enable == 0 && dtv_pipe->blt_addr) {
+		dtv_pipe->blt_addr = 0;
+		change++;
+	}
+	pr_debug("%s: blt_addr=%x\n", __func__, (int)dtv_pipe->blt_addr);
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+
+	if (!change)
+		return;
+
+	mdp4_overlay_dtv_wait4dmae(mfd);
+
+	MDP_OUTP(MDP_BASE + DTV_BASE, 0);	/* stop dtv */
+	msleep(20);
+	mdp4_overlayproc_cfg(dtv_pipe);
+	mdp4_overlay_dmae_xy(dtv_pipe);
+	MDP_OUTP(MDP_BASE + DTV_BASE, 1);	/* start dtv */
+}
+
+void mdp4_dtv_overlay_blt_start(struct msm_fb_data_type *mfd)
+{
+	mdp4_allocate_writeback_buf(mfd, MDP4_MIXER1);
+	mdp4_dtv_do_blt(mfd, 1);
+}
+
+void mdp4_dtv_overlay_blt_stop(struct msm_fb_data_type *mfd)
+{
+	mdp4_free_writeback_buf(mfd, MDP4_MIXER1);
+	mdp4_dtv_do_blt(mfd, 0);
+}
+
 void mdp4_dtv_overlay(struct msm_fb_data_type *mfd)
 {
 	struct mdp4_overlay_pipe *pipe;
diff --git a/drivers/video/msm/mdp4_util.c b/drivers/video/msm/mdp4_util.c
index bd8ec55..ba6c746 100644
--- a/drivers/video/msm/mdp4_util.c
+++ b/drivers/video/msm/mdp4_util.c
@@ -554,7 +554,7 @@
 		mdp_intr_mask &= ~INTR_DMA_E_DONE;
 		outp32(MDP_INTR_ENABLE, mdp_intr_mask);
 		dma->busy = FALSE;
-
+		mdp4_dma_e_done_dtv();
 		if (dma->waiting) {
 			dma->waiting = FALSE;
 			complete(&dma->comp);