Merge tag 'fbdev-updates-for-3.7' of git://github.com/schandinat/linux-2.6
[linux-2.6-microblaze.git] / drivers / video / omap2 / dss / dispc.c
index ee9e296..b43477a 100644 (file)
@@ -38,7 +38,6 @@
 #include <linux/pm_runtime.h>
 
 #include <plat/cpu.h>
-#include <plat/clock.h>
 
 #include <video/omapdss.h>
 
@@ -82,6 +81,30 @@ struct dispc_irq_stats {
        unsigned irqs[32];
 };
 
+struct dispc_features {
+       u8 sw_start;
+       u8 fp_start;
+       u8 bp_start;
+       u16 sw_max;
+       u16 vp_max;
+       u16 hp_max;
+       int (*calc_scaling) (enum omap_plane plane,
+               const struct omap_video_timings *mgr_timings,
+               u16 width, u16 height, u16 out_width, u16 out_height,
+               enum omap_color_mode color_mode, bool *five_taps,
+               int *x_predecim, int *y_predecim, int *decim_x, int *decim_y,
+               u16 pos_x, unsigned long *core_clk, bool mem_to_mem);
+       unsigned long (*calc_core_clk) (enum omap_plane plane,
+               u16 width, u16 height, u16 out_width, u16 out_height,
+               bool mem_to_mem);
+       u8 num_fifos;
+
+       /* swap GFX & WB fifos */
+       bool gfx_fifo_workaround:1;
+};
+
+#define DISPC_MAX_NR_FIFOS 5
+
 static struct {
        struct platform_device *pdev;
        void __iomem    *base;
@@ -91,7 +114,9 @@ static struct {
        int irq;
        struct clk *dss_clk;
 
-       u32     fifo_size[MAX_DSS_OVERLAYS];
+       u32 fifo_size[DISPC_MAX_NR_FIFOS];
+       /* maps which plane is using a fifo. fifo-id -> plane-id */
+       int fifo_assignment[DISPC_MAX_NR_FIFOS];
 
        spinlock_t irq_lock;
        u32 irq_error_mask;
@@ -102,6 +127,8 @@ static struct {
        bool            ctx_valid;
        u32             ctx[DISPC_SZ_REGS / sizeof(u32)];
 
+       const struct dispc_features *feat;
+
 #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
        spinlock_t irq_stats_lock;
        struct dispc_irq_stats irq_stats;
@@ -211,7 +238,14 @@ static const struct {
        },
 };
 
+struct color_conv_coef {
+       int ry, rcr, rcb, gy, gcr, gcb, by, bcr, bcb;
+       int full_range;
+};
+
 static void _omap_dispc_set_irqs(void);
+static unsigned long dispc_plane_pclk_rate(enum omap_plane plane);
+static unsigned long dispc_plane_lclk_rate(enum omap_plane plane);
 
 static inline void dispc_write_reg(const u16 idx, u32 val)
 {
@@ -509,6 +543,11 @@ u32 dispc_mgr_get_framedone_irq(enum omap_channel channel)
        return mgr_desc[channel].framedone_irq;
 }
 
+u32 dispc_wb_get_framedone_irq(void)
+{
+       return DISPC_IRQ_FRAMEDONEWB;
+}
+
 bool dispc_mgr_go_busy(enum omap_channel channel)
 {
        return mgr_fld_read(channel, DISPC_MGR_FLD_GO) == 1;
@@ -536,6 +575,30 @@ void dispc_mgr_go(enum omap_channel channel)
        mgr_fld_write(channel, DISPC_MGR_FLD_GO, 1);
 }
 
+bool dispc_wb_go_busy(void)
+{
+       return REG_GET(DISPC_CONTROL2, 6, 6) == 1;
+}
+
+void dispc_wb_go(void)
+{
+       enum omap_plane plane = OMAP_DSS_WB;
+       bool enable, go;
+
+       enable = REG_GET(DISPC_OVL_ATTRIBUTES(plane), 0, 0) == 1;
+
+       if (!enable)
+               return;
+
+       go = REG_GET(DISPC_CONTROL2, 6, 6) == 1;
+       if (go) {
+               DSSERR("GO bit not down for WB\n");
+               return;
+       }
+
+       REG_FLD_MOD(DISPC_CONTROL2, 1, 6, 6);
+}
+
 static void dispc_ovl_write_firh_reg(enum omap_plane plane, int reg, u32 value)
 {
        dispc_write_reg(DISPC_OVL_FIR_COEF_H(plane, reg), value);
@@ -618,41 +681,41 @@ static void dispc_ovl_set_scale_coef(enum omap_plane plane, int fir_hinc,
        }
 }
 
-static void _dispc_setup_color_conv_coef(void)
-{
-       int i;
-       const struct color_conv_coef {
-               int  ry,  rcr,  rcb,   gy,  gcr,  gcb,   by,  bcr,  bcb;
-               int  full_range;
-       }  ctbl_bt601_5 = {
-               298,  409,    0,  298, -208, -100,  298,    0,  517, 0,
-       };
-
-       const struct color_conv_coef *ct;
 
+static void dispc_ovl_write_color_conv_coef(enum omap_plane plane,
+               const struct color_conv_coef *ct)
+{
 #define CVAL(x, y) (FLD_VAL(x, 26, 16) | FLD_VAL(y, 10, 0))
 
-       ct = &ctbl_bt601_5;
+       dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 0), CVAL(ct->rcr, ct->ry));
+       dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 1), CVAL(ct->gy,  ct->rcb));
+       dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 2), CVAL(ct->gcb, ct->gcr));
+       dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 3), CVAL(ct->bcr, ct->by));
+       dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 4), CVAL(0, ct->bcb));
 
-       for (i = 1; i < dss_feat_get_num_ovls(); i++) {
-               dispc_write_reg(DISPC_OVL_CONV_COEF(i, 0),
-                       CVAL(ct->rcr, ct->ry));
-               dispc_write_reg(DISPC_OVL_CONV_COEF(i, 1),
-                       CVAL(ct->gy,  ct->rcb));
-               dispc_write_reg(DISPC_OVL_CONV_COEF(i, 2),
-                       CVAL(ct->gcb, ct->gcr));
-               dispc_write_reg(DISPC_OVL_CONV_COEF(i, 3),
-                       CVAL(ct->bcr, ct->by));
-               dispc_write_reg(DISPC_OVL_CONV_COEF(i, 4),
-                       CVAL(0, ct->bcb));
-
-               REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(i), ct->full_range,
-                       11, 11);
-       }
+       REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), ct->full_range, 11, 11);
 
 #undef CVAL
 }
 
+static void dispc_setup_color_conv_coef(void)
+{
+       int i;
+       int num_ovl = dss_feat_get_num_ovls();
+       int num_wb = dss_feat_get_num_wbs();
+       const struct color_conv_coef ctbl_bt601_5_ovl = {
+               298, 409, 0, 298, -208, -100, 298, 0, 517, 0,
+       };
+       const struct color_conv_coef ctbl_bt601_5_wb = {
+               66, 112, -38, 129, -94, -74, 25, -18, 112, 0,
+       };
+
+       for (i = 1; i < num_ovl; i++)
+               dispc_ovl_write_color_conv_coef(i, &ctbl_bt601_5_ovl);
+
+       for (; i < num_wb; i++)
+               dispc_ovl_write_color_conv_coef(i, &ctbl_bt601_5_wb);
+}
 
 static void dispc_ovl_set_ba0(enum omap_plane plane, u32 paddr)
 {
@@ -674,24 +737,32 @@ static void dispc_ovl_set_ba1_uv(enum omap_plane plane, u32 paddr)
        dispc_write_reg(DISPC_OVL_BA1_UV(plane), paddr);
 }
 
-static void dispc_ovl_set_pos(enum omap_plane plane, int x, int y)
+static void dispc_ovl_set_pos(enum omap_plane plane,
+               enum omap_overlay_caps caps, int x, int y)
 {
-       u32 val = FLD_VAL(y, 26, 16) | FLD_VAL(x, 10, 0);
+       u32 val;
+
+       if ((caps & OMAP_DSS_OVL_CAP_POS) == 0)
+               return;
+
+       val = FLD_VAL(y, 26, 16) | FLD_VAL(x, 10, 0);
 
        dispc_write_reg(DISPC_OVL_POSITION(plane), val);
 }
 
-static void dispc_ovl_set_pic_size(enum omap_plane plane, int width, int height)
+static void dispc_ovl_set_input_size(enum omap_plane plane, int width,
+               int height)
 {
        u32 val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0);
 
-       if (plane == OMAP_DSS_GFX)
+       if (plane == OMAP_DSS_GFX || plane == OMAP_DSS_WB)
                dispc_write_reg(DISPC_OVL_SIZE(plane), val);
        else
                dispc_write_reg(DISPC_OVL_PICTURE_SIZE(plane), val);
 }
 
-static void dispc_ovl_set_vid_size(enum omap_plane plane, int width, int height)
+static void dispc_ovl_set_output_size(enum omap_plane plane, int width,
+               int height)
 {
        u32 val;
 
@@ -699,14 +770,16 @@ static void dispc_ovl_set_vid_size(enum omap_plane plane, int width, int height)
 
        val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0);
 
-       dispc_write_reg(DISPC_OVL_SIZE(plane), val);
+       if (plane == OMAP_DSS_WB)
+               dispc_write_reg(DISPC_OVL_PICTURE_SIZE(plane), val);
+       else
+               dispc_write_reg(DISPC_OVL_SIZE(plane), val);
 }
 
-static void dispc_ovl_set_zorder(enum omap_plane plane, u8 zorder)
+static void dispc_ovl_set_zorder(enum omap_plane plane,
+               enum omap_overlay_caps caps, u8 zorder)
 {
-       struct omap_overlay *ovl = omap_dss_get_overlay(plane);
-
-       if ((ovl->caps & OMAP_DSS_OVL_CAP_ZORDER) == 0)
+       if ((caps & OMAP_DSS_OVL_CAP_ZORDER) == 0)
                return;
 
        REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), zorder, 27, 26);
@@ -723,23 +796,22 @@ static void dispc_ovl_enable_zorder_planes(void)
                REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(i), 1, 25, 25);
 }
 
-static void dispc_ovl_set_pre_mult_alpha(enum omap_plane plane, bool enable)
+static void dispc_ovl_set_pre_mult_alpha(enum omap_plane plane,
+               enum omap_overlay_caps caps, bool enable)
 {
-       struct omap_overlay *ovl = omap_dss_get_overlay(plane);
-
-       if ((ovl->caps & OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA) == 0)
+       if ((caps & OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA) == 0)
                return;
 
        REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable ? 1 : 0, 28, 28);
 }
 
-static void dispc_ovl_setup_global_alpha(enum omap_plane plane, u8 global_alpha)
+static void dispc_ovl_setup_global_alpha(enum omap_plane plane,
+               enum omap_overlay_caps caps, u8 global_alpha)
 {
        static const unsigned shifts[] = { 0, 8, 16, 24, };
        int shift;
-       struct omap_overlay *ovl = omap_dss_get_overlay(plane);
 
-       if ((ovl->caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0)
+       if ((caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0)
                return;
 
        shift = shifts[plane];
@@ -947,10 +1019,17 @@ static enum omap_channel dispc_ovl_get_channel_out(enum omap_plane plane)
        return channel;
 }
 
+void dispc_wb_set_channel_in(enum dss_writeback_channel channel)
+{
+       enum omap_plane plane = OMAP_DSS_WB;
+
+       REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), channel, 18, 16);
+}
+
 static void dispc_ovl_set_burst_size(enum omap_plane plane,
                enum omap_burst_size burst_size)
 {
-       static const unsigned shifts[] = { 6, 14, 14, 14, };
+       static const unsigned shifts[] = { 6, 14, 14, 14, 14, };
        int shift;
 
        shift = shifts[plane];
@@ -1027,11 +1106,15 @@ static void dispc_ovl_set_vid_color_conv(enum omap_plane plane, bool enable)
        dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), val);
 }
 
-static void dispc_ovl_enable_replication(enum omap_plane plane, bool enable)
+static void dispc_ovl_enable_replication(enum omap_plane plane,
+               enum omap_overlay_caps caps, bool enable)
 {
        static const unsigned shifts[] = { 5, 10, 10, 10 };
        int shift;
 
+       if ((caps & OMAP_DSS_OVL_CAP_REPLICATION) == 0)
+               return;
+
        shift = shifts[plane];
        REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable, shift, shift);
 }
@@ -1045,10 +1128,10 @@ static void dispc_mgr_set_size(enum omap_channel channel, u16 width,
        dispc_write_reg(DISPC_SIZE_MGR(channel), val);
 }
 
-static void dispc_read_plane_fifo_sizes(void)
+static void dispc_init_fifos(void)
 {
        u32 size;
-       int plane;
+       int fifo;
        u8 start, end;
        u32 unit;
 
@@ -1056,16 +1139,53 @@ static void dispc_read_plane_fifo_sizes(void)
 
        dss_feat_get_reg_field(FEAT_REG_FIFOSIZE, &start, &end);
 
-       for (plane = 0; plane < dss_feat_get_num_ovls(); ++plane) {
-               size = REG_GET(DISPC_OVL_FIFO_SIZE_STATUS(plane), start, end);
+       for (fifo = 0; fifo < dispc.feat->num_fifos; ++fifo) {
+               size = REG_GET(DISPC_OVL_FIFO_SIZE_STATUS(fifo), start, end);
                size *= unit;
-               dispc.fifo_size[plane] = size;
+               dispc.fifo_size[fifo] = size;
+
+               /*
+                * By default fifos are mapped directly to overlays, fifo 0 to
+                * ovl 0, fifo 1 to ovl 1, etc.
+                */
+               dispc.fifo_assignment[fifo] = fifo;
+       }
+
+       /*
+        * The GFX fifo on OMAP4 is smaller than the other fifos. The small fifo
+        * causes problems with certain use cases, like using the tiler in 2D
+        * mode. The below hack swaps the fifos of GFX and WB planes, thus
+        * giving GFX plane a larger fifo. WB but should work fine with a
+        * smaller fifo.
+        */
+       if (dispc.feat->gfx_fifo_workaround) {
+               u32 v;
+
+               v = dispc_read_reg(DISPC_GLOBAL_BUFFER);
+
+               v = FLD_MOD(v, 4, 2, 0); /* GFX BUF top to WB */
+               v = FLD_MOD(v, 4, 5, 3); /* GFX BUF bottom to WB */
+               v = FLD_MOD(v, 0, 26, 24); /* WB BUF top to GFX */
+               v = FLD_MOD(v, 0, 29, 27); /* WB BUF bottom to GFX */
+
+               dispc_write_reg(DISPC_GLOBAL_BUFFER, v);
+
+               dispc.fifo_assignment[OMAP_DSS_GFX] = OMAP_DSS_WB;
+               dispc.fifo_assignment[OMAP_DSS_WB] = OMAP_DSS_GFX;
        }
 }
 
 static u32 dispc_ovl_get_fifo_size(enum omap_plane plane)
 {
-       return dispc.fifo_size[plane];
+       int fifo;
+       u32 size = 0;
+
+       for (fifo = 0; fifo < dispc.feat->num_fifos; ++fifo) {
+               if (dispc.fifo_assignment[fifo] == plane)
+                       size += dispc.fifo_size[fifo];
+       }
+
+       return size;
 }
 
 void dispc_ovl_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high)
@@ -1141,6 +1261,14 @@ void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane,
        if (manual_update && dss_has_feature(FEAT_OMAP3_DSI_FIFO_BUG)) {
                *fifo_low = ovl_fifo_size - burst_size * 2;
                *fifo_high = total_fifo_size - burst_size;
+       } else if (plane == OMAP_DSS_WB) {
+               /*
+                * Most optimal configuration for writeback is to push out data
+                * to the interconnect the moment writeback pushes enough pixels
+                * in the FIFO to form a burst
+                */
+               *fifo_low = 0;
+               *fifo_high = burst_size;
        } else {
                *fifo_low = ovl_fifo_size - burst_size;
                *fifo_high = total_fifo_size - buf_unit;
@@ -1383,6 +1511,7 @@ static void dispc_ovl_set_scaling_uv(enum omap_plane plane,
 {
        int scale_x = out_width != orig_width;
        int scale_y = out_height != orig_height;
+       bool chroma_upscale = plane != OMAP_DSS_WB ? true : false;
 
        if (!dss_has_feature(FEAT_HANDLE_UV_SEPARATE))
                return;
@@ -1390,7 +1519,8 @@ static void dispc_ovl_set_scaling_uv(enum omap_plane plane,
                        color_mode != OMAP_DSS_COLOR_UYVY &&
                        color_mode != OMAP_DSS_COLOR_NV12)) {
                /* reset chroma resampling for RGB formats  */
-               REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane), 0, 8, 8);
+               if (plane != OMAP_DSS_WB)
+                       REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane), 0, 8, 8);
                return;
        }
 
@@ -1399,23 +1529,34 @@ static void dispc_ovl_set_scaling_uv(enum omap_plane plane,
 
        switch (color_mode) {
        case OMAP_DSS_COLOR_NV12:
-               /* UV is subsampled by 2 vertically*/
-               orig_height >>= 1;
-               /* UV is subsampled by 2 horz.*/
-               orig_width >>= 1;
+               if (chroma_upscale) {
+                       /* UV is subsampled by 2 horizontally and vertically */
+                       orig_height >>= 1;
+                       orig_width >>= 1;
+               } else {
+                       /* UV is downsampled by 2 horizontally and vertically */
+                       orig_height <<= 1;
+                       orig_width <<= 1;
+               }
+
                break;
        case OMAP_DSS_COLOR_YUV2:
        case OMAP_DSS_COLOR_UYVY:
-               /*For YUV422 with 90/270 rotation,
-                *we don't upsample chroma
-                */
+               /* For YUV422 with 90/270 rotation, we don't upsample chroma */
                if (rotation == OMAP_DSS_ROT_0 ||
-                       rotation == OMAP_DSS_ROT_180)
-                       /* UV is subsampled by 2 hrz*/
-                       orig_width >>= 1;
+                               rotation == OMAP_DSS_ROT_180) {
+                       if (chroma_upscale)
+                               /* UV is subsampled by 2 horizontally */
+                               orig_width >>= 1;
+                       else
+                               /* UV is downsampled by 2 horizontally */
+                               orig_width <<= 1;
+               }
+
                /* must use FIR for YUV422 if rotated */
                if (rotation != OMAP_DSS_ROT_0)
                        scale_x = scale_y = true;
+
                break;
        default:
                BUG();
@@ -1431,8 +1572,10 @@ static void dispc_ovl_set_scaling_uv(enum omap_plane plane,
                        out_width, out_height, five_taps,
                                rotation, DISPC_COLOR_COMPONENT_UV);
 
-       REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane),
-               (scale_x || scale_y) ? 1 : 0, 8, 8);
+       if (plane != OMAP_DSS_WB)
+               REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane),
+                       (scale_x || scale_y) ? 1 : 0, 8, 8);
+
        /* set H scaling */
        REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), scale_x ? 1 : 0, 5, 5);
        /* set V scaling */
@@ -1848,22 +1991,19 @@ static void calc_tiler_rotation_offset(u16 screen_width, u16 width,
  * This function is used to avoid synclosts in OMAP3, because of some
  * undocumented horizontal position and timing related limitations.
  */
-static int check_horiz_timing_omap3(enum omap_channel channel,
+static int check_horiz_timing_omap3(enum omap_plane plane,
                const struct omap_video_timings *t, u16 pos_x,
                u16 width, u16 height, u16 out_width, u16 out_height)
 {
        int DS = DIV_ROUND_UP(height, out_height);
-       unsigned long nonactive, lclk, pclk;
+       unsigned long nonactive;
        static const u8 limits[3] = { 8, 10, 20 };
        u64 val, blank;
+       unsigned long pclk = dispc_plane_pclk_rate(plane);
+       unsigned long lclk = dispc_plane_lclk_rate(plane);
        int i;
 
        nonactive = t->x_res + t->hfp + t->hsw + t->hbp - out_width;
-       pclk = dispc_mgr_pclk_rate(channel);
-       if (dss_mgr_is_lcd(channel))
-               lclk = dispc_mgr_lclk_rate(channel);
-       else
-               lclk = dispc_fclk_rate();
 
        i = 0;
        if (out_height < height)
@@ -1900,13 +2040,14 @@ static int check_horiz_timing_omap3(enum omap_channel channel,
        return 0;
 }
 
-static unsigned long calc_core_clk_five_taps(enum omap_channel channel,
+static unsigned long calc_core_clk_five_taps(enum omap_plane plane,
                const struct omap_video_timings *mgr_timings, u16 width,
                u16 height, u16 out_width, u16 out_height,
                enum omap_color_mode color_mode)
 {
        u32 core_clk = 0;
-       u64 tmp, pclk = dispc_mgr_pclk_rate(channel);
+       u64 tmp;
+       unsigned long pclk = dispc_plane_pclk_rate(plane);
 
        if (height <= out_height && width <= out_width)
                return (unsigned long) pclk;
@@ -1940,11 +2081,22 @@ static unsigned long calc_core_clk_five_taps(enum omap_channel channel,
        return core_clk;
 }
 
-static unsigned long calc_core_clk(enum omap_channel channel, u16 width,
-               u16 height, u16 out_width, u16 out_height)
+static unsigned long calc_core_clk_24xx(enum omap_plane plane, u16 width,
+               u16 height, u16 out_width, u16 out_height, bool mem_to_mem)
+{
+       unsigned long pclk = dispc_plane_pclk_rate(plane);
+
+       if (height > out_height && width > out_width)
+               return pclk * 4;
+       else
+               return pclk * 2;
+}
+
+static unsigned long calc_core_clk_34xx(enum omap_plane plane, u16 width,
+               u16 height, u16 out_width, u16 out_height, bool mem_to_mem)
 {
        unsigned int hf, vf;
-       unsigned long pclk = dispc_mgr_pclk_rate(channel);
+       unsigned long pclk = dispc_plane_pclk_rate(plane);
 
        /*
         * FIXME how to determine the 'A' factor
@@ -1959,51 +2111,207 @@ static unsigned long calc_core_clk(enum omap_channel channel, u16 width,
                hf = 2;
        else
                hf = 1;
-
        if (height > out_height)
                vf = 2;
        else
                vf = 1;
 
-       if (cpu_is_omap24xx()) {
-               if (vf > 1 && hf > 1)
-                       return pclk * 4;
-               else
-                       return pclk * 2;
-       } else if (cpu_is_omap34xx()) {
-               return pclk * vf * hf;
-       } else {
-               if (hf > 1)
-                       return DIV_ROUND_UP(pclk, out_width) * width;
-               else
-                       return pclk;
+       return pclk * vf * hf;
+}
+
+static unsigned long calc_core_clk_44xx(enum omap_plane plane, u16 width,
+               u16 height, u16 out_width, u16 out_height, bool mem_to_mem)
+{
+       unsigned long pclk;
+
+       /*
+        * If the overlay/writeback is in mem to mem mode, there are no
+        * downscaling limitations with respect to pixel clock, return 1 as
+        * required core clock to represent that we have sufficient enough
+        * core clock to do maximum downscaling
+        */
+       if (mem_to_mem)
+               return 1;
+
+       pclk = dispc_plane_pclk_rate(plane);
+
+       if (width > out_width)
+               return DIV_ROUND_UP(pclk, out_width) * width;
+       else
+               return pclk;
+}
+
+static int dispc_ovl_calc_scaling_24xx(enum omap_plane plane,
+               const struct omap_video_timings *mgr_timings,
+               u16 width, u16 height, u16 out_width, u16 out_height,
+               enum omap_color_mode color_mode, bool *five_taps,
+               int *x_predecim, int *y_predecim, int *decim_x, int *decim_y,
+               u16 pos_x, unsigned long *core_clk, bool mem_to_mem)
+{
+       int error;
+       u16 in_width, in_height;
+       int min_factor = min(*decim_x, *decim_y);
+       const int maxsinglelinewidth =
+                       dss_feat_get_param_max(FEAT_PARAM_LINEWIDTH);
+
+       *five_taps = false;
+
+       do {
+               in_height = DIV_ROUND_UP(height, *decim_y);
+               in_width = DIV_ROUND_UP(width, *decim_x);
+               *core_clk = dispc.feat->calc_core_clk(plane, in_width,
+                               in_height, out_width, out_height, mem_to_mem);
+               error = (in_width > maxsinglelinewidth || !*core_clk ||
+                       *core_clk > dispc_core_clk_rate());
+               if (error) {
+                       if (*decim_x == *decim_y) {
+                               *decim_x = min_factor;
+                               ++*decim_y;
+                       } else {
+                               swap(*decim_x, *decim_y);
+                               if (*decim_x < *decim_y)
+                                       ++*decim_x;
+                       }
+               }
+       } while (*decim_x <= *x_predecim && *decim_y <= *y_predecim && error);
+
+       if (in_width > maxsinglelinewidth) {
+               DSSERR("Cannot scale max input width exceeded");
+               return -EINVAL;
        }
+       return 0;
 }
 
-static int dispc_ovl_calc_scaling(enum omap_plane plane,
-               enum omap_channel channel,
+static int dispc_ovl_calc_scaling_34xx(enum omap_plane plane,
                const struct omap_video_timings *mgr_timings,
                u16 width, u16 height, u16 out_width, u16 out_height,
                enum omap_color_mode color_mode, bool *five_taps,
-               int *x_predecim, int *y_predecim, u16 pos_x)
+               int *x_predecim, int *y_predecim, int *decim_x, int *decim_y,
+               u16 pos_x, unsigned long *core_clk, bool mem_to_mem)
 {
-       struct omap_overlay *ovl = omap_dss_get_overlay(plane);
-       const int maxdownscale = dss_feat_get_param_max(FEAT_PARAM_DOWNSCALE);
+       int error;
+       u16 in_width, in_height;
+       int min_factor = min(*decim_x, *decim_y);
+       const int maxsinglelinewidth =
+                       dss_feat_get_param_max(FEAT_PARAM_LINEWIDTH);
+
+       do {
+               in_height = DIV_ROUND_UP(height, *decim_y);
+               in_width = DIV_ROUND_UP(width, *decim_x);
+               *core_clk = calc_core_clk_five_taps(plane, mgr_timings,
+                       in_width, in_height, out_width, out_height, color_mode);
+
+               error = check_horiz_timing_omap3(plane, mgr_timings,
+                               pos_x, in_width, in_height, out_width,
+                               out_height);
+
+               if (in_width > maxsinglelinewidth)
+                       if (in_height > out_height &&
+                                               in_height < out_height * 2)
+                               *five_taps = false;
+               if (!*five_taps)
+                       *core_clk = dispc.feat->calc_core_clk(plane, in_width,
+                                       in_height, out_width, out_height,
+                                       mem_to_mem);
+
+               error = (error || in_width > maxsinglelinewidth * 2 ||
+                       (in_width > maxsinglelinewidth && *five_taps) ||
+                       !*core_clk || *core_clk > dispc_core_clk_rate());
+               if (error) {
+                       if (*decim_x == *decim_y) {
+                               *decim_x = min_factor;
+                               ++*decim_y;
+                       } else {
+                               swap(*decim_x, *decim_y);
+                               if (*decim_x < *decim_y)
+                                       ++*decim_x;
+                       }
+               }
+       } while (*decim_x <= *x_predecim && *decim_y <= *y_predecim && error);
+
+       if (check_horiz_timing_omap3(plane, mgr_timings, pos_x, width, height,
+               out_width, out_height)){
+                       DSSERR("horizontal timing too tight\n");
+                       return -EINVAL;
+       }
+
+       if (in_width > (maxsinglelinewidth * 2)) {
+               DSSERR("Cannot setup scaling");
+               DSSERR("width exceeds maximum width possible");
+               return -EINVAL;
+       }
+
+       if (in_width > maxsinglelinewidth && *five_taps) {
+               DSSERR("cannot setup scaling with five taps");
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int dispc_ovl_calc_scaling_44xx(enum omap_plane plane,
+               const struct omap_video_timings *mgr_timings,
+               u16 width, u16 height, u16 out_width, u16 out_height,
+               enum omap_color_mode color_mode, bool *five_taps,
+               int *x_predecim, int *y_predecim, int *decim_x, int *decim_y,
+               u16 pos_x, unsigned long *core_clk, bool mem_to_mem)
+{
+       u16 in_width, in_width_max;
+       int decim_x_min = *decim_x;
+       u16 in_height = DIV_ROUND_UP(height, *decim_y);
        const int maxsinglelinewidth =
                                dss_feat_get_param_max(FEAT_PARAM_LINEWIDTH);
+       unsigned long pclk = dispc_plane_pclk_rate(plane);
+       const int maxdownscale = dss_feat_get_param_max(FEAT_PARAM_DOWNSCALE);
+
+       if (mem_to_mem)
+               in_width_max = DIV_ROUND_UP(out_width, maxdownscale);
+       else
+               in_width_max = dispc_core_clk_rate() /
+                                       DIV_ROUND_UP(pclk, out_width);
+
+       *decim_x = DIV_ROUND_UP(width, in_width_max);
+
+       *decim_x = *decim_x > decim_x_min ? *decim_x : decim_x_min;
+       if (*decim_x > *x_predecim)
+               return -EINVAL;
+
+       do {
+               in_width = DIV_ROUND_UP(width, *decim_x);
+       } while (*decim_x <= *x_predecim &&
+                       in_width > maxsinglelinewidth && ++*decim_x);
+
+       if (in_width > maxsinglelinewidth) {
+               DSSERR("Cannot scale width exceeds max line width");
+               return -EINVAL;
+       }
+
+       *core_clk = dispc.feat->calc_core_clk(plane, in_width, in_height,
+                               out_width, out_height, mem_to_mem);
+       return 0;
+}
+
+static int dispc_ovl_calc_scaling(enum omap_plane plane,
+               enum omap_overlay_caps caps,
+               const struct omap_video_timings *mgr_timings,
+               u16 width, u16 height, u16 out_width, u16 out_height,
+               enum omap_color_mode color_mode, bool *five_taps,
+               int *x_predecim, int *y_predecim, u16 pos_x,
+               enum omap_dss_rotation_type rotation_type, bool mem_to_mem)
+{
+       const int maxdownscale = dss_feat_get_param_max(FEAT_PARAM_DOWNSCALE);
        const int max_decim_limit = 16;
        unsigned long core_clk = 0;
-       int decim_x, decim_y, error, min_factor;
-       u16 in_width, in_height, in_width_max = 0;
+       int decim_x, decim_y, ret;
 
        if (width == out_width && height == out_height)
                return 0;
 
-       if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0)
+       if ((caps & OMAP_DSS_OVL_CAP_SCALE) == 0)
                return -EINVAL;
 
        *x_predecim = max_decim_limit;
-       *y_predecim = max_decim_limit;
+       *y_predecim = (rotation_type == OMAP_DSS_ROT_TILER &&
+                       dss_has_feature(FEAT_BURST_2D)) ? 2 : max_decim_limit;
 
        if (color_mode == OMAP_DSS_COLOR_CLUT1 ||
            color_mode == OMAP_DSS_COLOR_CLUT2 ||
@@ -2018,118 +2326,18 @@ static int dispc_ovl_calc_scaling(enum omap_plane plane,
        decim_x = DIV_ROUND_UP(DIV_ROUND_UP(width, out_width), maxdownscale);
        decim_y = DIV_ROUND_UP(DIV_ROUND_UP(height, out_height), maxdownscale);
 
-       min_factor = min(decim_x, decim_y);
-
        if (decim_x > *x_predecim || out_width > width * 8)
                return -EINVAL;
 
        if (decim_y > *y_predecim || out_height > height * 8)
                return -EINVAL;
 
-       if (cpu_is_omap24xx()) {
-               *five_taps = false;
-
-               do {
-                       in_height = DIV_ROUND_UP(height, decim_y);
-                       in_width = DIV_ROUND_UP(width, decim_x);
-                       core_clk = calc_core_clk(channel, in_width, in_height,
-                                       out_width, out_height);
-                       error = (in_width > maxsinglelinewidth || !core_clk ||
-                               core_clk > dispc_core_clk_rate());
-                       if (error) {
-                               if (decim_x == decim_y) {
-                                       decim_x = min_factor;
-                                       decim_y++;
-                               } else {
-                                       swap(decim_x, decim_y);
-                                       if (decim_x < decim_y)
-                                               decim_x++;
-                               }
-                       }
-               } while (decim_x <= *x_predecim && decim_y <= *y_predecim &&
-                               error);
-
-               if (in_width > maxsinglelinewidth) {
-                       DSSERR("Cannot scale max input width exceeded");
-                       return -EINVAL;
-               }
-       } else if (cpu_is_omap34xx()) {
-
-               do {
-                       in_height = DIV_ROUND_UP(height, decim_y);
-                       in_width = DIV_ROUND_UP(width, decim_x);
-                       core_clk = calc_core_clk_five_taps(channel, mgr_timings,
-                               in_width, in_height, out_width, out_height,
-                               color_mode);
-
-                       error = check_horiz_timing_omap3(channel, mgr_timings,
-                               pos_x, in_width, in_height, out_width,
-                               out_height);
-
-                       if (in_width > maxsinglelinewidth)
-                               if (in_height > out_height &&
-                                       in_height < out_height * 2)
-                                       *five_taps = false;
-                       if (!*five_taps)
-                               core_clk = calc_core_clk(channel, in_width,
-                                       in_height, out_width, out_height);
-                       error = (error || in_width > maxsinglelinewidth * 2 ||
-                               (in_width > maxsinglelinewidth && *five_taps) ||
-                               !core_clk || core_clk > dispc_core_clk_rate());
-                       if (error) {
-                               if (decim_x == decim_y) {
-                                       decim_x = min_factor;
-                                       decim_y++;
-                               } else {
-                                       swap(decim_x, decim_y);
-                                       if (decim_x < decim_y)
-                                               decim_x++;
-                               }
-                       }
-               } while (decim_x <= *x_predecim && decim_y <= *y_predecim
-                       && error);
-
-               if (check_horiz_timing_omap3(channel, mgr_timings, pos_x, width,
-                       height, out_width, out_height)){
-                               DSSERR("horizontal timing too tight\n");
-                               return -EINVAL;
-               }
-
-               if (in_width > (maxsinglelinewidth * 2)) {
-                       DSSERR("Cannot setup scaling");
-                       DSSERR("width exceeds maximum width possible");
-                       return -EINVAL;
-               }
-
-               if (in_width > maxsinglelinewidth && *five_taps) {
-                       DSSERR("cannot setup scaling with five taps");
-                       return -EINVAL;
-               }
-       } else {
-               int decim_x_min = decim_x;
-               in_height = DIV_ROUND_UP(height, decim_y);
-               in_width_max = dispc_core_clk_rate() /
-                               DIV_ROUND_UP(dispc_mgr_pclk_rate(channel),
-                                               out_width);
-               decim_x = DIV_ROUND_UP(width, in_width_max);
-
-               decim_x = decim_x > decim_x_min ? decim_x : decim_x_min;
-               if (decim_x > *x_predecim)
-                       return -EINVAL;
-
-               do {
-                       in_width = DIV_ROUND_UP(width, decim_x);
-               } while (decim_x <= *x_predecim &&
-                               in_width > maxsinglelinewidth && decim_x++);
-
-               if (in_width > maxsinglelinewidth) {
-                       DSSERR("Cannot scale width exceeds max line width");
-                       return -EINVAL;
-               }
-
-               core_clk = calc_core_clk(channel, in_width, in_height,
-                               out_width, out_height);
-       }
+       ret = dispc.feat->calc_scaling(plane, mgr_timings, width, height,
+               out_width, out_height, color_mode, five_taps,
+               x_predecim, y_predecim, &decim_x, &decim_y, pos_x, &core_clk,
+               mem_to_mem);
+       if (ret)
+               return ret;
 
        DSSDBG("required core clk rate = %lu Hz\n", core_clk);
        DSSDBG("current core clk rate = %lu Hz\n", dispc_core_clk_rate());
@@ -2147,69 +2355,64 @@ static int dispc_ovl_calc_scaling(enum omap_plane plane,
        return 0;
 }
 
-int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi,
-               bool replication, const struct omap_video_timings *mgr_timings)
+static int dispc_ovl_setup_common(enum omap_plane plane,
+               enum omap_overlay_caps caps, u32 paddr, u32 p_uv_addr,
+               u16 screen_width, int pos_x, int pos_y, u16 width, u16 height,
+               u16 out_width, u16 out_height, enum omap_color_mode color_mode,
+               u8 rotation, bool mirror, u8 zorder, u8 pre_mult_alpha,
+               u8 global_alpha, enum omap_dss_rotation_type rotation_type,
+               bool replication, const struct omap_video_timings *mgr_timings,
+               bool mem_to_mem)
 {
-       struct omap_overlay *ovl = omap_dss_get_overlay(plane);
        bool five_taps = true;
        bool fieldmode = 0;
        int r, cconv = 0;
        unsigned offset0, offset1;
        s32 row_inc;
        s32 pix_inc;
-       u16 frame_height = oi->height;
+       u16 frame_height = height;
        unsigned int field_offset = 0;
-       u16 in_height = oi->height;
-       u16 in_width = oi->width;
-       u16 out_width, out_height;
-       enum omap_channel channel;
+       u16 in_height = height;
+       u16 in_width = width;
        int x_predecim = 1, y_predecim = 1;
        bool ilace = mgr_timings->interlace;
 
-       channel = dispc_ovl_get_channel_out(plane);
-
-       DSSDBG("dispc_ovl_setup %d, pa %x, pa_uv %x, sw %d, %d,%d, %dx%d -> "
-               "%dx%d, cmode %x, rot %d, mir %d, ilace %d chan %d repl %d\n",
-               plane, oi->paddr, oi->p_uv_addr,
-               oi->screen_width, oi->pos_x, oi->pos_y, oi->width, oi->height,
-               oi->out_width, oi->out_height, oi->color_mode, oi->rotation,
-               oi->mirror, ilace, channel, replication);
-
-       if (oi->paddr == 0)
+       if (paddr == 0)
                return -EINVAL;
 
-       out_width = oi->out_width == 0 ? oi->width : oi->out_width;
-       out_height = oi->out_height == 0 ? oi->height : oi->out_height;
+       out_width = out_width == 0 ? width : out_width;
+       out_height = out_height == 0 ? height : out_height;
 
-       if (ilace && oi->height == out_height)
+       if (ilace && height == out_height)
                fieldmode = 1;
 
        if (ilace) {
                if (fieldmode)
                        in_height /= 2;
-               oi->pos_y /= 2;
+               pos_y /= 2;
                out_height /= 2;
 
                DSSDBG("adjusting for ilace: height %d, pos_y %d, "
-                               "out_height %d\n",
-                               in_height, oi->pos_y, out_height);
+                       "out_height %d\n", in_height, pos_y,
+                       out_height);
        }
 
-       if (!dss_feat_color_mode_supported(plane, oi->color_mode))
+       if (!dss_feat_color_mode_supported(plane, color_mode))
                return -EINVAL;
 
-       r = dispc_ovl_calc_scaling(plane, channel, mgr_timings, in_width,
-                       in_height, out_width, out_height, oi->color_mode,
-                       &five_taps, &x_predecim, &y_predecim, oi->pos_x);
+       r = dispc_ovl_calc_scaling(plane, caps, mgr_timings, in_width,
+                       in_height, out_width, out_height, color_mode,
+                       &five_taps, &x_predecim, &y_predecim, pos_x,
+                       rotation_type, mem_to_mem);
        if (r)
                return r;
 
        in_width = DIV_ROUND_UP(in_width, x_predecim);
        in_height = DIV_ROUND_UP(in_height, y_predecim);
 
-       if (oi->color_mode == OMAP_DSS_COLOR_YUV2 ||
-                       oi->color_mode == OMAP_DSS_COLOR_UYVY ||
-                       oi->color_mode == OMAP_DSS_COLOR_NV12)
+       if (color_mode == OMAP_DSS_COLOR_YUV2 ||
+                       color_mode == OMAP_DSS_COLOR_UYVY ||
+                       color_mode == OMAP_DSS_COLOR_NV12)
                cconv = 1;
 
        if (ilace && !fieldmode) {
@@ -2235,70 +2438,144 @@ int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi,
        row_inc = 0;
        pix_inc = 0;
 
-       if (oi->rotation_type == OMAP_DSS_ROT_TILER)
-               calc_tiler_rotation_offset(oi->screen_width, in_width,
-                               oi->color_mode, fieldmode, field_offset,
+       if (rotation_type == OMAP_DSS_ROT_TILER)
+               calc_tiler_rotation_offset(screen_width, in_width,
+                               color_mode, fieldmode, field_offset,
                                &offset0, &offset1, &row_inc, &pix_inc,
                                x_predecim, y_predecim);
-       else if (oi->rotation_type == OMAP_DSS_ROT_DMA)
-               calc_dma_rotation_offset(oi->rotation, oi->mirror,
-                               oi->screen_width, in_width, frame_height,
-                               oi->color_mode, fieldmode, field_offset,
+       else if (rotation_type == OMAP_DSS_ROT_DMA)
+               calc_dma_rotation_offset(rotation, mirror,
+                               screen_width, in_width, frame_height,
+                               color_mode, fieldmode, field_offset,
                                &offset0, &offset1, &row_inc, &pix_inc,
                                x_predecim, y_predecim);
        else
-               calc_vrfb_rotation_offset(oi->rotation, oi->mirror,
-                               oi->screen_width, in_width, frame_height,
-                               oi->color_mode, fieldmode, field_offset,
+               calc_vrfb_rotation_offset(rotation, mirror,
+                               screen_width, in_width, frame_height,
+                               color_mode, fieldmode, field_offset,
                                &offset0, &offset1, &row_inc, &pix_inc,
                                x_predecim, y_predecim);
 
        DSSDBG("offset0 %u, offset1 %u, row_inc %d, pix_inc %d\n",
                        offset0, offset1, row_inc, pix_inc);
 
-       dispc_ovl_set_color_mode(plane, oi->color_mode);
+       dispc_ovl_set_color_mode(plane, color_mode);
 
-       dispc_ovl_configure_burst_type(plane, oi->rotation_type);
+       dispc_ovl_configure_burst_type(plane, rotation_type);
 
-       dispc_ovl_set_ba0(plane, oi->paddr + offset0);
-       dispc_ovl_set_ba1(plane, oi->paddr + offset1);
+       dispc_ovl_set_ba0(plane, paddr + offset0);
+       dispc_ovl_set_ba1(plane, paddr + offset1);
 
-       if (OMAP_DSS_COLOR_NV12 == oi->color_mode) {
-               dispc_ovl_set_ba0_uv(plane, oi->p_uv_addr + offset0);
-               dispc_ovl_set_ba1_uv(plane, oi->p_uv_addr + offset1);
+       if (OMAP_DSS_COLOR_NV12 == color_mode) {
+               dispc_ovl_set_ba0_uv(plane, p_uv_addr + offset0);
+               dispc_ovl_set_ba1_uv(plane, p_uv_addr + offset1);
        }
 
-
        dispc_ovl_set_row_inc(plane, row_inc);
        dispc_ovl_set_pix_inc(plane, pix_inc);
 
-       DSSDBG("%d,%d %dx%d -> %dx%d\n", oi->pos_x, oi->pos_y, in_width,
+       DSSDBG("%d,%d %dx%d -> %dx%d\n", pos_x, pos_y, in_width,
                        in_height, out_width, out_height);
 
-       dispc_ovl_set_pos(plane, oi->pos_x, oi->pos_y);
+       dispc_ovl_set_pos(plane, caps, pos_x, pos_y);
 
-       dispc_ovl_set_pic_size(plane, in_width, in_height);
+       dispc_ovl_set_input_size(plane, in_width, in_height);
 
-       if (ovl->caps & OMAP_DSS_OVL_CAP_SCALE) {
+       if (caps & OMAP_DSS_OVL_CAP_SCALE) {
                dispc_ovl_set_scaling(plane, in_width, in_height, out_width,
                                   out_height, ilace, five_taps, fieldmode,
-                                  oi->color_mode, oi->rotation);
-               dispc_ovl_set_vid_size(plane, out_width, out_height);
+                                  color_mode, rotation);
+               dispc_ovl_set_output_size(plane, out_width, out_height);
                dispc_ovl_set_vid_color_conv(plane, cconv);
        }
 
-       dispc_ovl_set_rotation_attrs(plane, oi->rotation, oi->mirror,
-                       oi->color_mode);
+       dispc_ovl_set_rotation_attrs(plane, rotation, mirror, color_mode);
 
-       dispc_ovl_set_zorder(plane, oi->zorder);
-       dispc_ovl_set_pre_mult_alpha(plane, oi->pre_mult_alpha);
-       dispc_ovl_setup_global_alpha(plane, oi->global_alpha);
+       dispc_ovl_set_zorder(plane, caps, zorder);
+       dispc_ovl_set_pre_mult_alpha(plane, caps, pre_mult_alpha);
+       dispc_ovl_setup_global_alpha(plane, caps, global_alpha);
 
-       dispc_ovl_enable_replication(plane, replication);
+       dispc_ovl_enable_replication(plane, caps, replication);
 
        return 0;
 }
 
+int dispc_ovl_setup(enum omap_plane plane, const struct omap_overlay_info *oi,
+               bool replication, const struct omap_video_timings *mgr_timings,
+               bool mem_to_mem)
+{
+       int r;
+       struct omap_overlay *ovl = omap_dss_get_overlay(plane);
+       enum omap_channel channel;
+
+       channel = dispc_ovl_get_channel_out(plane);
+
+       DSSDBG("dispc_ovl_setup %d, pa %x, pa_uv %x, sw %d, %d,%d, %dx%d -> "
+               "%dx%d, cmode %x, rot %d, mir %d, chan %d repl %d\n",
+               plane, oi->paddr, oi->p_uv_addr, oi->screen_width, oi->pos_x,
+               oi->pos_y, oi->width, oi->height, oi->out_width, oi->out_height,
+               oi->color_mode, oi->rotation, oi->mirror, channel, replication);
+
+       r = dispc_ovl_setup_common(plane, ovl->caps, oi->paddr, oi->p_uv_addr,
+               oi->screen_width, oi->pos_x, oi->pos_y, oi->width, oi->height,
+               oi->out_width, oi->out_height, oi->color_mode, oi->rotation,
+               oi->mirror, oi->zorder, oi->pre_mult_alpha, oi->global_alpha,
+               oi->rotation_type, replication, mgr_timings, mem_to_mem);
+
+       return r;
+}
+
+int dispc_wb_setup(const struct omap_dss_writeback_info *wi,
+               bool mem_to_mem, const struct omap_video_timings *mgr_timings)
+{
+       int r;
+       u32 l;
+       enum omap_plane plane = OMAP_DSS_WB;
+       const int pos_x = 0, pos_y = 0;
+       const u8 zorder = 0, global_alpha = 0;
+       const bool replication = false;
+       bool truncation;
+       int in_width = mgr_timings->x_res;
+       int in_height = mgr_timings->y_res;
+       enum omap_overlay_caps caps =
+               OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA;
+
+       DSSDBG("dispc_wb_setup, pa %x, pa_uv %x, %d,%d -> %dx%d, cmode %x, "
+               "rot %d, mir %d\n", wi->paddr, wi->p_uv_addr, in_width,
+               in_height, wi->width, wi->height, wi->color_mode, wi->rotation,
+               wi->mirror);
+
+       r = dispc_ovl_setup_common(plane, caps, wi->paddr, wi->p_uv_addr,
+               wi->buf_width, pos_x, pos_y, in_width, in_height, wi->width,
+               wi->height, wi->color_mode, wi->rotation, wi->mirror, zorder,
+               wi->pre_mult_alpha, global_alpha, wi->rotation_type,
+               replication, mgr_timings, mem_to_mem);
+
+       switch (wi->color_mode) {
+       case OMAP_DSS_COLOR_RGB16:
+       case OMAP_DSS_COLOR_RGB24P:
+       case OMAP_DSS_COLOR_ARGB16:
+       case OMAP_DSS_COLOR_RGBA16:
+       case OMAP_DSS_COLOR_RGB12U:
+       case OMAP_DSS_COLOR_ARGB16_1555:
+       case OMAP_DSS_COLOR_XRGB16_1555:
+       case OMAP_DSS_COLOR_RGBX16:
+               truncation = true;
+               break;
+       default:
+               truncation = false;
+               break;
+       }
+
+       /* setup extra DISPC_WB_ATTRIBUTES */
+       l = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane));
+       l = FLD_MOD(l, truncation, 10, 10);     /* TRUNCATIONENABLE */
+       l = FLD_MOD(l, mem_to_mem, 19, 19);     /* WRITEBACKMODE */
+       dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), l);
+
+       return r;
+}
+
 int dispc_ovl_enable(enum omap_plane plane, bool enable)
 {
        DSSDBG("dispc_enable_plane %d, %d\n", plane, enable);
@@ -2451,6 +2728,47 @@ void dispc_mgr_enable(enum omap_channel channel, bool enable)
                BUG();
 }
 
+void dispc_wb_enable(bool enable)
+{
+       enum omap_plane plane = OMAP_DSS_WB;
+       struct completion frame_done_completion;
+       bool is_on;
+       int r;
+       u32 irq;
+
+       is_on = REG_GET(DISPC_OVL_ATTRIBUTES(plane), 0, 0);
+       irq = DISPC_IRQ_FRAMEDONEWB;
+
+       if (!enable && is_on) {
+               init_completion(&frame_done_completion);
+
+               r = omap_dispc_register_isr(dispc_disable_isr,
+                               &frame_done_completion, irq);
+               if (r)
+                       DSSERR("failed to register FRAMEDONEWB isr\n");
+       }
+
+       REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable ? 1 : 0, 0, 0);
+
+       if (!enable && is_on) {
+               if (!wait_for_completion_timeout(&frame_done_completion,
+                                       msecs_to_jiffies(100)))
+                       DSSERR("timeout waiting for FRAMEDONEWB\n");
+
+               r = omap_dispc_unregister_isr(dispc_disable_isr,
+                               &frame_done_completion, irq);
+               if (r)
+                       DSSERR("failed to unregister FRAMEDONEWB isr\n");
+       }
+}
+
+bool dispc_wb_is_enabled(void)
+{
+       enum omap_plane plane = OMAP_DSS_WB;
+
+       return REG_GET(DISPC_OVL_ATTRIBUTES(plane), 0, 0);
+}
+
 void dispc_lcd_enable_signal_polarity(bool act_high)
 {
        if (!dss_has_feature(FEAT_LCDENABLEPOL))
@@ -2605,24 +2923,13 @@ static bool _dispc_mgr_size_ok(u16 width, u16 height)
 static bool _dispc_lcd_timings_ok(int hsw, int hfp, int hbp,
                int vsw, int vfp, int vbp)
 {
-       if (cpu_is_omap24xx() || omap_rev() < OMAP3430_REV_ES3_0) {
-               if (hsw < 1 || hsw > 64 ||
-                               hfp < 1 || hfp > 256 ||
-                               hbp < 1 || hbp > 256 ||
-                               vsw < 1 || vsw > 64 ||
-                               vfp < 0 || vfp > 255 ||
-                               vbp < 0 || vbp > 255)
-                       return false;
-       } else {
-               if (hsw < 1 || hsw > 256 ||
-                               hfp < 1 || hfp > 4096 ||
-                               hbp < 1 || hbp > 4096 ||
-                               vsw < 1 || vsw > 256 ||
-                               vfp < 0 || vfp > 4095 ||
-                               vbp < 0 || vbp > 4095)
-                       return false;
-       }
-
+       if (hsw < 1 || hsw > dispc.feat->sw_max ||
+                       hfp < 1 || hfp > dispc.feat->hp_max ||
+                       hbp < 1 || hbp > dispc.feat->hp_max ||
+                       vsw < 1 || vsw > dispc.feat->sw_max ||
+                       vfp < 0 || vfp > dispc.feat->vp_max ||
+                       vbp < 0 || vbp > dispc.feat->vp_max)
+               return false;
        return true;
 }
 
@@ -2654,19 +2961,12 @@ static void _dispc_mgr_set_lcd_timings(enum omap_channel channel, int hsw,
        u32 timing_h, timing_v, l;
        bool onoff, rf, ipc;
 
-       if (cpu_is_omap24xx() || omap_rev() < OMAP3430_REV_ES3_0) {
-               timing_h = FLD_VAL(hsw-1, 5, 0) | FLD_VAL(hfp-1, 15, 8) |
-                       FLD_VAL(hbp-1, 27, 20);
-
-               timing_v = FLD_VAL(vsw-1, 5, 0) | FLD_VAL(vfp, 15, 8) |
-                       FLD_VAL(vbp, 27, 20);
-       } else {
-               timing_h = FLD_VAL(hsw-1, 7, 0) | FLD_VAL(hfp-1, 19, 8) |
-                       FLD_VAL(hbp-1, 31, 20);
-
-               timing_v = FLD_VAL(vsw-1, 7, 0) | FLD_VAL(vfp, 19, 8) |
-                       FLD_VAL(vbp, 31, 20);
-       }
+       timing_h = FLD_VAL(hsw-1, dispc.feat->sw_start, 0) |
+                       FLD_VAL(hfp-1, dispc.feat->fp_start, 8) |
+                       FLD_VAL(hbp-1, dispc.feat->bp_start, 20);
+       timing_v = FLD_VAL(vsw-1, dispc.feat->sw_start, 0) |
+                       FLD_VAL(vfp, dispc.feat->fp_start, 8) |
+                       FLD_VAL(vbp, dispc.feat->bp_start, 20);
 
        dispc_write_reg(DISPC_TIMING_H(channel), timing_h);
        dispc_write_reg(DISPC_TIMING_V(channel), timing_v);
@@ -2872,6 +3172,23 @@ unsigned long dispc_core_clk_rate(void)
        return fclk / lcd;
 }
 
+static unsigned long dispc_plane_pclk_rate(enum omap_plane plane)
+{
+       enum omap_channel channel = dispc_ovl_get_channel_out(plane);
+
+       return dispc_mgr_pclk_rate(channel);
+}
+
+static unsigned long dispc_plane_lclk_rate(enum omap_plane plane)
+{
+       enum omap_channel channel = dispc_ovl_get_channel_out(plane);
+
+       if (dss_mgr_is_lcd(channel))
+               return dispc_mgr_lclk_rate(channel);
+       else
+               return dispc_fclk_rate();
+
+}
 static void dispc_dump_clocks_channel(struct seq_file *s, enum omap_channel channel)
 {
        int lcd, pcd;
@@ -3492,7 +3809,7 @@ static void dispc_error_worker(struct work_struct *work)
                                        ovl->name);
                        dispc_ovl_enable(ovl->id, false);
                        dispc_mgr_go(ovl->manager->id);
-                       mdelay(50);
+                       msleep(50);
                }
        }
 
@@ -3504,7 +3821,7 @@ static void dispc_error_worker(struct work_struct *work)
                bit = mgr_desc[i].sync_lost_irq;
 
                if (bit & errors) {
-                       struct omap_dss_device *dssdev = mgr->device;
+                       struct omap_dss_device *dssdev = mgr->get_device(mgr);
                        bool enable;
 
                        DSSERR("SYNC_LOST on channel %s, restarting the output "
@@ -3524,7 +3841,7 @@ static void dispc_error_worker(struct work_struct *work)
                        }
 
                        dispc_mgr_go(mgr->id);
-                       mdelay(50);
+                       msleep(50);
 
                        if (enable)
                                dssdev->driver->enable(dssdev);
@@ -3535,9 +3852,13 @@ static void dispc_error_worker(struct work_struct *work)
                DSSERR("OCP_ERR\n");
                for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
                        struct omap_overlay_manager *mgr;
+                       struct omap_dss_device *dssdev;
+
                        mgr = omap_dss_get_overlay_manager(i);
-                       if (mgr->device && mgr->device->driver)
-                               mgr->device->driver->disable(mgr->device);
+                       dssdev = mgr->get_device(mgr);
+
+                       if (dssdev && dssdev->driver)
+                               dssdev->driver->disable(dssdev);
                }
        }
 
@@ -3661,17 +3982,98 @@ static void _omap_dispc_initial_config(void)
        if (dss_has_feature(FEAT_FUNCGATED))
                REG_FLD_MOD(DISPC_CONFIG, 1, 9, 9);
 
-       _dispc_setup_color_conv_coef();
+       dispc_setup_color_conv_coef();
 
        dispc_set_loadmode(OMAP_DSS_LOAD_FRAME_ONLY);
 
-       dispc_read_plane_fifo_sizes();
+       dispc_init_fifos();
 
        dispc_configure_burst_sizes();
 
        dispc_ovl_enable_zorder_planes();
 }
 
+static const struct dispc_features omap24xx_dispc_feats __initconst = {
+       .sw_start               =       5,
+       .fp_start               =       15,
+       .bp_start               =       27,
+       .sw_max                 =       64,
+       .vp_max                 =       255,
+       .hp_max                 =       256,
+       .calc_scaling           =       dispc_ovl_calc_scaling_24xx,
+       .calc_core_clk          =       calc_core_clk_24xx,
+       .num_fifos              =       3,
+};
+
+static const struct dispc_features omap34xx_rev1_0_dispc_feats __initconst = {
+       .sw_start               =       5,
+       .fp_start               =       15,
+       .bp_start               =       27,
+       .sw_max                 =       64,
+       .vp_max                 =       255,
+       .hp_max                 =       256,
+       .calc_scaling           =       dispc_ovl_calc_scaling_34xx,
+       .calc_core_clk          =       calc_core_clk_34xx,
+       .num_fifos              =       3,
+};
+
+static const struct dispc_features omap34xx_rev3_0_dispc_feats __initconst = {
+       .sw_start               =       7,
+       .fp_start               =       19,
+       .bp_start               =       31,
+       .sw_max                 =       256,
+       .vp_max                 =       4095,
+       .hp_max                 =       4096,
+       .calc_scaling           =       dispc_ovl_calc_scaling_34xx,
+       .calc_core_clk          =       calc_core_clk_34xx,
+       .num_fifos              =       3,
+};
+
+static const struct dispc_features omap44xx_dispc_feats __initconst = {
+       .sw_start               =       7,
+       .fp_start               =       19,
+       .bp_start               =       31,
+       .sw_max                 =       256,
+       .vp_max                 =       4095,
+       .hp_max                 =       4096,
+       .calc_scaling           =       dispc_ovl_calc_scaling_44xx,
+       .calc_core_clk          =       calc_core_clk_44xx,
+       .num_fifos              =       5,
+       .gfx_fifo_workaround    =       true,
+};
+
+static int __init dispc_init_features(struct device *dev)
+{
+       const struct dispc_features *src;
+       struct dispc_features *dst;
+
+       dst = devm_kzalloc(dev, sizeof(*dst), GFP_KERNEL);
+       if (!dst) {
+               dev_err(dev, "Failed to allocate DISPC Features\n");
+               return -ENOMEM;
+       }
+
+       if (cpu_is_omap24xx()) {
+               src = &omap24xx_dispc_feats;
+       } else if (cpu_is_omap34xx()) {
+               if (omap_rev() < OMAP3430_REV_ES3_0)
+                       src = &omap34xx_rev1_0_dispc_feats;
+               else
+                       src = &omap34xx_rev3_0_dispc_feats;
+       } else if (cpu_is_omap44xx()) {
+               src = &omap44xx_dispc_feats;
+       } else if (soc_is_omap54xx()) {
+               src = &omap44xx_dispc_feats;
+       } else {
+               return -ENODEV;
+       }
+
+       memcpy(dst, src, sizeof(*dst));
+       dispc.feat = dst;
+
+       return 0;
+}
+
 /* DISPC HW IP initialisation */
 static int __init omap_dispchw_probe(struct platform_device *pdev)
 {
@@ -3682,6 +4084,10 @@ static int __init omap_dispchw_probe(struct platform_device *pdev)
 
        dispc.pdev = pdev;
 
+       r = dispc_init_features(&dispc.pdev->dev);
+       if (r)
+               return r;
+
        spin_lock_init(&dispc.irq_lock);
 
 #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS