OMAP: DSS2: change manual update scaling setup

Currently the update area on manual update displays is automatically
enlargened to fully cover scaled overlays. This patch makes that
optional, allowing the panel driver to choose if it's used or not.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@nokia.com>
diff --git a/arch/arm/plat-omap/include/plat/display.h b/arch/arm/plat-omap/include/plat/display.h
index b8de704..8bd15bd 100644
--- a/arch/arm/plat-omap/include/plat/display.h
+++ b/arch/arm/plat-omap/include/plat/display.h
@@ -560,7 +560,8 @@
 int omapdss_dsi_enable_te(struct omap_dss_device *dssdev, bool enable);
 
 int omap_dsi_prepare_update(struct omap_dss_device *dssdev,
-				    u16 *x, u16 *y, u16 *w, u16 *h);
+				    u16 *x, u16 *y, u16 *w, u16 *h,
+				    bool enlarge_update_area);
 int omap_dsi_update(struct omap_dss_device *dssdev,
 		int channel,
 		u16 x, u16 y, u16 w, u16 h,
diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c
index 2a91d56..6848e36 100644
--- a/drivers/video/omap2/displays/panel-taal.c
+++ b/drivers/video/omap2/displays/panel-taal.c
@@ -1135,7 +1135,7 @@
 		goto err;
 	}
 
-	r = omap_dsi_prepare_update(dssdev, &x, &y, &w, &h);
+	r = omap_dsi_prepare_update(dssdev, &x, &y, &w, &h, true);
 	if (r)
 		goto err;
 
diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c
index 32297b4..d0881e9 100644
--- a/drivers/video/omap2/dss/dsi.c
+++ b/drivers/video/omap2/dss/dsi.c
@@ -2860,7 +2860,8 @@
 }
 
 int omap_dsi_prepare_update(struct omap_dss_device *dssdev,
-				    u16 *x, u16 *y, u16 *w, u16 *h)
+				    u16 *x, u16 *y, u16 *w, u16 *h,
+				    bool enlarge_update_area)
 {
 	u16 dw, dh;
 
@@ -2884,7 +2885,8 @@
 	dsi_perf_mark_setup();
 
 	if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
-		dss_setup_partial_planes(dssdev, x, y, w, h);
+		dss_setup_partial_planes(dssdev, x, y, w, h,
+				enlarge_update_area);
 		dispc_set_lcd_size(*w, *h);
 	}
 
diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h
index 66e8e97..5c7940d 100644
--- a/drivers/video/omap2/dss/dss.h
+++ b/drivers/video/omap2/dss/dss.h
@@ -199,7 +199,8 @@
 void dss_uninit_overlay_managers(struct platform_device *pdev);
 int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl);
 void dss_setup_partial_planes(struct omap_dss_device *dssdev,
-				u16 *x, u16 *y, u16 *w, u16 *h);
+				u16 *x, u16 *y, u16 *w, u16 *h,
+				bool enlarge_update_area);
 void dss_start_update(struct omap_dss_device *dssdev);
 
 /* overlay */
diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c
index a1d84ef..4b09fcb 100644
--- a/drivers/video/omap2/dss/manager.c
+++ b/drivers/video/omap2/dss/manager.c
@@ -440,6 +440,10 @@
 
 	/* manual update region */
 	u16 x, y, w, h;
+
+	/* enlarge the update area if the update area contains scaled
+	 * overlays */
+	bool enlarge_update_area;
 };
 
 static struct {
@@ -721,6 +725,7 @@
 	u16 x, y, w, h;
 	u32 paddr;
 	int r;
+	u16 orig_w, orig_h, orig_outw, orig_outh;
 
 	DSSDBGF("%d", plane);
 
@@ -741,8 +746,16 @@
 	outh = c->out_height == 0 ? c->height : c->out_height;
 	paddr = c->paddr;
 
+	orig_w = w;
+	orig_h = h;
+	orig_outw = outw;
+	orig_outh = outh;
+
 	if (c->manual_update && mc->do_manual_update) {
 		unsigned bpp;
+		unsigned scale_x_m = w, scale_x_d = outw;
+		unsigned scale_y_m = h, scale_y_d = outh;
+
 		/* If the overlay is outside the update region, disable it */
 		if (!rectangle_intersects(mc->x, mc->y, mc->w, mc->h,
 					x, y, outw, outh)) {
@@ -773,39 +786,33 @@
 			BUG();
 		}
 
-		if (dispc_is_overlay_scaled(c)) {
-			/* If the overlay is scaled, the update area has
-			 * already been enlarged to cover the whole overlay. We
-			 * only need to adjust x/y here */
-			x = c->pos_x - mc->x;
-			y = c->pos_y - mc->y;
+		if (mc->x > c->pos_x) {
+			x = 0;
+			outw -= (mc->x - c->pos_x);
+			paddr += (mc->x - c->pos_x) *
+				scale_x_m / scale_x_d * bpp / 8;
 		} else {
-			if (mc->x > c->pos_x) {
-				x = 0;
-				w -= (mc->x - c->pos_x);
-				paddr += (mc->x - c->pos_x) * bpp / 8;
-			} else {
-				x = c->pos_x - mc->x;
-			}
-
-			if (mc->y > c->pos_y) {
-				y = 0;
-				h -= (mc->y - c->pos_y);
-				paddr += (mc->y - c->pos_y) * c->screen_width *
-					bpp / 8;
-			} else {
-				y = c->pos_y - mc->y;
-			}
-
-			if (mc->w < (x+w))
-				w -= (x+w) - (mc->w);
-
-			if (mc->h < (y+h))
-				h -= (y+h) - (mc->h);
-
-			outw = w;
-			outh = h;
+			x = c->pos_x - mc->x;
 		}
+
+		if (mc->y > c->pos_y) {
+			y = 0;
+			outh -= (mc->y - c->pos_y);
+			paddr += (mc->y - c->pos_y) *
+				scale_y_m / scale_y_d *
+				c->screen_width * bpp / 8;
+		} else {
+			y = c->pos_y - mc->y;
+		}
+
+		if (mc->w < (x + outw))
+			outw -= (x + outw) - (mc->w);
+
+		if (mc->h < (y + outh))
+			outh -= (y + outh) - (mc->h);
+
+		w = w * outw / orig_outw;
+		h = h * outh / orig_outh;
 	}
 
 	r = dispc_setup_plane(plane,
@@ -963,7 +970,7 @@
 /* Configure dispc for partial update. Return possibly modified update
  * area */
 void dss_setup_partial_planes(struct omap_dss_device *dssdev,
-		u16 *xi, u16 *yi, u16 *wi, u16 *hi)
+		u16 *xi, u16 *yi, u16 *wi, u16 *hi, bool enlarge_update_area)
 {
 	struct overlay_cache_data *oc;
 	struct manager_cache_data *mc;
@@ -1015,6 +1022,9 @@
 
 			oc->dirty = true;
 
+			if (!enlarge_update_area)
+				continue;
+
 			if (!oc->enabled)
 				continue;
 
@@ -1074,6 +1084,7 @@
 
 	mc = &dss_cache.manager_cache[mgr->id];
 	mc->do_manual_update = true;
+	mc->enlarge_update_area = enlarge_update_area;
 	mc->x = x;
 	mc->y = y;
 	mc->w = w;
diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c
index cc23f53..bbe6246 100644
--- a/drivers/video/omap2/dss/rfbi.c
+++ b/drivers/video/omap2/dss/rfbi.c
@@ -886,7 +886,7 @@
 		return -EINVAL;
 
 	if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
-		dss_setup_partial_planes(dssdev, x, y, w, h);
+		dss_setup_partial_planes(dssdev, x, y, w, h, true);
 		dispc_set_lcd_size(*w, *h);
 	}