summaryrefslogtreecommitdiff
path: root/recipes/linux/linux-omap-pm/dss2/0120-DSS2-DISPC-configuration-management.patch
diff options
context:
space:
mode:
Diffstat (limited to 'recipes/linux/linux-omap-pm/dss2/0120-DSS2-DISPC-configuration-management.patch')
-rw-r--r--recipes/linux/linux-omap-pm/dss2/0120-DSS2-DISPC-configuration-management.patch2006
1 files changed, 2006 insertions, 0 deletions
diff --git a/recipes/linux/linux-omap-pm/dss2/0120-DSS2-DISPC-configuration-management.patch b/recipes/linux/linux-omap-pm/dss2/0120-DSS2-DISPC-configuration-management.patch
new file mode 100644
index 0000000000..ef94abfd0c
--- /dev/null
+++ b/recipes/linux/linux-omap-pm/dss2/0120-DSS2-DISPC-configuration-management.patch
@@ -0,0 +1,2006 @@
+From 43e7fcaa40dd3726bda3317b8419d4be4e73dbfb Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@nokia.com>
+Date: Fri, 12 Jun 2009 10:55:49 +0300
+Subject: [PATCH 120/146] DSS2: DISPC configuration management
+
+This commit implements caching and synchronization of DISPC shadow
+registers.
+
+I had to disable fifomerging, enabling it needs more thought and
+work.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@nokia.com>
+---
+ arch/arm/plat-omap/include/mach/display.h | 30 +-
+ drivers/video/omap2/dss/dispc.c | 307 ++--------
+ drivers/video/omap2/dss/display.c | 21 +-
+ drivers/video/omap2/dss/dsi.c | 41 +-
+ drivers/video/omap2/dss/dss.h | 19 +-
+ drivers/video/omap2/dss/manager.c | 933 ++++++++++++++++++++++++++---
+ drivers/video/omap2/dss/overlay.c | 64 ++-
+ drivers/video/omap2/dss/rfbi.c | 2 +-
+ drivers/video/omap2/dss/venc.c | 10 +
+ drivers/video/omap2/omapfb/omapfb-ioctl.c | 26 +
+ include/linux/omapfb.h | 1 +
+ 11 files changed, 1047 insertions(+), 407 deletions(-)
+
+diff --git a/arch/arm/plat-omap/include/mach/display.h b/arch/arm/plat-omap/include/mach/display.h
+index 0695497..be07c35 100644
+--- a/arch/arm/plat-omap/include/mach/display.h
++++ b/arch/arm/plat-omap/include/mach/display.h
+@@ -275,13 +275,19 @@ struct omap_overlay {
+ struct kobject kobj;
+ struct list_head list;
+
++ /* static fields */
+ const char *name;
+ int id;
+- struct omap_overlay_manager *manager;
+ enum omap_color_mode supported_modes;
+- struct omap_overlay_info info;
+ enum omap_overlay_caps caps;
+
++ /* dynamic fields */
++ struct omap_overlay_manager *manager;
++ struct omap_overlay_info info;
++
++ /* if true, info has been changed, but not applied() yet */
++ bool info_dirty;
++
+ int (*set_manager)(struct omap_overlay *ovl,
+ struct omap_overlay_manager *mgr);
+ int (*unset_manager)(struct omap_overlay *ovl);
+@@ -290,6 +296,8 @@ struct omap_overlay {
+ struct omap_overlay_info *info);
+ void (*get_overlay_info)(struct omap_overlay *ovl,
+ struct omap_overlay_info *info);
++
++ int (*wait_for_go)(struct omap_overlay *ovl);
+ };
+
+ struct omap_overlay_manager_info {
+@@ -306,25 +314,33 @@ struct omap_overlay_manager {
+ struct kobject kobj;
+ struct list_head list;
+
++ /* static fields */
+ const char *name;
+ int id;
+ enum omap_overlay_manager_caps caps;
+- struct omap_overlay_manager_info info;
+- struct omap_dss_device *device;
+ int num_overlays;
+ struct omap_overlay **overlays;
+ enum omap_display_type supported_displays;
+
++ /* dynamic fields */
++ struct omap_dss_device *device;
++ struct omap_overlay_manager_info info;
++
++ bool device_changed;
++ /* if true, info has been changed but not applied() yet */
++ bool info_dirty;
++
+ int (*set_device)(struct omap_overlay_manager *mgr,
+ struct omap_dss_device *dssdev);
+ int (*unset_device)(struct omap_overlay_manager *mgr);
+
+- int (*apply)(struct omap_overlay_manager *mgr);
+-
+ int (*set_manager_info)(struct omap_overlay_manager *mgr,
+ struct omap_overlay_manager_info *info);
+ void (*get_manager_info)(struct omap_overlay_manager *mgr,
+ struct omap_overlay_manager_info *info);
++
++ int (*apply)(struct omap_overlay_manager *mgr);
++ int (*wait_for_go)(struct omap_overlay_manager *mgr);
+ };
+
+ struct omap_dss_device {
+@@ -450,8 +466,6 @@ struct omap_dss_device {
+ void *buf, size_t size,
+ u16 x, u16 y, u16 w, u16 h);
+
+- void (*configure_overlay)(struct omap_overlay *overlay);
+-
+ int (*set_wss)(struct omap_dss_device *dssdev, u32 wss);
+ u32 (*get_wss)(struct omap_dss_device *dssdev);
+
+diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c
+index 4e1da6b..d061d75 100644
+--- a/drivers/video/omap2/dss/dispc.c
++++ b/drivers/video/omap2/dss/dispc.c
+@@ -158,6 +158,8 @@ static struct {
+ unsigned long cache_prate;
+ struct dispc_clock_info cache_cinfo;
+
++ u32 fifo_size[3];
++
+ spinlock_t irq_lock;
+ u32 irq_error_mask;
+ struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS];
+@@ -479,10 +481,21 @@ static inline void enable_clocks(bool enable)
+ dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+ }
+
++bool dispc_go_busy(enum omap_channel channel)
++{
++ int bit;
++
++ if (channel == OMAP_DSS_CHANNEL_LCD)
++ bit = 5; /* GOLCD */
++ else
++ bit = 6; /* GODIGIT */
++
++ return REG_GET(DISPC_CONTROL, bit, bit) == 1;
++}
++
+ void dispc_go(enum omap_channel channel)
+ {
+ int bit;
+- unsigned long tmo;
+
+ enable_clocks(1);
+
+@@ -500,13 +513,9 @@ void dispc_go(enum omap_channel channel)
+ else
+ bit = 6; /* GODIGIT */
+
+- tmo = jiffies + msecs_to_jiffies(200);
+- while (REG_GET(DISPC_CONTROL, bit, bit) == 1) {
+- if (time_after(jiffies, tmo)) {
+- DSSERR("timeout waiting GO flag\n");
+- goto end;
+- }
+- cpu_relax();
++ if (REG_GET(DISPC_CONTROL, bit, bit) == 1) {
++ DSSERR("GO bit not down for channel %d\n", channel);
++ goto end;
+ }
+
+ DSSDBG("GO %s\n", channel == OMAP_DSS_CHANNEL_LCD ? "LCD" : "DIGIT");
+@@ -942,31 +951,33 @@ void dispc_set_digit_size(u16 width, u16 height)
+ enable_clocks(0);
+ }
+
+-u32 dispc_get_plane_fifo_size(enum omap_plane plane)
++static void dispc_read_plane_fifo_sizes(void)
+ {
+ const struct dispc_reg fsz_reg[] = { DISPC_GFX_FIFO_SIZE_STATUS,
+ DISPC_VID_FIFO_SIZE_STATUS(0),
+ DISPC_VID_FIFO_SIZE_STATUS(1) };
+ u32 size;
++ int plane;
+
+ enable_clocks(1);
+
+- if (cpu_is_omap24xx())
+- size = FLD_GET(dispc_read_reg(fsz_reg[plane]), 8, 0);
+- else if (cpu_is_omap34xx())
+- size = FLD_GET(dispc_read_reg(fsz_reg[plane]), 10, 0);
+- else
+- BUG();
++ for (plane = 0; plane < ARRAY_SIZE(dispc.fifo_size); ++plane) {
++ if (cpu_is_omap24xx())
++ size = FLD_GET(dispc_read_reg(fsz_reg[plane]), 8, 0);
++ else if (cpu_is_omap34xx())
++ size = FLD_GET(dispc_read_reg(fsz_reg[plane]), 10, 0);
++ else
++ BUG();
+
+- if (cpu_is_omap34xx()) {
+- /* FIFOMERGE */
+- if (REG_GET(DISPC_CONFIG, 14, 14))
+- size *= 3;
++ dispc.fifo_size[plane] = size;
+ }
+
+ enable_clocks(0);
++}
+
+- return size;
++u32 dispc_get_plane_fifo_size(enum omap_plane plane)
++{
++ return dispc.fifo_size[plane];
+ }
+
+ void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high)
+@@ -974,16 +985,10 @@ void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high)
+ const struct dispc_reg ftrs_reg[] = { DISPC_GFX_FIFO_THRESHOLD,
+ DISPC_VID_FIFO_THRESHOLD(0),
+ DISPC_VID_FIFO_THRESHOLD(1) };
+- u32 size;
+-
+ enable_clocks(1);
+
+- size = dispc_get_plane_fifo_size(plane);
+-
+- BUG_ON(low > size || high > size);
+-
+- DSSDBG("fifo(%d) size %d, low/high old %u/%u, new %u/%u\n",
+- plane, size,
++ DSSDBG("fifo(%d) low/high old %u/%u, new %u/%u\n",
++ plane,
+ REG_GET(ftrs_reg[plane], 11, 0),
+ REG_GET(ftrs_reg[plane], 27, 16),
+ low, high);
+@@ -1480,8 +1485,14 @@ static unsigned long calc_fclk(u16 width, u16 height,
+ return dispc_pclk_rate() * vf * hf;
+ }
+
++void dispc_set_channel_out(enum omap_plane plane, enum omap_channel channel_out)
++{
++ enable_clocks(1);
++ _dispc_set_channel_out(plane, channel_out);
++ enable_clocks(0);
++}
++
+ static int _dispc_setup_plane(enum omap_plane plane,
+- enum omap_channel channel_out,
+ u32 paddr, u16 screen_width,
+ u16 pos_x, u16 pos_y,
+ u16 width, u16 height,
+@@ -1631,7 +1642,6 @@ static int _dispc_setup_plane(enum omap_plane plane,
+ DSSDBG("offset0 %u, offset1 %u, row_inc %d, pix_inc %d\n",
+ offset0, offset1, row_inc, pix_inc);
+
+- _dispc_set_channel_out(plane, channel_out);
+ _dispc_set_color_mode(plane, color_mode);
+
+ _dispc_set_plane_ba0(plane, paddr + offset0);
+@@ -3068,6 +3078,8 @@ static void _omap_dispc_initial_config(void)
+ _dispc_setup_color_conv_coef();
+
+ dispc_set_loadmode(OMAP_DSS_LOAD_FRAME_ONLY);
++
++ dispc_read_plane_fifo_sizes();
+ }
+
+ int dispc_init(void)
+@@ -3127,7 +3139,7 @@ int dispc_enable_plane(enum omap_plane plane, bool enable)
+ return 0;
+ }
+
+-int dispc_setup_plane(enum omap_plane plane, enum omap_channel channel_out,
++int dispc_setup_plane(enum omap_plane plane,
+ u32 paddr, u16 screen_width,
+ u16 pos_x, u16 pos_y,
+ u16 width, u16 height,
+@@ -3139,9 +3151,9 @@ int dispc_setup_plane(enum omap_plane plane, enum omap_channel channel_out,
+ {
+ int r = 0;
+
+- DSSDBG("dispc_setup_plane %d, ch %d, pa %x, sw %d, %d,%d, %dx%d -> "
++ DSSDBG("dispc_setup_plane %d, pa %x, sw %d, %d,%d, %dx%d -> "
+ "%dx%d, ilace %d, cmode %x, rot %d, mir %d\n",
+- plane, channel_out, paddr, screen_width, pos_x, pos_y,
++ plane, paddr, screen_width, pos_x, pos_y,
+ width, height,
+ out_width, out_height,
+ ilace, color_mode,
+@@ -3149,7 +3161,7 @@ int dispc_setup_plane(enum omap_plane plane, enum omap_channel channel_out,
+
+ enable_clocks(1);
+
+- r = _dispc_setup_plane(plane, channel_out,
++ r = _dispc_setup_plane(plane,
+ paddr, screen_width,
+ pos_x, pos_y,
+ width, height,
+@@ -3163,230 +3175,3 @@ int dispc_setup_plane(enum omap_plane plane, enum omap_channel channel_out,
+
+ return r;
+ }
+-
+-static int dispc_is_intersecting(int x1, int y1, int w1, int h1,
+- int x2, int y2, int w2, int h2)
+-{
+- if (x1 >= (x2+w2))
+- return 0;
+-
+- if ((x1+w1) <= x2)
+- return 0;
+-
+- if (y1 >= (y2+h2))
+- return 0;
+-
+- if ((y1+h1) <= y2)
+- return 0;
+-
+- return 1;
+-}
+-
+-static int dispc_is_overlay_scaled(struct omap_overlay_info *pi)
+-{
+- if (pi->width != pi->out_width)
+- return 1;
+-
+- if (pi->height != pi->out_height)
+- return 1;
+-
+- return 0;
+-}
+-
+-/* returns the area that needs updating */
+-void dispc_setup_partial_planes(struct omap_dss_device *dssdev,
+- u16 *xi, u16 *yi, u16 *wi, u16 *hi)
+-{
+- struct omap_overlay_manager *mgr;
+- int i;
+-
+- int x, y, w, h;
+-
+- x = *xi;
+- y = *yi;
+- w = *wi;
+- h = *hi;
+-
+- DSSDBG("dispc_setup_partial_planes %d,%d %dx%d\n",
+- *xi, *yi, *wi, *hi);
+-
+-
+- mgr = dssdev->manager;
+-
+- if (!mgr) {
+- DSSDBG("no manager\n");
+- return;
+- }
+-
+- for (i = 0; i < mgr->num_overlays; i++) {
+- struct omap_overlay *ovl;
+- struct omap_overlay_info *pi;
+- ovl = mgr->overlays[i];
+-
+- if (ovl->manager != mgr)
+- continue;
+-
+- if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0)
+- continue;
+-
+- pi = &ovl->info;
+-
+- if (!pi->enabled)
+- continue;
+- /*
+- * If the plane is intersecting and scaled, we
+- * enlarge the update region to accomodate the
+- * whole area
+- */
+-
+- if (dispc_is_intersecting(x, y, w, h,
+- pi->pos_x, pi->pos_y,
+- pi->out_width, pi->out_height)) {
+- if (dispc_is_overlay_scaled(pi)) {
+-
+- int x1, y1, x2, y2;
+-
+- if (x > pi->pos_x)
+- x1 = pi->pos_x;
+- else
+- x1 = x;
+-
+- if (y > pi->pos_y)
+- y1 = pi->pos_y;
+- else
+- y1 = y;
+-
+- if ((x + w) < (pi->pos_x + pi->out_width))
+- x2 = pi->pos_x + pi->out_width;
+- else
+- x2 = x + w;
+-
+- if ((y + h) < (pi->pos_y + pi->out_height))
+- y2 = pi->pos_y + pi->out_height;
+- else
+- y2 = y + h;
+-
+- x = x1;
+- y = y1;
+- w = x2 - x1;
+- h = y2 - y1;
+-
+- DSSDBG("Update area after enlarge due to "
+- "scaling %d, %d %dx%d\n",
+- x, y, w, h);
+- }
+- }
+- }
+-
+- for (i = 0; i < mgr->num_overlays; i++) {
+- struct omap_overlay *ovl = mgr->overlays[i];
+- struct omap_overlay_info *pi = &ovl->info;
+-
+- int px = pi->pos_x;
+- int py = pi->pos_y;
+- int pw = pi->width;
+- int ph = pi->height;
+- int pow = pi->out_width;
+- int poh = pi->out_height;
+- u32 pa = pi->paddr;
+- int psw = pi->screen_width;
+- int bpp;
+-
+- if (ovl->manager != mgr)
+- continue;
+-
+- /*
+- * If plane is not enabled or the update region
+- * does not intersect with the plane in question,
+- * we really disable the plane from hardware
+- */
+-
+- if (!pi->enabled ||
+- !dispc_is_intersecting(x, y, w, h,
+- px, py, pow, poh)) {
+- dispc_enable_plane(ovl->id, 0);
+- continue;
+- }
+-
+- switch (pi->color_mode) {
+- case OMAP_DSS_COLOR_RGB16:
+- case OMAP_DSS_COLOR_ARGB16:
+- case OMAP_DSS_COLOR_YUV2:
+- case OMAP_DSS_COLOR_UYVY:
+- bpp = 16;
+- break;
+-
+- case OMAP_DSS_COLOR_RGB24P:
+- bpp = 24;
+- break;
+-
+- case OMAP_DSS_COLOR_RGB24U:
+- case OMAP_DSS_COLOR_ARGB32:
+- case OMAP_DSS_COLOR_RGBA32:
+- case OMAP_DSS_COLOR_RGBX32:
+- bpp = 32;
+- break;
+-
+- default:
+- BUG();
+- return;
+- }
+-
+- if (x > pi->pos_x) {
+- px = 0;
+- pw -= (x - pi->pos_x);
+- pa += (x - pi->pos_x) * bpp / 8;
+- } else {
+- px = pi->pos_x - x;
+- }
+-
+- if (y > pi->pos_y) {
+- py = 0;
+- ph -= (y - pi->pos_y);
+- pa += (y - pi->pos_y) * psw * bpp / 8;
+- } else {
+- py = pi->pos_y - y;
+- }
+-
+- if (w < (px+pw))
+- pw -= (px+pw) - (w);
+-
+- if (h < (py+ph))
+- ph -= (py+ph) - (h);
+-
+- /* Can't scale the GFX plane */
+- if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0 ||
+- dispc_is_overlay_scaled(pi) == 0) {
+- pow = pw;
+- poh = ph;
+- }
+-
+- DSSDBG("calc plane %d, %x, sw %d, %d,%d, %dx%d -> %dx%d\n",
+- ovl->id, pa, psw, px, py, pw, ph, pow, poh);
+-
+- dispc_setup_plane(ovl->id, mgr->id,
+- pa, psw,
+- px, py,
+- pw, ph,
+- pow, poh,
+- pi->color_mode, 0,
+- pi->rotation_type,
+- pi->rotation,
+- pi->mirror,
+- pi->global_alpha);
+-
+- if (dss_use_replication(dssdev, ovl->info.color_mode))
+- dispc_enable_replication(ovl->id, true);
+- else
+- dispc_enable_replication(ovl->id, false);
+-
+- dispc_enable_plane(ovl->id, 1);
+- }
+-
+- *xi = x;
+- *yi = y;
+- *wi = w;
+- *hi = h;
+-
+-}
+-
+diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c
+index 1242c39..6b5d0cf 100644
+--- a/drivers/video/omap2/dss/display.c
++++ b/drivers/video/omap2/dss/display.c
+@@ -310,21 +310,17 @@ static void default_get_resolution(struct omap_dss_device *dssdev,
+ *yres = dssdev->panel.timings.y_res;
+ }
+
+-static void default_configure_overlay(struct omap_overlay *ovl)
++void default_get_overlay_fifo_thresholds(enum omap_plane plane,
++ u32 fifo_size, enum omap_burst_size *burst_size,
++ u32 *fifo_low, u32 *fifo_high)
+ {
+- unsigned low, high, size;
+- enum omap_burst_size burst;
+- enum omap_plane plane = ovl->id;
++ unsigned burst_size_bytes;
+
+- burst = OMAP_DSS_BURST_16x32;
+- size = 16 * 32 / 8;
++ *burst_size = OMAP_DSS_BURST_16x32;
++ burst_size_bytes = 16 * 32 / 8;
+
+- dispc_set_burst_size(plane, burst);
+-
+- high = dispc_get_plane_fifo_size(plane) - 1;
+- low = dispc_get_plane_fifo_size(plane) - size;
+-
+- dispc_setup_plane_fifo(plane, low, high);
++ *fifo_high = fifo_size - 1;
++ *fifo_low = fifo_size - burst_size_bytes;
+ }
+
+ static int default_wait_vsync(struct omap_dss_device *dssdev)
+@@ -431,7 +427,6 @@ void dss_init_device(struct platform_device *pdev,
+
+ dssdev->get_resolution = default_get_resolution;
+ dssdev->get_recommended_bpp = default_get_recommended_bpp;
+- dssdev->configure_overlay = default_configure_overlay;
+ dssdev->wait_vsync = default_wait_vsync;
+
+ switch (dssdev->type) {
+diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c
+index ffac77b..875be0c 100644
+--- a/drivers/video/omap2/dss/dsi.c
++++ b/drivers/video/omap2/dss/dsi.c
+@@ -2566,7 +2566,7 @@ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev,
+
+ dispc_disable_sidle();
+
+- dispc_enable_lcd_out(1);
++ dss_start_update(dssdev);
+
+ if (use_te_trigger)
+ dsi_vc_send_bta(1);
+@@ -2606,12 +2606,19 @@ static void dsi_set_update_region(struct omap_dss_device *dssdev,
+ static void dsi_start_auto_update(struct omap_dss_device *dssdev)
+ {
+ u16 w, h;
++ int i;
+
+ DSSDBG("starting auto update\n");
+
+ /* In automatic mode the overlay settings are applied like on DPI/SDI.
+- * The overlay settings may not have been applied, if we were in manual
+- * mode earlier, so do it here */
++ * Mark the overlays dirty, so that we get the overlays configured, as
++ * manual mode has left them in bad shape after config partia planes */
++ for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
++ struct omap_overlay *ovl;
++ ovl = omap_dss_get_overlay(i);
++ if (ovl->manager == dssdev->manager)
++ ovl->info_dirty = true;
++ }
+ dssdev->manager->apply(dssdev->manager);
+
+ dssdev->get_resolution(dssdev, &w, &h);
+@@ -2732,7 +2739,7 @@ static int dsi_update_thread(void *data)
+ if (device->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
+
+ if (dsi.update_mode == OMAP_DSS_UPDATE_MANUAL) {
+- dispc_setup_partial_planes(device,
++ dss_setup_partial_planes(device,
+ &x, &y, &w, &h);
+ #if 1
+ /* XXX there seems to be a bug in this driver
+@@ -2741,12 +2748,13 @@ static int dsi_update_thread(void *data)
+ * are always odd, so "fix" it here for now */
+ if (w & 1) {
+ u16 dw, dh;
+- device->get_resolution(device, &dw, &dh);
++ device->get_resolution(device,
++ &dw, &dh);
+ if (x + w == dw)
+ x &= ~1;
+ ++w;
+
+- dispc_setup_partial_planes(device,
++ dss_setup_partial_planes(device,
+ &x, &y, &w, &h);
+ }
+ #endif
+@@ -3361,20 +3369,17 @@ static int dsi_display_memory_read(struct omap_dss_device *dssdev,
+ return r;
+ }
+
+-static void dsi_configure_overlay(struct omap_overlay *ovl)
++void dsi_get_overlay_fifo_thresholds(enum omap_plane plane,
++ u32 fifo_size, enum omap_burst_size *burst_size,
++ u32 *fifo_low, u32 *fifo_high)
+ {
+- unsigned low, high, size;
+- enum omap_burst_size burst;
+- enum omap_plane plane = ovl->id;
++ unsigned burst_size_bytes;
+
+- burst = OMAP_DSS_BURST_16x32;
+- size = 16 * 32 / 8;
++ *burst_size = OMAP_DSS_BURST_16x32;
++ burst_size_bytes = 16 * 32 / 8;
+
+- dispc_set_burst_size(plane, burst);
+-
+- high = dispc_get_plane_fifo_size(plane) - size;
+- low = 0;
+- dispc_setup_plane_fifo(plane, low, high);
++ *fifo_high = fifo_size - burst_size_bytes;
++ *fifo_low = 0;
+ }
+
+ int dsi_init_display(struct omap_dss_device *dssdev)
+@@ -3401,8 +3406,6 @@ int dsi_init_display(struct omap_dss_device *dssdev)
+ dssdev->run_test = dsi_display_run_test;
+ dssdev->memory_read = dsi_display_memory_read;
+
+- dssdev->configure_overlay = dsi_configure_overlay;
+-
+ dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
+
+ dsi.vc[0].dssdev = dssdev;
+diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h
+index 53f0f3d..50f7f5f 100644
+--- a/drivers/video/omap2/dss/dss.h
++++ b/drivers/video/omap2/dss/dss.h
+@@ -175,10 +175,17 @@ void dss_uninit_device(struct platform_device *pdev,
+ struct omap_dss_device *dssdev);
+ bool dss_use_replication(struct omap_dss_device *dssdev,
+ enum omap_color_mode mode);
++void default_get_overlay_fifo_thresholds(enum omap_plane plane,
++ u32 fifo_size, enum omap_burst_size *burst_size,
++ u32 *fifo_low, u32 *fifo_high);
+
+ /* manager */
+ int dss_init_overlay_managers(struct platform_device *pdev);
+ 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);
++void dss_start_update(struct omap_dss_device *dssdev);
+
+ /* overlay */
+ void dss_init_overlays(struct platform_device *pdev);
+@@ -233,6 +240,9 @@ int dsi_pll_calc_pck(bool is_tft, unsigned long req_pck,
+ int dsi_pll_program(struct dsi_clock_info *cinfo);
+ int dsi_pll_init(bool enable_hsclk, bool enable_hsdiv);
+ void dsi_pll_uninit(void);
++void dsi_get_overlay_fifo_thresholds(enum omap_plane plane,
++ u32 fifo_size, enum omap_burst_size *burst_size,
++ u32 *fifo_low, u32 *fifo_high);
+
+ /* DPI */
+ int dpi_init(void);
+@@ -270,8 +280,10 @@ void dispc_set_plane_ba0(enum omap_plane plane, u32 paddr);
+ void dispc_set_plane_ba1(enum omap_plane plane, u32 paddr);
+ void dispc_set_plane_pos(enum omap_plane plane, u16 x, u16 y);
+ void dispc_set_plane_size(enum omap_plane plane, u16 width, u16 height);
++void dispc_set_channel_out(enum omap_plane plane,
++ enum omap_channel channel_out);
+
+-int dispc_setup_plane(enum omap_plane plane, enum omap_channel channel_out,
++int dispc_setup_plane(enum omap_plane plane,
+ u32 paddr, u16 screen_width,
+ u16 pos_x, u16 pos_y,
+ u16 width, u16 height,
+@@ -282,6 +294,7 @@ int dispc_setup_plane(enum omap_plane plane, enum omap_channel channel_out,
+ u8 rotation, bool mirror,
+ u8 global_alpha);
+
++bool dispc_go_busy(enum omap_channel channel);
+ void dispc_go(enum omap_channel channel);
+ void dispc_enable_lcd_out(bool enable);
+ void dispc_enable_digit_out(bool enable);
+@@ -320,10 +333,6 @@ int dispc_set_clock_div(struct dispc_clock_info *cinfo);
+ int dispc_get_clock_div(struct dispc_clock_info *cinfo);
+ void dispc_set_lcd_divisor(u16 lck_div, u16 pck_div);
+
+-void dispc_setup_partial_planes(struct omap_dss_device *dssdev,
+- u16 *x, u16 *y, u16 *w, u16 *h);
+-void dispc_draw_partial_planes(struct omap_dss_device *dssdev);
+-
+
+ /* VENC */
+ int venc_init(struct platform_device *pdev);
+diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c
+index 0b0c51e..7b8e556 100644
+--- a/drivers/video/omap2/dss/manager.c
++++ b/drivers/video/omap2/dss/manager.c
+@@ -25,8 +25,11 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/platform_device.h>
++#include <linux/spinlock.h>
++#include <linux/jiffies.h>
+
+ #include <mach/display.h>
++#include <mach/cpu.h>
+
+ #include "dss.h"
+
+@@ -348,6 +351,106 @@ static struct kobj_type manager_ktype = {
+ .default_attrs = manager_sysfs_attrs,
+ };
+
++/*
++ * We have 4 levels of cache for the dispc settings. First two are in SW and
++ * the latter two in HW.
++ *
++ * +--------------------+
++ * |overlay/manager_info|
++ * +--------------------+
++ * v
++ * apply()
++ * v
++ * +--------------------+
++ * | dss_cache |
++ * +--------------------+
++ * v
++ * configure()
++ * v
++ * +--------------------+
++ * | shadow registers |
++ * +--------------------+
++ * v
++ * VFP or lcd/digit_enable
++ * v
++ * +--------------------+
++ * | registers |
++ * +--------------------+
++ */
++
++struct overlay_cache_data {
++ /* If true, cache changed, but not written to shadow registers. Set
++ * in apply(), cleared when registers written. */
++ bool dirty;
++ /* If true, shadow registers contain changed values not yet in real
++ * registers. Set when writing to shadow registers, cleared at
++ * VSYNC/EVSYNC */
++ bool shadow_dirty;
++
++ bool enabled;
++
++ u32 paddr;
++ void __iomem *vaddr;
++ u16 screen_width;
++ u16 width;
++ u16 height;
++ enum omap_color_mode color_mode;
++ u8 rotation;
++ enum omap_dss_rotation_type rotation_type;
++ bool mirror;
++
++ u16 pos_x;
++ u16 pos_y;
++ u16 out_width; /* if 0, out_width == width */
++ u16 out_height; /* if 0, out_height == height */
++ u8 global_alpha;
++
++ enum omap_channel channel;
++ bool replication;
++ bool ilace;
++
++ enum omap_burst_size burst_size;
++ u32 fifo_low;
++ u32 fifo_high;
++
++ bool manual_update;
++};
++
++struct manager_cache_data {
++ /* If true, cache changed, but not written to shadow registers. Set
++ * in apply(), cleared when registers written. */
++ bool dirty;
++ /* If true, shadow registers contain changed values not yet in real
++ * registers. Set when writing to shadow registers, cleared at
++ * VSYNC/EVSYNC */
++ bool shadow_dirty;
++
++ u32 default_color;
++
++ enum omap_dss_trans_key_type trans_key_type;
++ u32 trans_key;
++ bool trans_enabled;
++
++ bool alpha_enabled;
++
++ bool manual_upd_display;
++ bool manual_update;
++ bool do_manual_update;
++
++ /* manual update region */
++ u16 x, y, w, h;
++};
++
++static struct {
++ spinlock_t lock;
++ struct overlay_cache_data overlay_cache[3];
++ struct manager_cache_data manager_cache[2];
++
++ bool irq_enabled;
++} dss_cache;
++
++
++
+ static int omap_dss_set_device(struct omap_overlay_manager *mgr,
+ struct omap_dss_device *dssdev)
+ {
+@@ -379,6 +482,7 @@ static int omap_dss_set_device(struct omap_overlay_manager *mgr,
+
+ dssdev->manager = mgr;
+ mgr->device = dssdev;
++ mgr->device_changed = true;
+
+ return 0;
+ }
+@@ -392,154 +496,814 @@ static int omap_dss_unset_device(struct omap_overlay_manager *mgr)
+
+ mgr->device->manager = NULL;
+ mgr->device = NULL;
++ mgr->device_changed = true;
+
+ return 0;
+ }
+
++static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
++{
++ unsigned long timeout = msecs_to_jiffies(500);
++ struct manager_cache_data *mc;
++ enum omap_channel channel;
++ u32 irq;
++ int r;
++ int i;
++
++ if (!mgr->device)
++ return 0;
++
++ if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC) {
++ irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN;
++ channel = OMAP_DSS_CHANNEL_DIGIT;
++ } else {
++ if (mgr->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) {
++ enum omap_dss_update_mode mode;
++ mode = mgr->device->get_update_mode(mgr->device);
++ if (mode != OMAP_DSS_UPDATE_AUTO)
++ return 0;
++
++ irq = DISPC_IRQ_FRAMEDONE;
++ } else {
++ irq = DISPC_IRQ_VSYNC;
++ }
++ channel = OMAP_DSS_CHANNEL_LCD;
++ }
++
++ mc = &dss_cache.manager_cache[mgr->id];
++ i = 0;
++ while (1) {
++ unsigned long flags;
++ bool shadow_dirty, dirty;
++
++ spin_lock_irqsave(&dss_cache.lock, flags);
++ dirty = mc->dirty;
++ shadow_dirty = mc->shadow_dirty;
++ spin_unlock_irqrestore(&dss_cache.lock, flags);
++
++ if (!dirty && !shadow_dirty) {
++ r = 0;
++ break;
++ }
++
++ /* 4 iterations is the worst case:
++ * 1 - initial iteration, dirty = true (between VFP and VSYNC)
++ * 2 - first VSYNC, dirty = true
++ * 3 - dirty = false, shadow_dirty = true
++ * 4 - shadow_dirty = false */
++ if (i++ == 3) {
++ DSSERR("mgr(%d)->wait_for_go() not finishing\n",
++ mgr->id);
++ r = 0;
++ break;
++ }
++
++ r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
++ if (r == -ERESTARTSYS)
++ break;
++
++ if (r) {
++ DSSERR("mgr(%d)->wait_for_go() timeout\n", mgr->id);
++ break;
++ }
++ }
++
++ return r;
++}
++
++int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl)
++{
++ unsigned long timeout = msecs_to_jiffies(500);
++ enum omap_channel channel;
++ struct overlay_cache_data *oc;
++ struct omap_dss_device *dssdev;
++ u32 irq;
++ int r;
++ int i;
++
++ if (!ovl->manager || !ovl->manager->device)
++ return 0;
++
++ dssdev = ovl->manager->device;
++
++ if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) {
++ irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN;
++ channel = OMAP_DSS_CHANNEL_DIGIT;
++ } else {
++ if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) {
++ enum omap_dss_update_mode mode;
++ mode = dssdev->get_update_mode(dssdev);
++ if (mode != OMAP_DSS_UPDATE_AUTO)
++ return 0;
++
++ irq = DISPC_IRQ_FRAMEDONE;
++ } else {
++ irq = DISPC_IRQ_VSYNC;
++ }
++ channel = OMAP_DSS_CHANNEL_LCD;
++ }
++
++ oc = &dss_cache.overlay_cache[ovl->id];
++ i = 0;
++ while (1) {
++ unsigned long flags;
++ bool shadow_dirty, dirty;
++
++ spin_lock_irqsave(&dss_cache.lock, flags);
++ dirty = oc->dirty;
++ shadow_dirty = oc->shadow_dirty;
++ spin_unlock_irqrestore(&dss_cache.lock, flags);
++
++ if (!dirty && !shadow_dirty) {
++ r = 0;
++ break;
++ }
++
++ /* 4 iterations is the worst case:
++ * 1 - initial iteration, dirty = true (between VFP and VSYNC)
++ * 2 - first VSYNC, dirty = true
++ * 3 - dirty = false, shadow_dirty = true
++ * 4 - shadow_dirty = false */
++ if (i++ == 3) {
++ DSSERR("ovl(%d)->wait_for_go() not finishing\n",
++ ovl->id);
++ r = 0;
++ break;
++ }
++
++ r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
++ if (r == -ERESTARTSYS)
++ break;
++
++ if (r) {
++ DSSERR("ovl(%d)->wait_for_go() timeout\n", ovl->id);
++ break;
++ }
++ }
++
++ return r;
++}
+
+ static int overlay_enabled(struct omap_overlay *ovl)
+ {
+ return ovl->info.enabled && ovl->manager && ovl->manager->device;
+ }
+
+-/* We apply settings to both managers here so that we can use optimizations
+- * like fifomerge. Shadow registers can be changed first and the non-shadowed
+- * should be changed last, at the same time with GO */
+-static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
++/* Is rect1 a subset of rect2? */
++static bool rectangle_subset(int x1, int y1, int w1, int h1,
++ int x2, int y2, int w2, int h2)
+ {
++ if (x1 < x2 || y1 < y2)
++ return false;
++
++ if (x1 + w1 > x2 + w2)
++ return false;
++
++ if (y1 + h1 > y2 + h2)
++ return false;
++
++ return true;
++}
++
++/* Do rect1 and rect2 overlap? */
++static bool rectangle_intersects(int x1, int y1, int w1, int h1,
++ int x2, int y2, int w2, int h2)
++{
++ if (x1 >= x2 + w2)
++ return false;
++
++ if (x2 >= x1 + w1)
++ return false;
++
++ if (y1 >= y2 + h2)
++ return false;
++
++ if (y2 >= y1 + h1)
++ return false;
++
++ return true;
++}
++
++static bool dispc_is_overlay_scaled(struct overlay_cache_data *oc)
++{
++ if (oc->out_width != 0 && oc->width != oc->out_width)
++ return true;
++
++ if (oc->out_height != 0 && oc->height != oc->out_height)
++ return true;
++
++ return false;
++}
++
++static int configure_overlay(enum omap_plane plane)
++{
++ struct overlay_cache_data *c;
++ struct manager_cache_data *mc;
++ u16 outw, outh;
++ u16 x, y, w, h;
++ u32 paddr;
++ int r;
++
++ DSSDBGF("%d", plane);
++
++ c = &dss_cache.overlay_cache[plane];
++
++ if (!c->enabled) {
++ dispc_enable_plane(plane, 0);
++ return 0;
++ }
++
++ mc = &dss_cache.manager_cache[c->channel];
++
++ x = c->pos_x;
++ y = c->pos_y;
++ w = c->width;
++ h = c->height;
++ outw = c->out_width == 0 ? c->width : c->out_width;
++ outh = c->out_height == 0 ? c->height : c->out_height;
++ paddr = c->paddr;
++
++ if (c->manual_update && mc->do_manual_update) {
++ unsigned bpp;
++ /* 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)) {
++ dispc_enable_plane(plane, 0);
++ return 0;
++ }
++
++ switch (c->color_mode) {
++ case OMAP_DSS_COLOR_RGB16:
++ case OMAP_DSS_COLOR_ARGB16:
++ case OMAP_DSS_COLOR_YUV2:
++ case OMAP_DSS_COLOR_UYVY:
++ bpp = 16;
++ break;
++
++ case OMAP_DSS_COLOR_RGB24P:
++ bpp = 24;
++ break;
++
++ case OMAP_DSS_COLOR_RGB24U:
++ case OMAP_DSS_COLOR_ARGB32:
++ case OMAP_DSS_COLOR_RGBA32:
++ case OMAP_DSS_COLOR_RGBX32:
++ bpp = 32;
++ break;
++
++ default:
++ BUG();
++ }
++
++ 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);
++
++ if (!dispc_is_overlay_scaled(c)) {
++ outw = w;
++ outh = h;
++ }
++ }
++
++ r = dispc_setup_plane(plane,
++ paddr,
++ c->screen_width,
++ x, y,
++ w, h,
++ outw, outh,
++ c->color_mode,
++ c->ilace,
++ c->rotation_type,
++ c->rotation,
++ c->mirror,
++ c->global_alpha);
++
++ if (r) {
++ /* this shouldn't happen */
++ DSSERR("dispc_setup_plane failed for ovl %d\n", plane);
++ dispc_enable_plane(plane, 0);
++ return r;
++ }
++
++ dispc_enable_replication(plane, c->replication);
++
++ dispc_set_burst_size(plane, c->burst_size);
++ dispc_setup_plane_fifo(plane, c->fifo_low, c->fifo_high);
++
++ dispc_enable_plane(plane, 1);
++
++ return 0;
++}
++
++static void configure_manager(enum omap_channel channel)
++{
++ struct manager_cache_data *c;
++
++ DSSDBGF("%d", channel);
++
++ c = &dss_cache.manager_cache[channel];
++
++ dispc_set_trans_key(channel, c->trans_key_type, c->trans_key);
++ dispc_enable_trans_key(channel, c->trans_enabled);
++ dispc_enable_alpha_blending(channel, c->alpha_enabled);
++}
++
++/* configure_dispc() tries to write values from cache to shadow registers.
++ * It writes only to those managers/overlays that are not busy.
++ * returns 0 if everything could be written to shadow registers.
++ * returns 1 if not everything could be written to shadow registers. */
++static int configure_dispc(void)
++{
++ struct overlay_cache_data *oc;
++ struct manager_cache_data *mc;
++ const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache);
++ const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache);
+ int i;
+- int ret = 0;
+- enum omap_dss_update_mode mode;
+- struct omap_dss_device *dssdev;
+- struct omap_overlay *ovl;
+- bool ilace;
+- int outw, outh;
+ int r;
+- int num_planes_enabled = 0;
++ bool mgr_busy[2];
++ bool mgr_go[2];
++ bool busy;
+
+- DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name);
++ r = 0;
++ busy = false;
+
+- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
++ mgr_busy[0] = dispc_go_busy(0);
++ mgr_busy[1] = dispc_go_busy(1);
++ mgr_go[0] = false;
++ mgr_go[1] = false;
+
+- /* Configure normal overlay parameters and disable unused overlays */
+- for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
+- ovl = omap_dss_get_overlay(i);
++ /* Commit overlay settings */
++ for (i = 0; i < num_ovls; ++i) {
++ oc = &dss_cache.overlay_cache[i];
++ mc = &dss_cache.manager_cache[oc->channel];
+
+- if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC))
++ if (!oc->dirty)
+ continue;
+
+- if (!overlay_enabled(ovl)) {
+- dispc_enable_plane(ovl->id, 0);
++ if (oc->manual_update && !mc->do_manual_update)
++ continue;
++
++ if (mgr_busy[oc->channel]) {
++ busy = true;
+ continue;
+ }
+
+- dssdev = ovl->manager->device;
++ r = configure_overlay(i);
++ if (r)
++ DSSERR("configure_overlay %d failed\n", i);
+
+- if (dss_check_overlay(ovl, dssdev)) {
+- dispc_enable_plane(ovl->id, 0);
++ oc->dirty = false;
++ oc->shadow_dirty = true;
++ mgr_go[oc->channel] = true;
++ }
++
++ /* Commit manager settings */
++ for (i = 0; i < num_mgrs; ++i) {
++ mc = &dss_cache.manager_cache[i];
++
++ if (!mc->dirty)
++ continue;
++
++ if (mc->manual_update && !mc->do_manual_update)
++ continue;
++
++ if (mgr_busy[i]) {
++ busy = true;
+ continue;
+ }
+
+- ++num_planes_enabled;
++ configure_manager(i);
++ mc->dirty = false;
++ mc->shadow_dirty = true;
++ mgr_go[i] = true;
++ }
+
+- /* On a manual update display, in manual update mode, update()
+- * handles configuring planes */
+- mode = OMAP_DSS_UPDATE_AUTO;
+- if (dssdev->get_update_mode)
+- mode = dssdev->get_update_mode(dssdev);
++ /* set GO */
++ for (i = 0; i < num_mgrs; ++i) {
++ mc = &dss_cache.manager_cache[i];
+
+- if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE &&
+- mode != OMAP_DSS_UPDATE_AUTO)
++ if (!mgr_go[i])
+ continue;
+
+- ilace = dssdev->type == OMAP_DISPLAY_TYPE_VENC;
++ /* We don't need GO with manual update display. LCD iface will
++ * always be turned off after frame, and new settings will be
++ * taken in to use at next update */
++ if (!mc->manual_upd_display)
++ dispc_go(i);
++ }
++
++ if (busy)
++ r = 1;
++ else
++ r = 0;
++
++ return r;
++}
+
+- if (ovl->info.out_width == 0)
+- outw = ovl->info.width;
++/* 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)
++{
++ struct overlay_cache_data *oc;
++ struct manager_cache_data *mc;
++ const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache);
++ struct omap_overlay_manager *mgr;
++ int i;
++ u16 x, y, w, h;
++ unsigned long flags;
++
++ x = *xi;
++ y = *yi;
++ w = *wi;
++ h = *hi;
++
++ DSSDBG("dispc_setup_partial_planes %d,%d %dx%d\n",
++ *xi, *yi, *wi, *hi);
++
++ mgr = dssdev->manager;
++
++ if (!mgr) {
++ DSSDBG("no manager\n");
++ return;
++ }
++
++ spin_lock_irqsave(&dss_cache.lock, flags);
++
++ /* We need to show the whole overlay if it is scaled. So look for
++ * those, and make the update area larger if found.
++ * Also mark the overlay cache dirty */
++ for (i = 0; i < num_ovls; ++i) {
++ unsigned x1, y1, x2, y2;
++ unsigned outw, outh;
++
++ oc = &dss_cache.overlay_cache[i];
++
++ if (oc->channel != mgr->id)
++ continue;
++
++ oc->dirty = true;
++
++ if (!oc->enabled)
++ continue;
++
++ if (!dispc_is_overlay_scaled(oc))
++ continue;
++
++ outw = oc->out_width == 0 ? oc->width : oc->out_width;
++ outh = oc->out_height == 0 ? oc->height : oc->out_height;
++
++ /* is the overlay outside the update region? */
++ if (!rectangle_intersects(x, y, w, h,
++ oc->pos_x, oc->pos_y,
++ outw, outh))
++ continue;
++
++ /* if the overlay totally inside the update region? */
++ if (rectangle_subset(oc->pos_x, oc->pos_y, outw, outh,
++ x, y, w, h))
++ continue;
++
++ if (x > oc->pos_x)
++ x1 = oc->pos_x;
+ else
+- outw = ovl->info.out_width;
++ x1 = x;
+
+- if (ovl->info.out_height == 0)
+- outh = ovl->info.height;
++ if (y > oc->pos_y)
++ y1 = oc->pos_y;
+ else
+- outh = ovl->info.out_height;
+-
+- r = dispc_setup_plane(ovl->id, ovl->manager->id,
+- ovl->info.paddr,
+- ovl->info.screen_width,
+- ovl->info.pos_x,
+- ovl->info.pos_y,
+- ovl->info.width,
+- ovl->info.height,
+- outw,
+- outh,
+- ovl->info.color_mode,
+- ilace,
+- ovl->info.rotation_type,
+- ovl->info.rotation,
+- ovl->info.mirror,
+- ovl->info.global_alpha);
++ y1 = y;
+
+- if (r) {
+- DSSERR("dispc_setup_plane failed for ovl %d\n",
+- ovl->id);
+- dispc_enable_plane(ovl->id, 0);
+- continue;
+- }
++ if ((x + w) < (oc->pos_x + outw))
++ x2 = oc->pos_x + outw;
++ else
++ x2 = x + w;
+
+- if (dss_use_replication(dssdev, ovl->info.color_mode))
+- dispc_enable_replication(ovl->id, true);
++ if ((y + h) < (oc->pos_y + outh))
++ y2 = oc->pos_y + outh;
+ else
+- dispc_enable_replication(ovl->id, false);
++ y2 = y + h;
++
++ x = x1;
++ y = y1;
++ w = x2 - x1;
++ h = y2 - y1;
++
++ DSSDBG("changing upd area due to ovl(%d) scaling %d,%d %dx%d\n",
++ i, x, y, w, h);
++ }
++
++ mc = &dss_cache.manager_cache[mgr->id];
++ mc->do_manual_update = true;
++ mc->x = x;
++ mc->y = y;
++ mc->w = w;
++ mc->h = h;
++
++ configure_dispc();
++
++ mc->do_manual_update = false;
++
++ spin_unlock_irqrestore(&dss_cache.lock, flags);
++
++ *xi = x;
++ *yi = y;
++ *wi = w;
++ *hi = h;
++}
++
++void dss_start_update(struct omap_dss_device *dssdev)
++{
++ struct manager_cache_data *mc;
++ struct overlay_cache_data *oc;
++ const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache);
++ const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache);
++ struct omap_overlay_manager *mgr;
++ int i;
++
++ mgr = dssdev->manager;
++
++ for (i = 0; i < num_ovls; ++i) {
++ oc = &dss_cache.overlay_cache[i];
++ if (oc->channel != mgr->id)
++ continue;
++
++ oc->shadow_dirty = false;
++ }
++
++ for (i = 0; i < num_mgrs; ++i) {
++ mc = &dss_cache.manager_cache[i];
++ if (mgr->id != i)
++ continue;
++
++ mc->shadow_dirty = false;
++ }
++
++ dispc_enable_lcd_out(1);
++}
++
++static void dss_apply_irq_handler(void *data, u32 mask)
++{
++ struct manager_cache_data *mc;
++ struct overlay_cache_data *oc;
++ const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache);
++ const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache);
++ int i, r;
++ bool mgr_busy[2];
++
++ mgr_busy[0] = dispc_go_busy(0);
++ mgr_busy[1] = dispc_go_busy(1);
++
++ spin_lock(&dss_cache.lock);
++
++ for (i = 0; i < num_ovls; ++i) {
++ oc = &dss_cache.overlay_cache[i];
++ if (!mgr_busy[oc->channel])
++ oc->shadow_dirty = false;
++ }
++
++ for (i = 0; i < num_mgrs; ++i) {
++ mc = &dss_cache.manager_cache[i];
++ if (!mgr_busy[i])
++ mc->shadow_dirty = false;
++ }
++
++ r = configure_dispc();
++ if (r == 1)
++ goto end;
++
++ /* re-read busy flags */
++ mgr_busy[0] = dispc_go_busy(0);
++ mgr_busy[1] = dispc_go_busy(1);
+
+- dispc_enable_plane(ovl->id, 1);
++ /* keep running as long as there are busy managers, so that
++ * we can collect overlay-applied information */
++ for (i = 0; i < num_mgrs; ++i) {
++ if (mgr_busy[i])
++ goto end;
+ }
+
+- /* Enable fifo merge if possible */
+- dispc_enable_fifomerge(num_planes_enabled == 1);
++ omap_dispc_unregister_isr(dss_apply_irq_handler, NULL,
++ DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD |
++ DISPC_IRQ_EVSYNC_EVEN);
++ dss_cache.irq_enabled = false;
++
++end:
++ spin_unlock(&dss_cache.lock);
++}
++
++static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
++{
++ struct overlay_cache_data *oc;
++ struct manager_cache_data *mc;
++ int i;
++ struct omap_overlay *ovl;
++ int num_planes_enabled = 0;
++ bool use_fifomerge;
++ unsigned long flags;
++ int r;
+
+- /* Go through overlays again. This time we configure fifos. We have to
+- * do this after enabling/disabling fifomerge so that we have correct
+- * knowledge of fifo sizes */
++ DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name);
++
++ spin_lock_irqsave(&dss_cache.lock, flags);
++
++ /* Configure overlays */
+ for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
++ struct omap_dss_device *dssdev;
++
+ ovl = omap_dss_get_overlay(i);
+
+ if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC))
+ continue;
+
+- if (!overlay_enabled(ovl))
++ oc = &dss_cache.overlay_cache[ovl->id];
++
++ if (!overlay_enabled(ovl)) {
++ if (oc->enabled) {
++ oc->enabled = false;
++ oc->dirty = true;
++ }
++ continue;
++ }
++
++ if (!ovl->info_dirty) {
++ if (oc->enabled)
++ ++num_planes_enabled;
++ continue;
++ }
++
++ dssdev = ovl->manager->device;
++
++ if (dss_check_overlay(ovl, dssdev)) {
++ if (oc->enabled) {
++ oc->enabled = false;
++ oc->dirty = true;
++ }
+ continue;
++ }
++
++ ovl->info_dirty = false;
++ oc->dirty = true;
++
++ oc->paddr = ovl->info.paddr;
++ oc->vaddr = ovl->info.vaddr;
++ oc->screen_width = ovl->info.screen_width;
++ oc->width = ovl->info.width;
++ oc->height = ovl->info.height;
++ oc->color_mode = ovl->info.color_mode;
++ oc->rotation = ovl->info.rotation;
++ oc->rotation_type = ovl->info.rotation_type;
++ oc->mirror = ovl->info.mirror;
++ oc->pos_x = ovl->info.pos_x;
++ oc->pos_y = ovl->info.pos_y;
++ oc->out_width = ovl->info.out_width;
++ oc->out_height = ovl->info.out_height;
++ oc->global_alpha = ovl->info.global_alpha;
+
+- ovl->manager->device->configure_overlay(ovl);
++ oc->replication =
++ dss_use_replication(dssdev, ovl->info.color_mode);
++
++ oc->ilace = dssdev->type == OMAP_DISPLAY_TYPE_VENC;
++
++ oc->channel = ovl->manager->id;
++
++ oc->enabled = true;
++
++ oc->manual_update =
++ dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE &&
++ dssdev->get_update_mode(dssdev) != OMAP_DSS_UPDATE_AUTO;
++
++ ++num_planes_enabled;
+ }
+
+- /* Configure managers, and issue GO */
++ /* Configure managers */
+ list_for_each_entry(mgr, &manager_list, list) {
++ struct omap_dss_device *dssdev;
++
+ if (!(mgr->caps & OMAP_DSS_OVL_MGR_CAP_DISPC))
+ continue;
+
++ mc = &dss_cache.manager_cache[mgr->id];
++
++ if (mgr->device_changed) {
++ mgr->device_changed = false;
++ mgr->info_dirty = true;
++ }
++
++ if (!mgr->info_dirty)
++ continue;
++
++ if (!mgr->device)
++ continue;
++
+ dssdev = mgr->device;
+
+- if (!dssdev)
++ mgr->info_dirty = false;
++ mc->dirty = true;
++
++ mc->default_color = mgr->info.default_color;
++ mc->trans_key_type = mgr->info.trans_key_type;
++ mc->trans_key = mgr->info.trans_key;
++ mc->trans_enabled = mgr->info.trans_enabled;
++ mc->alpha_enabled = mgr->info.alpha_enabled;
++
++ mc->manual_upd_display =
++ dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
++
++ mc->manual_update =
++ dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE &&
++ dssdev->get_update_mode(dssdev) != OMAP_DSS_UPDATE_AUTO;
++ }
++
++ /* XXX TODO: Try to get fifomerge working. The problem is that it
++ * affects both managers, not individually but at the same time. This
++ * means the change has to be well synchronized. I guess the proper way
++ * is to have a two step process for fifo merge:
++ * fifomerge enable:
++ * 1. disable other planes, leaving one plane enabled
++ * 2. wait until the planes are disabled on HW
++ * 3. config merged fifo thresholds, enable fifomerge
++ * fifomerge disable:
++ * 1. config unmerged fifo thresholds, disable fifomerge
++ * 2. wait until fifo changes are in HW
++ * 3. enable planes
++ */
++ use_fifomerge = false;
++
++ /* Configure overlay fifos */
++ for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
++ struct omap_dss_device *dssdev;
++ u32 size;
++
++ ovl = omap_dss_get_overlay(i);
++
++ if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC))
+ continue;
+
+- dispc_set_trans_key(mgr->id, mgr->info.trans_key_type,
+- mgr->info.trans_key);
+- dispc_enable_trans_key(mgr->id, mgr->info.trans_enabled);
+- dispc_enable_alpha_blending(mgr->id, mgr->info.alpha_enabled);
++ oc = &dss_cache.overlay_cache[ovl->id];
+
+- /* We don't need GO with manual update display. LCD iface will
+- * always be turned off after frame, and new settings will
+- * be taken in to use at next update */
+- if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE)
++ if (!oc->enabled)
+ continue;
+
+- dispc_go(mgr->id);
++ dssdev = ovl->manager->device;
++
++ size = dispc_get_plane_fifo_size(ovl->id);
++ if (use_fifomerge)
++ size *= 3;
++
++ switch (dssdev->type) {
++ case OMAP_DISPLAY_TYPE_DPI:
++ case OMAP_DISPLAY_TYPE_DBI:
++ case OMAP_DISPLAY_TYPE_SDI:
++ case OMAP_DISPLAY_TYPE_VENC:
++ default_get_overlay_fifo_thresholds(ovl->id, size,
++ &oc->burst_size, &oc->fifo_low,
++ &oc->fifo_high);
++ break;
++#ifdef CONFIG_OMAP2_DSS_DSI
++ case OMAP_DISPLAY_TYPE_DSI:
++ dsi_get_overlay_fifo_thresholds(ovl->id, size,
++ &oc->burst_size, &oc->fifo_low,
++ &oc->fifo_high);
++ break;
++#endif
++ default:
++ BUG();
++ }
+ }
+
++ r = 0;
++ dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
++ if (!dss_cache.irq_enabled) {
++ r = omap_dispc_register_isr(dss_apply_irq_handler, NULL,
++ DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD |
++ DISPC_IRQ_EVSYNC_EVEN);
++ dss_cache.irq_enabled = true;
++ }
++ configure_dispc();
+ dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+- return ret;
++ spin_unlock_irqrestore(&dss_cache.lock, flags);
++
++ return r;
+ }
+
+ static int dss_check_manager(struct omap_overlay_manager *mgr)
+@@ -569,6 +1333,8 @@ static int omap_dss_mgr_set_info(struct omap_overlay_manager *mgr,
+ return r;
+ }
+
++ mgr->info_dirty = true;
++
+ return 0;
+ }
+
+@@ -588,6 +1354,8 @@ int dss_init_overlay_managers(struct platform_device *pdev)
+ {
+ int i, r;
+
++ spin_lock_init(&dss_cache.lock);
++
+ INIT_LIST_HEAD(&manager_list);
+
+ num_managers = 0;
+@@ -618,6 +1386,7 @@ int dss_init_overlay_managers(struct platform_device *pdev)
+ mgr->apply = &omap_dss_mgr_apply;
+ mgr->set_manager_info = &omap_dss_mgr_set_info;
+ mgr->get_manager_info = &omap_dss_mgr_get_info;
++ mgr->wait_for_go = &dss_mgr_wait_for_go;
+
+ mgr->caps = OMAP_DSS_OVL_MGR_CAP_DISPC;
+
+diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c
+index 9f883aa..3652106 100644
+--- a/drivers/video/omap2/dss/overlay.c
++++ b/drivers/video/omap2/dss/overlay.c
+@@ -52,6 +52,7 @@ static ssize_t overlay_manager_store(struct omap_overlay *ovl, const char *buf,
+ {
+ int i, r;
+ struct omap_overlay_manager *mgr = NULL;
++ struct omap_overlay_manager *old_mgr;
+ int len = size;
+
+ if (buf[size-1] == '\n')
+@@ -74,27 +75,32 @@ static ssize_t overlay_manager_store(struct omap_overlay *ovl, const char *buf,
+ if (mgr)
+ DSSDBG("manager %s found\n", mgr->name);
+
+- if (mgr != ovl->manager) {
+- /* detach old manager */
+- if (ovl->manager) {
+- r = ovl->unset_manager(ovl);
+- if (r) {
+- DSSERR("detach failed\n");
+- return r;
+- }
+- }
++ if (mgr == ovl->manager)
++ return size;
+
+- if (mgr) {
+- r = ovl->set_manager(ovl, mgr);
+- if (r) {
+- DSSERR("Failed to attach overlay\n");
+- return r;
+- }
++ old_mgr = ovl->manager;
++
++ /* detach old manager */
++ if (old_mgr) {
++ r = ovl->unset_manager(ovl);
++ if (r) {
++ DSSERR("detach failed\n");
++ return r;
+ }
++
++ r = old_mgr->apply(old_mgr);
++ if (r)
++ return r;
+ }
+
+- if (ovl->manager) {
+- r = ovl->manager->apply(ovl->manager);
++ if (mgr) {
++ r = ovl->set_manager(ovl, mgr);
++ if (r) {
++ DSSERR("Failed to attach overlay\n");
++ return r;
++ }
++
++ r = mgr->apply(mgr);
+ if (r)
+ return r;
+ }
+@@ -403,6 +409,8 @@ static int dss_ovl_set_overlay_info(struct omap_overlay *ovl,
+ }
+ }
+
++ ovl->info_dirty = true;
++
+ return 0;
+ }
+
+@@ -412,22 +420,40 @@ static void dss_ovl_get_overlay_info(struct omap_overlay *ovl,
+ *info = ovl->info;
+ }
+
++static int dss_ovl_wait_for_go(struct omap_overlay *ovl)
++{
++ return dss_mgr_wait_for_go_ovl(ovl);
++}
++
+ static int omap_dss_set_manager(struct omap_overlay *ovl,
+ struct omap_overlay_manager *mgr)
+ {
+ int r;
+
++ if (!mgr)
++ return -EINVAL;
++
+ if (ovl->manager) {
+ DSSERR("overlay '%s' already has a manager '%s'\n",
+ ovl->name, ovl->manager->name);
++ return -EINVAL;
+ }
+
+- r = dss_check_overlay(ovl, mgr->device);
++ r = ovl->wait_for_go(ovl);
+ if (r)
+ return r;
+
++ if (ovl->info.enabled) {
++ DSSERR("overlay has to be disabled to change the manager\n");
++ return -EINVAL;
++ }
++
+ ovl->manager = mgr;
+
++ dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
++ dispc_set_channel_out(ovl->id, mgr->id);
++ dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
++
+ return 0;
+ }
+
+@@ -439,6 +465,7 @@ static int omap_dss_unset_manager(struct omap_overlay *ovl)
+ }
+
+ ovl->manager = NULL;
++ /* XXX disable overlay? */
+
+ return 0;
+ }
+@@ -530,6 +557,7 @@ void dss_init_overlays(struct platform_device *pdev)
+ ovl->unset_manager = &omap_dss_unset_manager;
+ ovl->set_overlay_info = &dss_ovl_set_overlay_info;
+ ovl->get_overlay_info = &dss_ovl_get_overlay_info;
++ ovl->wait_for_go = &dss_ovl_wait_for_go;
+
+ omap_dss_add_overlay(ovl);
+
+diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c
+index 96602ae..9dd2349 100644
+--- a/drivers/video/omap2/dss/rfbi.c
++++ b/drivers/video/omap2/dss/rfbi.c
+@@ -972,7 +972,7 @@ static int do_update(struct omap_dss_device *dssdev, struct update_region *upd)
+
+ if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
+ /*dssdev->driver->enable_te(dssdev, 1); */
+- dispc_setup_partial_planes(dssdev, &x, &y, &w, &h);
++ dss_setup_partial_planes(dssdev, &x, &y, &w, &h);
+ }
+
+ #ifdef MEASURE_PERF
+diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c
+index d8a83b2..a9a5a8c 100644
+--- a/drivers/video/omap2/dss/venc.c
++++ b/drivers/video/omap2/dss/venc.c
+@@ -711,6 +711,15 @@ static int venc_set_wss(struct omap_dss_device *dssdev, u32 wss)
+ return 0;
+ }
+
++static enum omap_dss_update_mode venc_display_get_update_mode(
++ struct omap_dss_device *dssdev)
++{
++ if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
++ return OMAP_DSS_UPDATE_AUTO;
++ else
++ return OMAP_DSS_UPDATE_DISABLED;
++}
++
+ int venc_init_display(struct omap_dss_device *dssdev)
+ {
+ DSSDBG("init_display\n");
+@@ -724,6 +733,7 @@ int venc_init_display(struct omap_dss_device *dssdev)
+ dssdev->check_timings = venc_check_timings;
+ dssdev->get_wss = venc_get_wss;
+ dssdev->set_wss = venc_set_wss;
++ dssdev->get_update_mode = venc_display_get_update_mode;
+
+ return 0;
+ }
+diff --git a/drivers/video/omap2/omapfb/omapfb-ioctl.c b/drivers/video/omap2/omapfb/omapfb-ioctl.c
+index 91308c2..257f7cb 100644
+--- a/drivers/video/omap2/omapfb/omapfb-ioctl.c
++++ b/drivers/video/omap2/omapfb/omapfb-ioctl.c
+@@ -442,6 +442,22 @@ static int omapfb_get_ovl_colormode(struct omapfb2_device *fbdev,
+ return 0;
+ }
+
++static int omapfb_wait_for_go(struct fb_info *fbi)
++{
++ struct omapfb_info *ofbi = FB2OFB(fbi);
++ int r = 0;
++ int i;
++
++ for (i = 0; i < ofbi->num_overlays; ++i) {
++ struct omap_overlay *ovl = ofbi->overlays[i];
++ r = ovl->wait_for_go(ovl);
++ if (r)
++ break;
++ }
++
++ return r;
++}
++
+ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
+ {
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+@@ -624,6 +640,16 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
+ r = display->wait_vsync(display);
+ break;
+
++ case OMAPFB_WAITFORGO:
++ DBG("ioctl WAITFORGO\n");
++ if (!display) {
++ r = -EINVAL;
++ break;
++ }
++
++ r = omapfb_wait_for_go(fbi);
++ break;
++
+ /* LCD and CTRL tests do the same thing for backward
+ * compatibility */
+ case OMAPFB_LCD_TEST:
+diff --git a/include/linux/omapfb.h b/include/linux/omapfb.h
+index fd522d3..5014d2e 100644
+--- a/include/linux/omapfb.h
++++ b/include/linux/omapfb.h
+@@ -55,6 +55,7 @@
+ #define OMAPFB_WAITFORVSYNC OMAP_IO(57)
+ #define OMAPFB_MEMORY_READ OMAP_IOR(58, struct omapfb_memory_read)
+ #define OMAPFB_GET_OVERLAY_COLORMODE OMAP_IOR(59, struct omapfb_ovl_colormode)
++#define OMAPFB_WAITFORGO OMAP_IO(60)
+
+ #define OMAPFB_CAPS_GENERIC_MASK 0x00000fff
+ #define OMAPFB_CAPS_LCDC_MASK 0x00fff000
+--
+1.6.2.4
+