summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Morell <rmorell@nvidia.com>2011-03-17 17:56:49 -0700
committerVarun Colbert <vcolbert@nvidia.com>2011-08-11 11:53:47 -0700
commitd8dc0aa6548723d8b0637ab131e0732e2e7e7296 (patch)
tree06994e82d6737e9f3276bb2278ee4f8ff2ea7611
parenteef15b664f09304530f8692fa75f5ce225937db3 (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.h9
-rw-r--r--drivers/video/tegra/dc/dc.c88
-rw-r--r--drivers/video/tegra/dc/ext/dev.c8
-rw-r--r--drivers/video/tegra/dc/overlay.c17
-rw-r--r--drivers/video/tegra/fb.c12
-rw-r--r--include/video/tegra_dc_ext.h4
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;