msm: mdss: Add plane alpha support

Clean up the blending rule to follow blend_op set from hwc.
Use modulate alpha for plane alpha when it is not 0xff.

Change-Id: Iad7217563bcae5e89fe8e50954494f6afdcb8e79
Signed-off-by: Mayank Chopra <makchopra@codeaurora.org>
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
index aba77e3..717241d 100644
--- a/drivers/video/msm/mdss/mdss_mdp.h
+++ b/drivers/video/msm/mdss/mdss_mdp.h
@@ -333,6 +333,7 @@
 	u8 mixer_stage;
 	u8 is_fg;
 	u8 alpha;
+	u8 blend_op;
 	u8 overfetch_disable;
 	u32 transp;
 
diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c
index d1595b3..edd4c19 100644
--- a/drivers/video/msm/mdss/mdss_mdp_ctl.c
+++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c
@@ -280,11 +280,6 @@
 		pipe = mixer->stage_pipe[i];
 		if (pipe == NULL)
 			continue;
-		if (pipe->is_fg) {
-			ab_total = 0;
-			ib_total = 0;
-			max_clk_rate = 0;
-		}
 
 		if (mdss_mdp_perf_calc_pipe(pipe, &perf))
 			continue;
@@ -1207,7 +1202,8 @@
 {
 	struct mdss_mdp_pipe *pipe;
 	u32 off, blend_op, blend_stage;
-	u32 mixercfg = 0, blend_color_out = 0, bgalpha = 0;
+	u32 mixercfg = 0, blend_color_out = 0, bg_alpha_enable = 0;
+	u32 fg_alpha = 0, bg_alpha = 0;
 	int stage, secure = 0;
 
 	if (!mixer)
@@ -1227,7 +1223,7 @@
 			mixercfg = 1 << (3 * pipe->num);
 		}
 		if (pipe->src_fmt->alpha_enable)
-			bgalpha = 1;
+			bg_alpha_enable = 1;
 		secure = pipe->flags & MDP_SECURE_OVERLAY_SESSION;
 	}
 
@@ -1244,48 +1240,79 @@
 		blend_stage = stage - MDSS_MDP_STAGE_0;
 		off = MDSS_MDP_REG_LM_BLEND_OFFSET(blend_stage);
 
-		if (pipe->is_fg) {
-			bgalpha = 0;
-			if (!secure)
-				mixercfg = MDSS_MDP_LM_BORDER_COLOR;
+		blend_op = (MDSS_MDP_BLEND_FG_ALPHA_FG_CONST |
+			    MDSS_MDP_BLEND_BG_ALPHA_BG_CONST);
+		fg_alpha = pipe->alpha;
+		bg_alpha = 0xFF - pipe->alpha;
+		/* keep fg alpha */
+		blend_color_out |= 1 << (blend_stage + 1);
+
+		switch (pipe->blend_op) {
+		case BLEND_OP_OPAQUE:
 
 			blend_op = (MDSS_MDP_BLEND_FG_ALPHA_FG_CONST |
 				    MDSS_MDP_BLEND_BG_ALPHA_BG_CONST);
-			/* keep fg alpha */
-			blend_color_out |= 1 << (blend_stage + 1);
 
-			pr_debug("pnum=%d stg=%d alpha=IS_FG\n", pipe->num,
+			pr_debug("pnum=%d stg=%d op=OPAQUE\n", pipe->num,
 					stage);
-		} else if (pipe->src_fmt->alpha_enable) {
-			bgalpha = 0;
-			blend_op = (MDSS_MDP_BLEND_BG_ALPHA_FG_PIXEL |
-				    MDSS_MDP_BLEND_BG_INV_ALPHA);
-			/* keep fg alpha */
-			blend_color_out |= 1 << (blend_stage + 1);
+			break;
 
-			pr_debug("pnum=%d stg=%d alpha=FG PIXEL\n", pipe->num,
+		case BLEND_OP_PREMULTIPLIED:
+			if (pipe->src_fmt->alpha_enable) {
+				blend_op = (MDSS_MDP_BLEND_FG_ALPHA_FG_CONST |
+					    MDSS_MDP_BLEND_BG_ALPHA_FG_PIXEL);
+				if (fg_alpha != 0xff) {
+					bg_alpha = fg_alpha;
+					blend_op |=
+						MDSS_MDP_BLEND_BG_MOD_ALPHA |
+						MDSS_MDP_BLEND_BG_INV_MOD_ALPHA;
+				} else {
+					blend_op |= MDSS_MDP_BLEND_BG_INV_ALPHA;
+				}
+			}
+			pr_debug("pnum=%d stg=%d op=PREMULTIPLIED\n", pipe->num,
 					stage);
-		} else if (bgalpha) {
-			blend_op = (MDSS_MDP_BLEND_BG_ALPHA_BG_PIXEL |
-				    MDSS_MDP_BLEND_FG_ALPHA_BG_PIXEL |
-				    MDSS_MDP_BLEND_FG_INV_ALPHA);
-			/* keep bg alpha */
-			pr_debug("pnum=%d stg=%d alpha=BG_PIXEL\n", pipe->num,
+			break;
+
+		case BLEND_OP_COVERAGE:
+			if (pipe->src_fmt->alpha_enable) {
+				blend_op = (MDSS_MDP_BLEND_FG_ALPHA_FG_PIXEL |
+					    MDSS_MDP_BLEND_BG_ALPHA_FG_PIXEL);
+				if (fg_alpha != 0xff) {
+					bg_alpha = fg_alpha;
+					blend_op |=
+					       MDSS_MDP_BLEND_FG_MOD_ALPHA |
+					       MDSS_MDP_BLEND_FG_INV_MOD_ALPHA |
+					       MDSS_MDP_BLEND_BG_MOD_ALPHA |
+					       MDSS_MDP_BLEND_BG_INV_MOD_ALPHA;
+				} else {
+					blend_op |= MDSS_MDP_BLEND_BG_INV_ALPHA;
+				}
+			}
+			pr_debug("pnum=%d stg=%d op=COVERAGE\n", pipe->num,
 					stage);
-		} else {
+			break;
+
+		default:
 			blend_op = (MDSS_MDP_BLEND_FG_ALPHA_FG_CONST |
 				    MDSS_MDP_BLEND_BG_ALPHA_BG_CONST);
-			pr_debug("pnum=%d stg=%d alpha=CONST\n", pipe->num,
+			pr_debug("pnum=%d stg=%d op=NONE\n", pipe->num,
 					stage);
+			break;
 		}
 
+		if (!pipe->src_fmt->alpha_enable && bg_alpha_enable)
+			blend_color_out = 0;
+
 		mixercfg |= stage << (3 * pipe->num);
 
+		pr_debug("stg=%d op=%x fg_alpha=%x bg_alpha=%x\n", stage,
+					blend_op, fg_alpha, bg_alpha);
 		mdp_mixer_write(mixer, off + MDSS_MDP_REG_LM_OP_MODE, blend_op);
 		mdp_mixer_write(mixer, off + MDSS_MDP_REG_LM_BLEND_FG_ALPHA,
-				   pipe->alpha);
+				   fg_alpha);
 		mdp_mixer_write(mixer, off + MDSS_MDP_REG_LM_BLEND_BG_ALPHA,
-				   0xFF - pipe->alpha);
+				   bg_alpha);
 	}
 
 	if (mixer->cursor_enabled)
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index c4dee86..6252e17 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -491,6 +491,16 @@
 	pipe->is_fg = req->is_fg;
 	pipe->alpha = req->alpha;
 	pipe->transp = req->transp_mask;
+	pipe->blend_op = req->blend_op;
+	if (pipe->blend_op == BLEND_OP_NOT_DEFINED)
+		pipe->blend_op = fmt->alpha_enable ?
+					BLEND_OP_PREMULTIPLIED :
+					BLEND_OP_OPAQUE;
+
+	if (!fmt->alpha_enable && (pipe->blend_op != BLEND_OP_OPAQUE))
+		pr_warn("Unintended blend_op %d on layer with no alpha plane\n",
+			pipe->blend_op);
+
 	pipe->overfetch_disable = fmt->is_yuv &&
 			!(pipe->flags & MDP_SOURCE_ROTATED_90);
 
diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h
index 2455212..fab9301 100644
--- a/include/linux/msm_mdp.h
+++ b/include/linux/msm_mdp.h
@@ -405,6 +405,33 @@
 	struct mdp_hist_lut_data hist_lut_cfg;
 };
 
+/**
+ * enum mdss_mdp_blend_op - Different blend operations set by userspace
+ *
+ * @BLEND_OP_NOT_DEFINED:    No blend operation defined for the layer.
+ * @BLEND_OP_OPAQUE:         Apply a constant blend operation. The layer
+ *                           would appear opaque in case fg plane alpha is
+ *                           0xff.
+ * @BLEND_OP_PREMULTIPLIED:  Apply source over blend rule. Layer already has
+ *                           alpha pre-multiplication done. If fg plane alpha
+ *                           is less than 0xff, apply modulation as well. This
+ *                           operation is intended on layers having alpha
+ *                           channel.
+ * @BLEND_OP_COVERAGE:       Apply source over blend rule. Layer is not alpha
+ *                           pre-multiplied. Apply pre-multiplication. If fg
+ *                           plane alpha is less than 0xff, apply modulation as
+ *                           well.
+ * @BLEND_OP_MAX:            Used to track maximum blend operation possible by
+ *                           mdp.
+ */
+enum mdss_mdp_blend_op {
+	BLEND_OP_NOT_DEFINED = 0,
+	BLEND_OP_OPAQUE,
+	BLEND_OP_PREMULTIPLIED,
+	BLEND_OP_COVERAGE,
+	BLEND_OP_MAX,
+};
+
 struct mdp_overlay {
 	struct msmfb_img src;
 	struct mdp_rect src_rect;
@@ -412,6 +439,7 @@
 	uint32_t z_order;	/* stage number */
 	uint32_t is_fg;		/* control alpha & transp */
 	uint32_t alpha;
+	uint32_t blend_op;
 	uint32_t transp_mask;
 	uint32_t flags;
 	uint32_t id;