From: Antti Hatala <ahatala@nvidia.com>
Date: Wed, 8 Sep 2010 23:09:31 +0000 (-0700)
Subject: video: tegra: add z order blending, output position, and stride to overlays
X-Git-Tag: firefly_0821_release~9833^2~220
X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=d546987a68d4ec071425affed8e12f408dc88777;p=firefly-linux-kernel-4.4.55.git

video: tegra: add z order blending, output position, and stride to overlays

Change-Id: I7439f60bfa3264bec9b1447fd970eef9e4c089d9
Signed-off-by: Erik Gilling <konkers@android.com>
---

diff --git a/arch/arm/mach-tegra/include/mach/dc.h b/arch/arm/mach-tegra/include/mach/dc.h
index 1a73bae4d237..436a101ded2d 100644
--- a/arch/arm/mach-tegra/include/mach/dc.h
+++ b/arch/arm/mach-tegra/include/mach/dc.h
@@ -25,18 +25,6 @@
 #define TEGRA_MAX_DC		2
 #define DC_N_WINDOWS		3
 
-struct tegra_dc_blend {
-	u32	nokey;
-	u32	one_win;
-	u32	two_win_x;
-	u32	two_win_y;
-	u32	three_win_xy;
-};
-
-#define BLEND(key, control, weight0, weight1)				\
-	(CKEY_ ## key | BLEND_CONTROL_ ## control |			\
-	 BLEND_WEIGHT0(weight0) | BLEND_WEIGHT0(weight1))
-
 struct tegra_dc_mode {
 	int	pclk;
 	int	h_ref_to_sync;
@@ -96,19 +84,27 @@ struct tegra_dc_win {
 
 	void			*virt_addr;
 	dma_addr_t		phys_addr;
+	unsigned		stride;
 	unsigned		x;
 	unsigned		y;
 	unsigned		w;
 	unsigned		h;
+	unsigned		out_x;
+	unsigned		out_y;
 	unsigned		out_w;
 	unsigned		out_h;
+	unsigned		z;
 
 	int			dirty;
 	struct tegra_dc		*dc;
 };
 
 #define TEGRA_WIN_FLAG_ENABLED		(1 << 0)
-#define TEGRA_WIN_FLAG_COLOR_EXPAND	(1 << 1)
+#define TEGRA_WIN_FLAG_BLEND_PREMULT	(1 << 1)
+#define TEGRA_WIN_FLAG_BLEND_COVERAGE	(1 << 2)
+
+#define TEGRA_WIN_BLEND_FLAGS_MASK \
+	(TEGRA_WIN_FLAG_BLEND_PREMULT | TEGRA_WIN_FLAG_BLEND_COVERAGE)
 
 /* Note: These are the actual values written to the DC_WIN_COLOR_DEPTH register
  * and may change in new tegra architectures.
@@ -164,9 +160,6 @@ void tegra_dc_disable(struct tegra_dc *dc);
 int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n);
 int tegra_dc_sync_windows(struct tegra_dc_win *windows[], int n);
 
-/* will probably be replaced with an interface describing the window order */
-void tegra_dc_set_blending(struct tegra_dc *dc, struct tegra_dc_blend *blend);
-
 int tegra_dc_set_mode(struct tegra_dc *dc, const struct tegra_dc_mode *mode);
 
 #endif
diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c
index 666e14cc9c22..0d0608d014b8 100644
--- a/drivers/video/tegra/dc/dc.c
+++ b/drivers/video/tegra/dc/dc.c
@@ -39,25 +39,6 @@
 #include "dc_reg.h"
 #include "dc_priv.h"
 
-struct tegra_dc_blend tegra_dc_blend_modes[][DC_N_WINDOWS] = {
-	{{.nokey = BLEND(NOKEY, FIX, 0xff, 0xff),
-	  .one_win = BLEND(NOKEY, FIX, 0xff, 0xff),
-	  .two_win_x = BLEND(NOKEY, FIX, 0x00, 0x00),
-	  .two_win_y = BLEND(NOKEY, DEPENDANT, 0x00, 0x00),
-	  .three_win_xy = BLEND(NOKEY, FIX, 0x00, 0x00)},
-	 {.nokey = BLEND(NOKEY, FIX, 0xff, 0xff),
-	  .one_win = BLEND(NOKEY, FIX, 0xff, 0xff),
-	  .two_win_x = BLEND(NOKEY, FIX, 0xff, 0xff),
-	  .two_win_y = BLEND(NOKEY, DEPENDANT, 0x00, 0x00),
-	  .three_win_xy = BLEND(NOKEY, DEPENDANT, 0x00, 0x00)},
-	 {.nokey = BLEND(NOKEY, FIX, 0xff, 0xff),
-	  .one_win = BLEND(NOKEY, FIX, 0xff, 0xff),
-	  .two_win_x = BLEND(NOKEY, ALPHA, 0xff, 0xff),
-	  .two_win_y = BLEND(NOKEY, ALPHA, 0xff, 0xff),
-	  .three_win_xy = BLEND(NOKEY, ALPHA, 0xff, 0xff)}
-	}
-};
-
 struct tegra_dc *tegra_dcs[TEGRA_MAX_DC];
 
 DEFINE_MUTEX(tegra_dc_lock);
@@ -349,12 +330,88 @@ struct tegra_dc_win *tegra_dc_get_window(struct tegra_dc *dc, unsigned win)
 }
 EXPORT_SYMBOL(tegra_dc_get_window);
 
+static int get_topmost_window(u32 *depths, unsigned long *wins)
+{
+	int idx, best = -1;
+
+	for_each_set_bit(idx, wins, sizeof(*wins)) {
+		if (best == -1 || depths[idx] < depths[best])
+			best = idx;
+	}
+	clear_bit(best, wins);
+	return best;
+}
+
+static u32 blend_topwin(u32 flags)
+{
+	if (flags & TEGRA_WIN_FLAG_BLEND_COVERAGE)
+		return BLEND(NOKEY, ALPHA, 0xff, 0xff);
+	else if (flags & TEGRA_WIN_FLAG_BLEND_PREMULT)
+		return BLEND(NOKEY, PREMULT, 0xff, 0xff);
+	else
+		return BLEND(NOKEY, FIX, 0xff, 0xff);
+}
+
+static u32 blend_2win(int idx, unsigned long behind_mask, u32* flags, int xy)
+{
+	int other;
+
+	for (other = 0; other < DC_N_WINDOWS; other++) {
+		if (other != idx && (xy-- == 0))
+			break;
+	}
+	if (BIT(other) & behind_mask)
+		return blend_topwin(flags[idx]);
+	else if (flags[other])
+		return BLEND(NOKEY, DEPENDANT, 0x00, 0x00);
+	else
+		return BLEND(NOKEY, FIX, 0x00, 0x00);
+}
+
+static u32 blend_3win(int idx, unsigned long behind_mask, u32* flags)
+{
+	unsigned long infront_mask;
+
+	infront_mask = ~(behind_mask | BIT(idx));
+	infront_mask &= (BIT(DC_N_WINDOWS) - 1);
+
+	if (!infront_mask)
+		return blend_topwin(flags[idx]);
+	else if (behind_mask && flags[ffs(infront_mask)])
+		return BLEND(NOKEY, DEPENDANT, 0x00, 0x00);
+	else
+		return BLEND(NOKEY, FIX, 0x0, 0x0);
+}
+
+static void tegra_dc_set_blending(struct tegra_dc *dc, struct tegra_dc_blend *blend)
+{
+	unsigned long mask = BIT(DC_N_WINDOWS) - 1;
+
+	while (mask) {
+		int idx = get_topmost_window(blend->z, &mask);
+
+		tegra_dc_writel(dc, WINDOW_A_SELECT << idx,
+				DC_CMD_DISPLAY_WINDOW_HEADER);
+		tegra_dc_writel(dc, BLEND(NOKEY, FIX, 0xff, 0xff),
+				DC_WIN_BLEND_NOKEY);
+		tegra_dc_writel(dc, BLEND(NOKEY, FIX, 0xff, 0xff),
+				DC_WIN_BLEND_1WIN);
+		tegra_dc_writel(dc, blend_2win(idx, mask, blend->flags, 0),
+				DC_WIN_BLEND_2WIN_X);
+		tegra_dc_writel(dc, blend_2win(idx, mask, blend->flags, 1),
+				DC_WIN_BLEND_2WIN_Y);
+		tegra_dc_writel(dc, blend_3win(idx, mask, blend->flags),
+				DC_WIN_BLEND_3WIN_XY);
+	}
+}
+
 /* does not support updating windows on multiple dcs in one call */
 int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
 {
 	struct tegra_dc *dc;
 	unsigned long update_mask = GENERAL_ACT_REQ;
 	unsigned long val;
+	bool update_blend = false;
 	int i;
 
 	dc = windows[0]->dc;
@@ -370,7 +427,17 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
 		struct tegra_dc_win *win = windows[i];
 		unsigned h_dda;
 		unsigned v_dda;
-		unsigned stride;
+
+		if (win->z != dc->blend.z[win->idx]) {
+			dc->blend.z[win->idx] = win->z;
+			update_blend = true;
+		}
+		if ((win->flags & TEGRA_WIN_BLEND_FLAGS_MASK) !=
+			dc->blend.flags[win->idx]) {
+			dc->blend.flags[win->idx] =
+				win->flags & TEGRA_WIN_BLEND_FLAGS_MASK;
+			update_blend = true;
+		}
 
 		tegra_dc_writel(dc, WINDOW_A_SELECT << win->idx,
 				DC_CMD_DISPLAY_WINDOW_HEADER);
@@ -385,41 +452,46 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
 		tegra_dc_writel(dc, win->fmt, DC_WIN_COLOR_DEPTH);
 		tegra_dc_writel(dc, 0, DC_WIN_BYTE_SWAP);
 
-		stride = win->w * tegra_dc_fmt_bpp(win->fmt) / 8;
-
 		/* TODO: implement filter on settings */
-		h_dda = (win->w * 0x1000) / (win->out_w - 1);
-		v_dda = (win->h * 0x1000) / (win->out_h - 1);
+		h_dda = (win->w * 0x1000) / max_t(int, win->out_w - 1, 1);
+		v_dda = (win->h * 0x1000) / max_t(int, win->out_h - 1, 1);
 
 		tegra_dc_writel(dc,
-				V_POSITION(win->y) | H_POSITION(win->x),
+				V_POSITION(win->out_y) | H_POSITION(win->out_x),
 				DC_WIN_POSITION);
 		tegra_dc_writel(dc,
 				V_SIZE(win->out_h) | H_SIZE(win->out_w),
 				DC_WIN_SIZE);
 		tegra_dc_writel(dc,
-				V_PRESCALED_SIZE(win->out_h) |
-				H_PRESCALED_SIZE(stride),
+				V_PRESCALED_SIZE(win->h) |
+				H_PRESCALED_SIZE(win->w*tegra_dc_fmt_bpp(win->fmt)/8),
 				DC_WIN_PRESCALED_SIZE);
 		tegra_dc_writel(dc, 0, DC_WIN_H_INITIAL_DDA);
 		tegra_dc_writel(dc, 0, DC_WIN_V_INITIAL_DDA);
 		tegra_dc_writel(dc, V_DDA_INC(v_dda) | H_DDA_INC(h_dda),
 				DC_WIN_DDA_INCREMENT);
-		tegra_dc_writel(dc, stride, DC_WIN_LINE_STRIDE);
+		tegra_dc_writel(dc, win->stride, DC_WIN_LINE_STRIDE);
 		tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE);
 
 		val = WIN_ENABLE;
-		if (win->flags & TEGRA_WIN_FLAG_COLOR_EXPAND)
+		if (tegra_dc_fmt_bpp(win->fmt) < 24)
 			val |= COLOR_EXPAND;
 		tegra_dc_writel(dc, val, DC_WIN_WIN_OPTIONS);
 
 		tegra_dc_writel(dc, (unsigned long)win->phys_addr,
 				DC_WINBUF_START_ADDR);
-		tegra_dc_writel(dc, 0, DC_WINBUF_ADDR_H_OFFSET);
-		tegra_dc_writel(dc, 0, DC_WINBUF_ADDR_V_OFFSET);
+		tegra_dc_writel(dc, win->x, DC_WINBUF_ADDR_H_OFFSET);
+		tegra_dc_writel(dc, win->y, DC_WINBUF_ADDR_V_OFFSET);
 
 		win->dirty = 1;
+	}
 
+	if (update_blend) {
+		tegra_dc_set_blending(dc, &dc->blend);
+		for (i = 0; i < DC_N_WINDOWS; i++) {
+			dc->windows[i].dirty = 1;
+			update_mask |= WIN_A_ACT_REQ << i;
+		}
 	}
 
 	tegra_dc_writel(dc, update_mask << 8, DC_CMD_STATE_CONTROL);
@@ -468,23 +540,6 @@ int tegra_dc_sync_windows(struct tegra_dc_win *windows[], int n)
 }
 EXPORT_SYMBOL(tegra_dc_sync_windows);
 
-void tegra_dc_set_blending(struct tegra_dc *dc, struct tegra_dc_blend *blend)
-{
-	int i;
-
-	for (i = 0; i < DC_N_WINDOWS; i++) {
-		tegra_dc_writel(dc, WINDOW_A_SELECT << i,
-				DC_CMD_DISPLAY_WINDOW_HEADER);
-		tegra_dc_writel(dc, blend[i].nokey, DC_WIN_BLEND_NOKEY);
-		tegra_dc_writel(dc, blend[i].one_win, DC_WIN_BLEND_1WIN);
-		tegra_dc_writel(dc, blend[i].two_win_x, DC_WIN_BLEND_2WIN_X);
-		tegra_dc_writel(dc, blend[i].two_win_y, DC_WIN_BLEND_2WIN_Y);
-		tegra_dc_writel(dc, blend[i].three_win_xy,
-				DC_WIN_BLEND_3WIN_XY);
-	}
-}
-EXPORT_SYMBOL(tegra_dc_set_blending);
-
 void tegra_dc_setup_clk(struct tegra_dc *dc, struct clk *clk)
 {
 	if (dc->out->type == TEGRA_DC_OUT_HDMI) {
@@ -674,8 +729,6 @@ static void _tegra_dc_enable(struct tegra_dc *dc)
 
 	if (dc->out_ops && dc->out_ops->enable)
 		dc->out_ops->enable(dc);
-
-	tegra_dc_set_blending(dc, tegra_dc_blend_modes[0]);
 }
 
 void tegra_dc_enable(struct tegra_dc *dc)
@@ -915,7 +968,8 @@ static int tegra_dc_resume(struct nvhost_device *ndev)
 			wins[i] = &dc->windows[i];
 
 		_tegra_dc_enable(dc);
-
+		/* force a full blending update */
+		dc->blend.z[0] = -1;
 		tegra_dc_update_windows(wins, dc->n_windows);
 	}
 
diff --git a/drivers/video/tegra/dc/dc_priv.h b/drivers/video/tegra/dc/dc_priv.h
index 1bc1c770ec1f..2297be40f030 100644
--- a/drivers/video/tegra/dc/dc_priv.h
+++ b/drivers/video/tegra/dc/dc_priv.h
@@ -25,6 +25,11 @@
 
 struct tegra_dc;
 
+struct tegra_dc_blend {
+	unsigned z[DC_N_WINDOWS];
+	unsigned flags[DC_N_WINDOWS];
+};
+
 struct tegra_dc_out_ops {
 	/* initialize output.  dc clocks are not on at this point */
 	int (*init)(struct tegra_dc *dc);
@@ -60,6 +65,7 @@ struct tegra_dc {
 	struct tegra_dc_mode		mode;
 
 	struct tegra_dc_win		windows[DC_N_WINDOWS];
+	struct tegra_dc_blend		blend;
 	int				n_windows;
 
 	wait_queue_head_t		wq;
diff --git a/drivers/video/tegra/dc/dc_reg.h b/drivers/video/tegra/dc/dc_reg.h
index f7342cc6eb0a..5dd50d21128c 100644
--- a/drivers/video/tegra/dc/dc_reg.h
+++ b/drivers/video/tegra/dc/dc_reg.h
@@ -377,8 +377,13 @@
 #define  BLEND_CONTROL_FIX		(0 << 2)
 #define  BLEND_CONTROL_ALPHA		(1 << 2)
 #define  BLEND_CONTROL_DEPENDANT	(2 << 2)
+#define  BLEND_CONTROL_PREMULT		(3 << 2)
 #define  BLEND_WEIGHT0(x)		(((x) & 0xff) << 8)
 #define  BLEND_WEIGHT1(x)		(((x) & 0xff) << 16)
+#define  BLEND(key, control, weight0, weight1)			\
+	  (CKEY_ ## key | BLEND_CONTROL_ ## control |		\
+	   BLEND_WEIGHT0(weight0) | BLEND_WEIGHT0(weight1))
+
 
 #define DC_WIN_HP_FETCH_CONTROL			0x714
 #define DC_WINBUF_START_ADDR			0x800
diff --git a/drivers/video/tegra/fb.c b/drivers/video/tegra/fb.c
index 46feef8759fe..45d47e6c991a 100644
--- a/drivers/video/tegra/fb.c
+++ b/drivers/video/tegra/fb.c
@@ -290,7 +290,8 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
 	struct fb_info *info;
 	struct tegra_fb_info *tegra_fb;
 	void __iomem *fb_base = NULL;
-	unsigned long fb_size = 0;	unsigned long fb_phys = 0;
+	unsigned long fb_size = 0;
+	unsigned long fb_phys = 0;
 	int ret = 0;
 
 	win = tegra_dc_get_window(dc, fb_data->win);
@@ -363,11 +364,15 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
 	win->w = fb_data->xres;
 	win->h = fb_data->yres;
 	/* TODO: set to output res dc */
+	win->out_x = 0;
+	win->out_y = 0;
 	win->out_w = fb_data->xres;
 	win->out_h = fb_data->yres;
+	win->z = 0;
 	win->phys_addr = fb_phys;
 	win->virt_addr = fb_base;
-	win->flags = TEGRA_WIN_FLAG_ENABLED | TEGRA_WIN_FLAG_COLOR_EXPAND;
+	win->stride = fb_data->xres * fb_data->bits_per_pixel / 8;
+	win->flags = TEGRA_WIN_FLAG_ENABLED;
 
 	if (fb_mem)
 		tegra_fb_set_par(info);