summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-tegra/include/mach/dc.h25
-rw-r--r--drivers/video/tegra/dc/dc.c156
-rw-r--r--drivers/video/tegra/dc/dc_priv.h6
-rw-r--r--drivers/video/tegra/dc/dc_reg.h5
-rw-r--r--drivers/video/tegra/fb.c9
5 files changed, 132 insertions, 69 deletions
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);