msm_fb: display: enable border color fill on external display

Enable border color fill feature on external display. This avoids usage
of RGB pipe for background border fill and can now be used as overlay
layer for UI display.

Change-Id: I652ab3ffe2c43260590e4e5a2ffc986340c4a8c0
Signed-off-by: Adrian Salido-Moreno <adrianm@codeaurora.org>
diff --git a/drivers/video/msm/mdp4.h b/drivers/video/msm/mdp4.h
index 2eb08b8..2bcac9c 100644
--- a/drivers/video/msm/mdp4.h
+++ b/drivers/video/msm/mdp4.h
@@ -139,12 +139,15 @@
 	OVERLAY_PIPE_RGB2,
 	OVERLAY_PIPE_VG1,	/* video/graphic */
 	OVERLAY_PIPE_VG2,
+	OVERLAY_PIPE_RGB3,
+	OVERLAY_PIPE_VG3,
 	OVERLAY_PIPE_MAX
 };
 
 enum {
 	OVERLAY_TYPE_RGB,
-	OVERLAY_TYPE_VIDEO
+	OVERLAY_TYPE_VIDEO,
+	OVERLAY_TYPE_BF
 };
 
 enum {
@@ -216,6 +219,10 @@
 #define MDP4_OP_SCALEX_FIR 		(0 << 2)
 #define MDP4_OP_SCALEX_MN_PHASE 	(1 << 2)
 #define MDP4_OP_SCALEX_PIXEL_RPT 	(2 << 2)
+#define MDP4_OP_SCALE_RGB_PIXEL_RPT	(0 << 3)
+#define MDP4_OP_SCALE_RGB_BILINEAR	(1 << 3)
+#define MDP4_OP_SCALE_ALPHA_PIXEL_RPT	(0 << 2)
+#define MDP4_OP_SCALE_ALPHA_BILINEAR	(1 << 2)
 #define MDP4_OP_SCALEY_EN	BIT(1)
 #define MDP4_OP_SCALEX_EN	BIT(0)
 
@@ -411,6 +418,17 @@
 }
 #endif
 
+static inline int mdp4_overlay_borderfill_supported(void)
+{
+	unsigned int mdp_hw_version;
+	mdp_hw_version = inpdw(MDP_BASE + 0x0); /* MDP_HW_VERSION */
+	return (mdp_hw_version >= 0x0402030b);
+}
+
+int mdp4_overlay_dtv_set(struct msm_fb_data_type *mfd,
+			struct mdp4_overlay_pipe *pipe);
+int mdp4_overlay_dtv_unset(struct msm_fb_data_type *mfd,
+			struct mdp4_overlay_pipe *pipe);
 void mdp4_dtv_overlay(struct msm_fb_data_type *mfd);
 int mdp4_dtv_on(struct platform_device *pdev);
 int mdp4_dtv_off(struct platform_device *pdev);
@@ -561,6 +579,7 @@
 int mdp4_mddi_overlay_blt_offset(int *off);
 void mdp4_mddi_overlay_blt(ulong addr);
 void mdp4_overlay_panel_mode(int mixer_num, uint32 mode);
+void mdp4_overlay_panel_mode_unset(int mixer_num, uint32 mode);
 int mdp4_overlay_mixer_play(int mixer_num);
 uint32 mdp4_overlay_panel_list(void);
 void mdp4_lcdc_overlay_kickoff(struct msm_fb_data_type *mfd,
diff --git a/drivers/video/msm/mdp4_overlay.c b/drivers/video/msm/mdp4_overlay.c
index 84beb6a..72a11a2 100644
--- a/drivers/video/msm/mdp4_overlay.c
+++ b/drivers/video/msm/mdp4_overlay.c
@@ -71,6 +71,18 @@
 			.pipe_num = OVERLAY_PIPE_VG2,
 			.pipe_ndx = 4,
 		},
+		{
+			.pipe_type = OVERLAY_TYPE_BF,
+			.pipe_num = OVERLAY_PIPE_RGB3,
+			.pipe_ndx = 5,
+			.mixer_num = MDP4_MIXER0,
+		},
+		{
+			.pipe_type = OVERLAY_TYPE_BF,
+			.pipe_num = OVERLAY_PIPE_VG3,
+			.pipe_ndx = 6,
+			.mixer_num = MDP4_MIXER1,
+		},
 	},
 };
 
@@ -108,6 +120,11 @@
 	ctrl->panel_mode |= mode;
 }
 
+void mdp4_overlay_panel_mode_unset(int mixer_num, uint32 mode)
+{
+	ctrl->panel_mode &= ~mode;
+}
+
 uint32 mdp4_overlay_panel_list(void)
 {
 	return ctrl->panel_mode;
@@ -339,11 +356,8 @@
 
 static void mdp4_scale_setup(struct mdp4_overlay_pipe *pipe)
 {
-	int ptype;
-
 	pipe->phasex_step = MDP4_VG_PHASE_STEP_DEFAULT;
 	pipe->phasey_step = MDP4_VG_PHASE_STEP_DEFAULT;
-	ptype = mdp4_overlay_format2type(pipe->src_format);
 
 	if (pipe->dst_h && pipe->src_h != pipe->dst_h) {
 		if (pipe->dst_h > pipe->src_h * 8)	/* too much */
@@ -351,10 +365,15 @@
 		pipe->op_mode |= MDP4_OP_SCALEY_EN;
 
 		if (pipe->pipe_num >= OVERLAY_PIPE_VG1) {
-			if (pipe->dst_h <= (pipe->src_h / 4))
+			if (pipe->alpha_enable)
+				pipe->op_mode |= MDP4_OP_SCALEY_PIXEL_RPT;
+			else if (pipe->dst_h <= (pipe->src_h / 4))
 				pipe->op_mode |= MDP4_OP_SCALEY_MN_PHASE;
 			else
 				pipe->op_mode |= MDP4_OP_SCALEY_FIR;
+		} else { /* RGB pipe */
+			pipe->op_mode |= MDP4_OP_SCALE_RGB_BILINEAR;
+			pipe->op_mode |= MDP4_OP_SCALE_ALPHA_BILINEAR;
 		}
 
 		pipe->phasey_step = mdp4_scale_phase_step(29,
@@ -367,10 +386,15 @@
 		pipe->op_mode |= MDP4_OP_SCALEX_EN;
 
 		if (pipe->pipe_num >= OVERLAY_PIPE_VG1) {
-			if (pipe->dst_w <= (pipe->src_w / 4))
+			if (pipe->alpha_enable)
+				pipe->op_mode |= MDP4_OP_SCALEX_PIXEL_RPT;
+			else if (pipe->dst_w <= (pipe->src_w / 4))
 				pipe->op_mode |= MDP4_OP_SCALEX_MN_PHASE;
 			else
 				pipe->op_mode |= MDP4_OP_SCALEX_FIR;
+		} else { /* RGB pipe */
+			pipe->op_mode |= MDP4_OP_SCALE_RGB_BILINEAR;
+			pipe->op_mode |= MDP4_OP_SCALE_ALPHA_BILINEAR;
 		}
 
 		pipe->phasex_step = mdp4_scale_phase_step(29,
@@ -1212,7 +1236,9 @@
 	if (mixer == MDP4_MIXER1)
 		stage += 8;
 
-	if (pipe->pipe_num >= OVERLAY_PIPE_VG1) {/* VG1 and VG2 */
+	if (pipe->pipe_type == OVERLAY_TYPE_BF) {
+		snum = 16 + (4 * mixer);
+	} else if (pipe->pipe_num >= OVERLAY_PIPE_VG1) {/* VG1 and VG2 */
 		pnum -= OVERLAY_PIPE_VG1; /* start from 0 */
 		snum = 0;
 		snum += (4 * pnum);
@@ -1256,7 +1282,9 @@
 	if (mixer == MDP4_MIXER1)
 		stage += 8;
 
-	if (pipe->pipe_num >= OVERLAY_PIPE_VG1) {/* VG1 and VG2 */
+	if (pipe->pipe_type == OVERLAY_TYPE_BF) {
+		snum = 16 + (4 * mixer);
+	} else if (pipe->pipe_num >= OVERLAY_PIPE_VG1) {
 		pnum -= OVERLAY_PIPE_VG1; /* start from 0 */
 		snum = 0;
 		snum += (4 * pnum);
@@ -1283,8 +1311,12 @@
 	struct mdp4_overlay_pipe *bg_pipe;
 	unsigned char *overlay_base, *rgb_base;
 	uint32 c0, c1, c2, blend_op, constant_color = 0, rgb_src_format;
+	uint32 fg_color3_out;
 	int off;
 
+	if (pipe->mixer_stage == MDP4_MIXER_STAGE_BASE)
+		return;
+
 	if (pipe->mixer_num) 	/* mixer number, /dev/fb0, /dev/fb1 */
 		overlay_base = MDP_BASE + MDP4_OVERLAYPROC1_BASE;/* 0x18000 */
 	else
@@ -1294,7 +1326,7 @@
 	off = 0x20 * (pipe->mixer_stage - MDP4_MIXER_STAGE0);
 
 	bg_pipe = mdp4_overlay_stage_pipe(pipe->mixer_num,
-					MDP4_MIXER_STAGE_BASE);
+					  MDP4_MIXER_STAGE_BASE);
 	if (bg_pipe == NULL) {
 		pr_err("%s: Error: no bg_pipe\n", __func__);
 		return;
@@ -1302,14 +1334,15 @@
 
 	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
 
-	blend_op = 0;
+	blend_op = (MDP4_BLEND_FG_ALPHA_FG_CONST |
+		    MDP4_BLEND_BG_ALPHA_BG_CONST);
+	outpdw(overlay_base + off + 0x108, pipe->alpha);
+	outpdw(overlay_base + off + 0x10c, 0xff - pipe->alpha);
+	fg_color3_out = 0; /* keep bg alpha by default */
 
 	if (pipe->is_fg) {
-		blend_op |= (MDP4_BLEND_FG_ALPHA_FG_CONST |
-				MDP4_BLEND_BG_ALPHA_BG_CONST);
-		outpdw(overlay_base + off + 0x108, pipe->alpha);
-		outpdw(overlay_base + off + 0x10c, 0xff - pipe->alpha);
-		if (pipe->alpha == 0xff) {
+		if (pipe->alpha == 0xff &&
+		    bg_pipe->pipe_num <= OVERLAY_PIPE_RGB2) {
 			rgb_base = MDP_BASE + MDP4_RGB_BASE;
 			rgb_base += MDP4_RGB_OFF * bg_pipe->pipe_num;
 			rgb_src_format = inpdw(rgb_base + 0x50);
@@ -1317,21 +1350,20 @@
 			outpdw(rgb_base + 0x50, rgb_src_format);
 			outpdw(rgb_base + 0x1008, constant_color);
 		}
-	} else {
-		if (bg_pipe->alpha_enable && pipe->alpha_enable) {
-			/* both pipe have alpha */
-			blend_op |= (MDP4_BLEND_FG_ALPHA_BG_PIXEL |
-				MDP4_BLEND_FG_INV_ALPHA |
-				MDP4_BLEND_BG_ALPHA_BG_PIXEL);
-		} else if (bg_pipe->alpha_enable && pipe->alpha_enable == 0) {
-			/* no alpha on both pipe */
-			blend_op = (MDP4_BLEND_BG_ALPHA_BG_PIXEL |
-				MDP4_BLEND_FG_ALPHA_BG_PIXEL |
-				MDP4_BLEND_FG_INV_ALPHA);
-		}
+	} else if (pipe->alpha_enable) {
+		/* pick fg alpha */
+		blend_op = (MDP4_BLEND_BG_ALPHA_FG_PIXEL |
+			    MDP4_BLEND_FG_ALPHA_FG_PIXEL |
+			    MDP4_BLEND_BG_INV_ALPHA);
+		fg_color3_out = 1; /* keep fg alpha */
+	} else if (bg_pipe->alpha_enable) {
+		/* pick bg alpha */
+		blend_op = (MDP4_BLEND_BG_ALPHA_BG_PIXEL |
+			    MDP4_BLEND_FG_ALPHA_BG_PIXEL |
+			    MDP4_BLEND_FG_INV_ALPHA);
+		fg_color3_out = 0; /* keep bg alpha */
 	}
 
-
 	if (pipe->transp != MDP_TRANSP_NOP) {
 		if (pipe->is_fg) {
 			transp_color_key(pipe->src_format, pipe->transp,
@@ -1363,6 +1395,7 @@
 	}
 
 	outpdw(overlay_base + off + 0x104, blend_op);
+	outpdw(overlay_base + (off << 5) + 0x1004, fg_color3_out);
 
 	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
 }
@@ -1377,7 +1410,7 @@
 	else
 		bits |= 0x01;
 
-	if (all) {
+	if (all && pipe->pipe_type != OVERLAY_TYPE_BF) {
 		if (pipe->pipe_num <= OVERLAY_PIPE_RGB2) {
 			if (pipe->pipe_num == OVERLAY_PIPE_RGB2)
 				bits |= 0x20;
@@ -1438,6 +1471,9 @@
 		if ((pipe->pipe_used == 0) && ((pipe->pipe_type == ptype) ||
 		    (ptype == OVERLAY_TYPE_RGB &&
 		     pipe->pipe_type == OVERLAY_TYPE_VIDEO))) {
+			if (ptype == OVERLAY_TYPE_BF &&
+			    mixer != pipe->mixer_num)
+				continue;
 			init_completion(&pipe->comp);
 			init_completion(&pipe->dmas_comp);
 			pr_info("%s: pipe=%x ndx=%d num=%d\n", __func__,
@@ -1454,19 +1490,21 @@
 
 void mdp4_overlay_pipe_free(struct mdp4_overlay_pipe *pipe)
 {
-	uint32 ptype, num, ndx;
+	uint32 ptype, num, ndx, mixer;
 
 	pr_info("%s: pipe=%x ndx=%d\n", __func__, (int)pipe, pipe->pipe_ndx);
 
 	ptype = pipe->pipe_type;
 	num = pipe->pipe_num;
 	ndx = pipe->pipe_ndx;
+	mixer = pipe->mixer_num;
 
 	memset(pipe, 0, sizeof(*pipe));
 
 	pipe->pipe_type = ptype;
 	pipe->pipe_num = num;
 	pipe->pipe_ndx = ndx;
+	pipe->mixer_num = mixer;
 }
 
 int mdp4_overlay_req_check(uint32 id, uint32 z_order, uint32 mixer)
@@ -1681,17 +1719,9 @@
 		return -ENOMEM;
 	}
 
-	/* no down scale at rgb pipe */
-	if (pipe->pipe_num <= OVERLAY_PIPE_RGB2) {
-		if ((req->src_rect.h > req->dst_rect.h) ||
-			(req->src_rect.w > req->dst_rect.w)) {
-				pr_err("%s: h>h || w>w!\n", __func__);
-				return -ERANGE;
-			}
-	}
-
 	pipe->src_format = req->src.format;
 	ret = mdp4_overlay_format2pipe(pipe);
+
 	if (ret < 0) {
 		pr_err("%s: mdp4_overlay_format2pipe!\n", __func__);
 		return ret;
@@ -1886,6 +1916,9 @@
 
 	*req = pipe->req_data;
 
+	if (mdp4_overlay_borderfill_supported())
+		req->flags |= MDP_BORDERFILL_SUPPORTED;
+
 	return 0;
 }
 
@@ -2088,6 +2121,10 @@
 			mdp4_overlay_status_write(MDP4_OVERLAY_TYPE_SET, true);
 	}
 
+	if (ctrl->panel_mode & MDP4_PANEL_DTV &&
+	    pipe->mixer_num == MDP4_MIXER1)
+		mdp4_overlay_dtv_set(mfd, pipe);
+
 	if (new_perf_level != perf_level) {
 		mdp4_update_perf_level(perf_level);
 
@@ -2110,10 +2147,8 @@
 				mdp4_overlay_dtv_vsync_push(mfd, pipe);
 		}
 	}
-
 	mutex_unlock(&mfd->dma->ov_mutex);
 
-
 #ifdef CONFIG_MSM_BUS_SCALING
 	if (pipe->mixer_num == MDP4_MIXER0) {
 		mdp_bus_scale_update_request(OVERLAY_BUS_SCALE_TABLE_BASE
@@ -2205,18 +2240,10 @@
 		mfd->ov0_blt_state = mfd->use_ov0_blt;
 
 	}
-#ifdef CONFIG_FB_MSM_DTV
 	else {	/* mixer1, DTV, ATV */
-		if (ctrl->panel_mode & MDP4_PANEL_DTV) {
-			if (mfd->panel_power_on) {
-				flags = pipe->flags;
-				pipe->flags &= ~MDP_OV_PLAY_NOWAIT;
-				mdp4_overlay_dtv_vsync_push(mfd, pipe);
-				pipe->flags = flags;
-			}
-		}
+		if (ctrl->panel_mode & MDP4_PANEL_DTV)
+			mdp4_overlay_dtv_unset(mfd, pipe);
 	}
-#endif
 
 	/* Reset any HSIC settings to default */
 	if (pipe->flags & MDP_DPP_HSIC) {
diff --git a/drivers/video/msm/mdp4_overlay_dtv.c b/drivers/video/msm/mdp4_overlay_dtv.c
index d0c86d0..c8135d4 100644
--- a/drivers/video/msm/mdp4_overlay_dtv.c
+++ b/drivers/video/msm/mdp4_overlay_dtv.c
@@ -54,7 +54,7 @@
 
 static struct mdp4_overlay_pipe *dtv_pipe;
 
-int mdp4_dtv_on(struct platform_device *pdev)
+static int mdp4_dtv_start(struct msm_fb_data_type *mfd)
 {
 	int dtv_width;
 	int dtv_height;
@@ -86,16 +86,8 @@
 	int data_en_polarity;
 	int hsync_start_x;
 	int hsync_end_x;
-	uint8 *buf;
-	int bpp, ptype;
-	uint32 format;
 	struct fb_info *fbi;
 	struct fb_var_screeninfo *var;
-	struct msm_fb_data_type *mfd;
-	struct mdp4_overlay_pipe *pipe;
-	int ret;
-
-	mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
 
 	if (!mfd)
 		return -ENODEV;
@@ -103,50 +95,12 @@
 	if (mfd->key != MFD_KEY)
 		return -EINVAL;
 
+	if (dtv_pipe == NULL)
+		return -EINVAL;
+
 	fbi = mfd->fbi;
 	var = &fbi->var;
 
-	bpp = fbi->var.bits_per_pixel / 8;
-	buf = (uint8 *) fbi->fix.smem_start;
-	buf += fbi->var.xoffset * bpp +
-		fbi->var.yoffset * fbi->fix.line_length;
-
-	if (bpp == 2)
-		format = MDP_RGB_565;
-	else if (bpp == 3)
-		format = MDP_RGB_888;
-	else {
-#ifdef CONFIG_FB_MSM_HDMI_AS_PRIMARY
-		format = MSMFB_DEFAULT_TYPE;
-#else
-		format = MDP_ARGB_8888;
-#endif
-	}
-
-	if (dtv_pipe == NULL) {
-		ptype = mdp4_overlay_format2type(format);
-		if (ptype < 0)
-			printk(KERN_INFO "%s: format2type failed\n", __func__);
-		pipe = mdp4_overlay_pipe_alloc(ptype, MDP4_MIXER1);
-		if (pipe == NULL) {
-			printk(KERN_INFO "%s: pipe_alloc failed\n", __func__);
-			return -EBUSY;
-		}
-		pipe->pipe_used++;
-		pipe->mixer_stage  = MDP4_MIXER_STAGE_BASE;
-		pipe->mixer_num  = MDP4_MIXER1;
-		pipe->src_format = format;
-		mdp4_overlay_panel_mode(pipe->mixer_num, MDP4_PANEL_DTV);
-		ret = mdp4_overlay_format2pipe(pipe);
-		if (ret < 0)
-			printk(KERN_INFO "%s: format2type failed\n", __func__);
-
-		dtv_pipe = pipe; /* keep it */
-	} else {
-		pipe = dtv_pipe;
-	}
-
-	/* MDP cmd block enable */
 	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
 #ifdef CONFIG_FB_MSM_HDMI_AS_PRIMARY
 	if (is_mdp4_hw_reset()) {
@@ -154,24 +108,8 @@
 		outpdw(MDP_BASE + 0x0038, mdp4_display_intf);
 	}
 #endif
-	pipe->src_height = fbi->var.yres;
-	pipe->src_width = fbi->var.xres;
-	pipe->src_h = fbi->var.yres;
-	pipe->src_w = fbi->var.xres;
-	pipe->src_y = 0;
-	pipe->src_x = 0;
-	pipe->srcp0_addr = (uint32) buf;
-	pipe->srcp0_ystride = fbi->fix.line_length;
-
-	mdp4_overlay_dmae_xy(pipe);	/* dma_e */
 	mdp4_overlay_dmae_cfg(mfd, 0);
 
-	mdp4_overlay_rgb_setup(pipe);
-
-	mdp4_mixer_stage_up(pipe);
-
-	mdp4_overlayproc_cfg(pipe);
-
 	/*
 	 * DTV timing setting
 	 */
@@ -255,24 +193,19 @@
 	/* Test pattern 8 x 8 pixel */
 	/* MDP_OUTP(MDP_BASE + DTV_BASE + 0x4C, 0x80000808); */
 
-	ret = panel_next_on(pdev);
-	if (ret == 0) {
-		/* enable DTV block */
-		MDP_OUTP(MDP_BASE + DTV_BASE, 1);
-		mdp_pipe_ctrl(MDP_OVERLAY1_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
-		dev_info(&pdev->dev, "mdp4_overlay_dtv: on");
-	} else {
-		dev_warn(&pdev->dev, "mdp4_overlay_dtv: panel_next_on failed");
-	}
+	/* enable DTV block */
+	MDP_OUTP(MDP_BASE + DTV_BASE, 1);
+	mdp_pipe_ctrl(MDP_OVERLAY1_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
 	/* MDP cmd block disable */
 	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
 
-	return ret;
+	return 0;
 }
 
-int mdp4_dtv_off(struct platform_device *pdev)
+static int mdp4_dtv_stop(struct msm_fb_data_type *mfd)
 {
-	int ret = 0;
+	if (dtv_pipe == NULL)
+		return -EINVAL;
 
 	/* MDP cmd block enable */
 	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
@@ -286,24 +219,56 @@
 	 * the last frame finishes
 	*/
 	msleep(20);
-	pr_info("%s\n", __func__);
 
-	ret = panel_next_off(pdev);
+	return 0;
+}
 
-	/* dis-engage rgb2 from mixer1 */
-	if (dtv_pipe)
+int mdp4_dtv_on(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+	int ret = 0;
+
+	mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return -ENODEV;
+
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	mdp4_overlay_panel_mode(MDP4_MIXER1, MDP4_PANEL_DTV);
+	if (dtv_pipe != NULL)
+		ret = mdp4_dtv_start(mfd);
+
+	ret = panel_next_on(pdev);
+	if (ret != 0)
+		dev_warn(&pdev->dev, "mdp4_overlay_dtv: panel_next_on failed");
+
+	dev_info(&pdev->dev, "mdp4_overlay_dtv: on");
+
+	return ret;
+}
+
+int mdp4_dtv_off(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+	int ret = 0;
+
+	mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
+
+	if (dtv_pipe != NULL) {
 		mdp4_mixer_stage_down(dtv_pipe);
-
-	/*
-	 * wait for another vsync == 16.6 ms to make sure
-	 * rgb2 dis-engaged
-	*/
-	msleep(20);
-	if (dtv_pipe) {
+		mdp4_dtv_stop(mfd);
 		mdp4_overlay_pipe_free(dtv_pipe);
 		dtv_pipe = NULL;
 	}
+	mdp4_overlay_panel_mode_unset(MDP4_MIXER1, MDP4_PANEL_DTV);
 
+	ret = panel_next_off(pdev);
+
+	msleep(20);
+
+	dev_info(&pdev->dev, "mdp4_overlay_dtv: off");
 	return ret;
 }
 
@@ -327,7 +292,6 @@
 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;
@@ -338,6 +302,114 @@
 	mdp4_set_perf_level();
 }
 
+static void mdp4_overlay_dtv_alloc_pipe(struct msm_fb_data_type *mfd,
+		int32 ptype)
+{
+	int ret = 0;
+	struct fb_info *fbi = mfd->fbi;
+	struct mdp4_overlay_pipe *pipe;
+
+	if (dtv_pipe != NULL)
+		return;
+
+	pr_debug("%s: ptype=%d\n", __func__, ptype);
+
+	pipe = mdp4_overlay_pipe_alloc(ptype, MDP4_MIXER1);
+	if (pipe == NULL) {
+		pr_err("%s: pipe_alloc failed\n", __func__);
+		return;
+	}
+	pipe->pipe_used++;
+	pipe->mixer_stage = MDP4_MIXER_STAGE_BASE;
+	pipe->mixer_num = MDP4_MIXER1;
+
+	if (ptype == OVERLAY_TYPE_BF) {
+		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+		/* LSP_BORDER_COLOR */
+		MDP_OUTP(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x5004,
+			((0x0 & 0xFFF) << 16) |	/* 12-bit B */
+			(0x0 & 0xFFF));		/* 12-bit G */
+		/* MSP_BORDER_COLOR */
+		MDP_OUTP(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x5008,
+			(0x0 & 0xFFF));		/* 12-bit R */
+		mdp_pipe_ctrl(MDP_OVERLAY1_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+		pipe->src_format = MDP_ARGB_8888;
+	} else {
+		switch (mfd->ibuf.bpp) {
+		case 2:
+			pipe->src_format = MDP_RGB_565;
+			break;
+		case 3:
+			pipe->src_format = MDP_RGB_888;
+			break;
+		case 4:
+		default:
+#ifdef CONFIG_FB_MSM_HDMI_AS_PRIMARY
+			pipe->src_format = MSMFB_DEFAULT_TYPE;
+#else
+			pipe->src_format = MDP_ARGB_8888;
+#endif
+			break;
+		}
+	}
+
+	pipe->src_height = fbi->var.yres;
+	pipe->src_width = fbi->var.xres;
+	pipe->src_h = fbi->var.yres;
+	pipe->src_w = fbi->var.xres;
+	pipe->src_y = 0;
+	pipe->src_x = 0;
+	pipe->srcp0_ystride = fbi->fix.line_length;
+
+	ret = mdp4_overlay_format2pipe(pipe);
+	if (ret < 0)
+		pr_warn("%s: format2type failed\n", __func__);
+
+	mdp4_overlay_dmae_xy(pipe);	/* dma_e */
+	mdp4_overlayproc_cfg(pipe);
+	mdp4_mixer_stage_up(pipe);
+
+	dtv_pipe = pipe; /* keep it */
+}
+
+int mdp4_overlay_dtv_set(struct msm_fb_data_type *mfd,
+			struct mdp4_overlay_pipe *pipe)
+{
+	if (dtv_pipe != NULL)
+		return 0;
+
+	if (pipe != NULL && pipe->mixer_stage == MDP4_MIXER_STAGE_BASE &&
+			pipe->pipe_type == OVERLAY_TYPE_RGB)
+		dtv_pipe = pipe; /* keep it */
+	else if (mdp4_overlay_borderfill_supported())
+		mdp4_overlay_dtv_alloc_pipe(mfd, OVERLAY_TYPE_BF);
+	else
+		mdp4_overlay_dtv_alloc_pipe(mfd, OVERLAY_TYPE_RGB);
+	if (dtv_pipe == NULL)
+		return -ENODEV;
+	return mdp4_dtv_start(mfd);
+}
+
+int mdp4_overlay_dtv_unset(struct msm_fb_data_type *mfd,
+			struct mdp4_overlay_pipe *pipe)
+{
+	unsigned int flags;
+	int result = 0;
+
+	flags = pipe->flags;
+	pipe->flags &= ~MDP_OV_PLAY_NOWAIT;
+	mdp4_overlay_dtv_vsync_push(mfd, pipe);
+	pipe->flags = flags;
+
+	if (pipe->mixer_stage == MDP4_MIXER_STAGE_BASE &&
+			pipe->pipe_type == OVERLAY_TYPE_RGB) {
+		result = mdp4_dtv_stop(mfd);
+		dtv_pipe = NULL;
+	}
+	return result;
+}
+
 static void mdp4_overlay_dtv_ov_start(struct msm_fb_data_type *mfd)
 {
 	unsigned long flag;
@@ -370,7 +442,6 @@
 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);
 
@@ -439,26 +510,25 @@
 
 void mdp4_dtv_overlay(struct msm_fb_data_type *mfd)
 {
-	struct fb_info *fbi = mfd->fbi;
-	uint8 *buf;
-	int bpp;
 	struct mdp4_overlay_pipe *pipe;
 
 	if (!mfd->panel_power_on)
 		return;
 
-	/* no need to power on cmd block since it's lcdc mode */
-	bpp = fbi->var.bits_per_pixel / 8;
-	buf = (uint8 *) fbi->fix.smem_start;
-	buf += fbi->var.xoffset * bpp +
-		fbi->var.yoffset * fbi->fix.line_length;
-
 	mutex_lock(&mfd->dma->ov_mutex);
+	if (dtv_pipe == NULL) {
+		if (mdp4_overlay_dtv_set(mfd, NULL)) {
+			pr_warn("%s: dtv_pipe == NULL\n", __func__);
+			mutex_unlock(&mfd->dma->ov_mutex);
+			return;
+		}
+	}
 
 	pipe = dtv_pipe;
-	pipe->srcp0_addr = (uint32) buf;
-	mdp4_overlay_rgb_setup(pipe);
-
+	if (pipe->pipe_type == OVERLAY_TYPE_RGB) {
+		pipe->srcp0_addr = (uint32) mfd->ibuf.buf;
+		mdp4_overlay_rgb_setup(pipe);
+	}
 	if (mfd->ov_start) {
 		mdp4_overlay_dtv_wait4_ov_done(mfd, pipe);
 		mfd->ov_end = true;