diff options
author | Robert Morell <rmorell@nvidia.com> | 2011-03-17 17:56:49 -0700 |
---|---|---|
committer | Varun Colbert <vcolbert@nvidia.com> | 2011-08-11 11:53:47 -0700 |
commit | d8dc0aa6548723d8b0637ab131e0732e2e7e7296 (patch) | |
tree | 06994e82d6737e9f3276bb2278ee4f8ff2ea7611 | |
parent | eef15b664f09304530f8692fa75f5ce225937db3 (diff) |
video: tegra: Allow fractional input rects
This change makes the input rect for Tegra windows be a 20.12
fixed-point number instead of an integer. This allows software to
specify sub-pixel precision.
bug 818525
Change-Id: I130f63b68159ed896d1113ea537307997875ca40
Signed-off-by: Robert Morell <rmorell@nvidia.com>
Reviewed-on: http://git-master/r/40526
Reviewed-by: Varun Colbert <vcolbert@nvidia.com>
Tested-by: Varun Colbert <vcolbert@nvidia.com>
-rw-r--r-- | arch/arm/mach-tegra/include/mach/dc.h | 9 | ||||
-rw-r--r-- | drivers/video/tegra/dc/dc.c | 88 | ||||
-rw-r--r-- | drivers/video/tegra/dc/ext/dev.c | 8 | ||||
-rw-r--r-- | drivers/video/tegra/dc/overlay.c | 17 | ||||
-rw-r--r-- | drivers/video/tegra/fb.c | 12 | ||||
-rw-r--r-- | include/video/tegra_dc_ext.h | 4 |
6 files changed, 101 insertions, 37 deletions
diff --git a/arch/arm/mach-tegra/include/mach/dc.h b/arch/arm/mach-tegra/include/mach/dc.h index 848711feccbb..f9378a46be42 100644 --- a/arch/arm/mach-tegra/include/mach/dc.h +++ b/arch/arm/mach-tegra/include/mach/dc.h @@ -24,6 +24,7 @@ #include <linux/pm.h> #include <linux/types.h> +#include <drm/drm_fixed.h> #define TEGRA_MAX_DC 2 #define DC_N_WINDOWS 3 @@ -360,10 +361,10 @@ struct tegra_dc_win { unsigned offset_v; unsigned stride; unsigned stride_uv; - unsigned x; - unsigned y; - unsigned w; - unsigned h; + fixed20_12 x; + fixed20_12 y; + fixed20_12 w; + fixed20_12 h; unsigned out_x; unsigned out_y; unsigned out_w; diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c index 6d2c268d9647..cc47993e1b35 100644 --- a/drivers/video/tegra/dc/dc.c +++ b/drivers/video/tegra/dc/dc.c @@ -34,6 +34,7 @@ #include <linux/seq_file.h> #include <linux/backlight.h> #include <linux/switch.h> +#include <drm/drm_fixed.h> #include <mach/clk.h> #include <mach/dc.h> @@ -83,12 +84,12 @@ static const struct { static inline bool win_use_v_filter(const struct tegra_dc_win *win) { return can_filter[win->idx].v && - win->h != win->out_h; + win->h.full != dfixed_const(win->out_h); } static inline bool win_use_h_filter(const struct tegra_dc_win *win) { return can_filter[win->idx].h && - win->w != win->out_w; + win->w.full != dfixed_const(win->out_w); } static inline int tegra_dc_fmt_bpp(int fmt) @@ -776,7 +777,8 @@ static unsigned long tegra_dc_calc_win_bandwidth(struct tegra_dc *dc, if (!WIN_IS_ENABLED(w)) return 0; - if (w->w == 0 || w->h == 0 || w->out_w == 0 || w->out_h == 0) + if (dfixed_trunc(w->w) == 0 || dfixed_trunc(w->h) == 0 || + w->out_w == 0 || w->out_h == 0) return 0; tiled_windows_bw_multiplier = @@ -791,7 +793,7 @@ static unsigned long tegra_dc_calc_win_bandwidth(struct tegra_dc *dc, * to prevent overflow of long. */ ret = (unsigned long)(dc->pixel_clk >> 16) * bpp / 8 * - (win_use_v_filter(w) ? 2 : 1) * w->w / w->out_w * + (win_use_v_filter(w) ? 2 : 1) * dfixed_trunc(w->w) / w->out_w * (WIN_IS_TILED(w) ? tiled_windows_bw_multiplier : 1); /* @@ -864,6 +866,53 @@ static int tegra_dc_set_dynamic_emc(struct tegra_dc_win *windows[], int n) return 0; } +static inline u32 compute_dda_inc(fixed20_12 in, unsigned out_int, + bool v, unsigned Bpp) +{ + /* + * min(round((prescaled_size_in_pixels - 1) * 0x1000 / + * (post_scaled_size_in_pixels - 1)), MAX) + * Where the value of MAX is as follows: + * For V_DDA_INCREMENT: 15.0 (0xF000) + * For H_DDA_INCREMENT: 4.0 (0x4000) for 4 Bytes/pix formats. + * 8.0 (0x8000) for 2 Bytes/pix formats. + */ + + fixed20_12 out = dfixed_init(out_int); + u32 dda_inc; + int max; + + if (v) { + max = 15; + } else { + switch (Bpp) { + default: + WARN_ON_ONCE(1); + /* fallthrough */ + case 4: + max = 4; + break; + case 2: + max = 8; + break; + } + } + + out.full = max_t(u32, out.full - dfixed_const(1), dfixed_const(1)); + in.full -= dfixed_const(1); + + dda_inc = dfixed_div(in, out); + + dda_inc = min_t(u32, dda_inc, dfixed_const(max)); + + return dda_inc; +} + +static inline u32 compute_initial_dda(fixed20_12 in) +{ + return dfixed_frac(in); +} + /* does not support updating windows on multiple dcs in one call */ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n) { @@ -891,11 +940,13 @@ 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 h_offset; - unsigned v_offset; + fixed20_12 h_offset, v_offset; bool invert_h = (win->flags & TEGRA_WIN_FLAG_INVERT_H) != 0; bool invert_v = (win->flags & TEGRA_WIN_FLAG_INVERT_V) != 0; bool yuvp = tegra_dc_is_yuv_planar(win->fmt); + unsigned Bpp = tegra_dc_fmt_bpp(win->fmt) / 8; + /* Bytes per pixel of bandwidth, used for dda_inc calculation */ + unsigned Bpp_bw = Bpp * (yuvp ? 2 : 1); const bool filter_h = win_use_h_filter(win); const bool filter_v = win_use_v_filter(win); @@ -931,16 +982,18 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n) V_SIZE(win->out_h) | H_SIZE(win->out_w), DC_WIN_SIZE); tegra_dc_writel(dc, - V_PRESCALED_SIZE(win->h) | - H_PRESCALED_SIZE(win->w * tegra_dc_fmt_bpp(win->fmt) / 8), + V_PRESCALED_SIZE(dfixed_trunc(win->h)) | + H_PRESCALED_SIZE(dfixed_trunc(win->w) * Bpp), DC_WIN_PRESCALED_SIZE); - h_dda = ((win->w - 1) * 0x1000) / max_t(int, win->out_w - 1, 1); - v_dda = ((win->h - 1) * 0x1000) / max_t(int, win->out_h - 1, 1); + h_dda = compute_dda_inc(win->w, win->out_w, false, Bpp_bw); + v_dda = compute_dda_inc(win->h, win->out_h, true, Bpp_bw); tegra_dc_writel(dc, V_DDA_INC(v_dda) | H_DDA_INC(h_dda), DC_WIN_DDA_INCREMENT); - tegra_dc_writel(dc, 0, DC_WIN_H_INITIAL_DDA); - tegra_dc_writel(dc, 0, DC_WIN_V_INITIAL_DDA); + h_dda = compute_initial_dda(win->x); + v_dda = compute_initial_dda(win->y); + tegra_dc_writel(dc, h_dda, DC_WIN_H_INITIAL_DDA); + tegra_dc_writel(dc, v_dda, DC_WIN_V_INITIAL_DDA); tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE); tegra_dc_writel(dc, 0, DC_WIN_UV_BUF_STRIDE); @@ -968,17 +1021,18 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n) h_offset = win->x; if (invert_h) { - h_offset += win->w - 1; + h_offset.full += win->w.full - dfixed_const(1); } - h_offset *= tegra_dc_fmt_bpp(win->fmt) / 8; v_offset = win->y; if (invert_v) { - v_offset += win->h - 1; + v_offset.full += win->h.full - dfixed_const(1); } - tegra_dc_writel(dc, h_offset, DC_WINBUF_ADDR_H_OFFSET); - tegra_dc_writel(dc, v_offset, DC_WINBUF_ADDR_V_OFFSET); + tegra_dc_writel(dc, dfixed_trunc(h_offset) * Bpp, + DC_WINBUF_ADDR_H_OFFSET); + tegra_dc_writel(dc, dfixed_trunc(v_offset), + DC_WINBUF_ADDR_V_OFFSET); if (WIN_IS_TILED(win)) tegra_dc_writel(dc, diff --git a/drivers/video/tegra/dc/ext/dev.c b/drivers/video/tegra/dc/ext/dev.c index 05569ea74d0a..788593ea0dcf 100644 --- a/drivers/video/tegra/dc/ext/dev.c +++ b/drivers/video/tegra/dc/ext/dev.c @@ -189,10 +189,10 @@ static int tegra_dc_ext_set_windowattr(struct tegra_dc_ext *ext, else if (flip_win->attr.blend == TEGRA_DC_EXT_BLEND_COVERAGE) win->flags |= TEGRA_WIN_FLAG_BLEND_COVERAGE; win->fmt = flip_win->attr.pixformat; - win->x = flip_win->attr.x; - win->y = flip_win->attr.y; - win->w = flip_win->attr.w; - win->h = flip_win->attr.h; + win->x.full = flip_win->attr.x; + win->y.full = flip_win->attr.y; + win->w.full = flip_win->attr.w; + win->h.full = flip_win->attr.h; /* XXX verify that this doesn't go outside display's active region */ win->out_x = flip_win->attr.out_x; win->out_y = flip_win->attr.out_y; diff --git a/drivers/video/tegra/dc/overlay.c b/drivers/video/tegra/dc/overlay.c index 3444b2f9fa34..6f42242a7102 100644 --- a/drivers/video/tegra/dc/overlay.c +++ b/drivers/video/tegra/dc/overlay.c @@ -27,6 +27,7 @@ #include <linux/spinlock.h> #include <linux/tegra_overlay.h> #include <linux/uaccess.h> +#include <drm/drm_fixed.h> #include <asm/atomic.h> @@ -159,10 +160,10 @@ static int tegra_overlay_set_windowattr(struct tegra_overlay_info *overlay, win->flags |= TEGRA_WIN_FLAG_TILED; win->fmt = flip_win->attr.pixformat; - win->x = flip_win->attr.x; - win->y = flip_win->attr.y; - win->w = flip_win->attr.w; - win->h = flip_win->attr.h; + win->x.full = dfixed_const(flip_win->attr.x); + win->y.full = dfixed_const(flip_win->attr.y); + win->w.full = dfixed_const(flip_win->attr.w); + win->h.full = dfixed_const(flip_win->attr.h); win->out_x = flip_win->attr.out_x; win->out_y = flip_win->attr.out_y; win->out_w = flip_win->attr.out_w; @@ -183,12 +184,16 @@ static int tegra_overlay_set_windowattr(struct tegra_overlay_info *overlay, if (((win->out_x + win->out_w) > xres) && (win->out_x < xres)) { long new_w = xres - win->out_x; - win->w = win->w * new_w / win->out_w; + u64 in_w = win->w.full * new_w; + do_div(in_w, win->out_w); + win->w.full = lower_32_bits(in_w); win->out_w = new_w; } if (((win->out_y + win->out_h) > yres) && (win->out_y < yres)) { long new_h = yres - win->out_y; - win->h = win->h * new_h / win->out_h; + u64 in_h = win->h.full * new_h; + do_div(in_h, win->out_h); + win->h.full = lower_32_bits(in_h); win->out_h = new_h; } diff --git a/drivers/video/tegra/fb.c b/drivers/video/tegra/fb.c index a3d5c26289dd..6aff1cab30d4 100644 --- a/drivers/video/tegra/fb.c +++ b/drivers/video/tegra/fb.c @@ -138,8 +138,8 @@ static int tegra_fb_set_par(struct fb_info *info) tegra_dc_set_fb_mode(tegra_fb->win->dc, info->mode, stereo); - tegra_fb->win->w = info->mode->xres; - tegra_fb->win->h = info->mode->yres; + tegra_fb->win->w.full = dfixed_const(info->mode->xres); + tegra_fb->win->h.full = dfixed_const(info->mode->yres); tegra_fb->win->out_w = info->mode->xres; tegra_fb->win->out_h = info->mode->yres; } @@ -446,10 +446,10 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev, info->var.vsync_len = 0; info->var.vmode = FB_VMODE_NONINTERLACED; - win->x = 0; - win->y = 0; - win->w = fb_data->xres; - win->h = fb_data->yres; + win->x.full = dfixed_const(0); + win->y.full = dfixed_const(0); + win->w.full = dfixed_const(fb_data->xres); + win->h.full = dfixed_const(fb_data->yres); /* TODO: set to output res dc */ win->out_x = 0; win->out_y = 0; diff --git a/include/video/tegra_dc_ext.h b/include/video/tegra_dc_ext.h index 6f43ee689b26..2e89a12add3e 100644 --- a/include/video/tegra_dc_ext.h +++ b/include/video/tegra_dc_ext.h @@ -65,6 +65,10 @@ struct tegra_dc_ext_flip_windowattr { __u32 stride; __u32 stride_uv; __u32 pixformat; + /* + * x, y, w, h are fixed-point: 20 bits of integer (MSB) and 12 bits of + * fractional (LSB) + */ __u32 x; __u32 y; __u32 w; |