video: tegra: add z order blending, output position, and stride to overlays
authorAntti Hatala <ahatala@nvidia.com>
Wed, 8 Sep 2010 23:09:31 +0000 (16:09 -0700)
committerColin Cross <ccross@android.com>
Wed, 6 Oct 2010 23:28:34 +0000 (16:28 -0700)
Change-Id: I7439f60bfa3264bec9b1447fd970eef9e4c089d9
Signed-off-by: Erik Gilling <konkers@android.com>
arch/arm/mach-tegra/include/mach/dc.h
drivers/video/tegra/dc/dc.c
drivers/video/tegra/dc/dc_priv.h
drivers/video/tegra/dc/dc_reg.h
drivers/video/tegra/fb.c

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