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