From 268892cb63a822315921a8dab48ac3e4abf7dd03 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 12 Oct 2017 16:39:20 +0200 Subject: drm/tegra: Sanitize format modifiers The existing format modifier definitions were merged prematurely, and recent work has unveiled that the definitions are suboptimal in several ways: - The format specifiers, except for one, are not Tegra specific, but the names don't reflect that. - The number space is split into two, reserving 32 bits for some "parameter" which most of the modifiers are not going to have. - Symbolic names for the modifiers are not using the standard DRM_FORMAT_MOD_* prefix, which makes them awkward to use. - The vendor prefix NV is somewhat ambiguous. Fortunately, nobody's started using these modifiers, so we can still fix the above issues. Do so by using the standard prefix. Also, remove TEGRA from the name of those modifiers that exist on NVIDIA GPUs as well. In case of the block linear modifiers, make the "parameter" smaller (4 bits, though only 6 values are valid) and don't let that leak into any of the other modifiers. Finally, also use the more canonical NVIDIA instead of the ambiguous NV prefix. Acked-by: Daniel Vetter Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/fb.c | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm/tegra/fb.c') diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index 80540c1c66dc..406e895d82cc 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -54,17 +54,40 @@ int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer, struct tegra_fb *fb = to_tegra_fb(framebuffer); uint64_t modifier = fb->base.modifier; - switch (fourcc_mod_tegra_mod(modifier)) { - case NV_FORMAT_MOD_TEGRA_TILED: + switch (modifier) { + case DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED: tiling->mode = TEGRA_BO_TILING_MODE_TILED; tiling->value = 0; break; - case NV_FORMAT_MOD_TEGRA_16BX2_BLOCK(0): + case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0): tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; - tiling->value = fourcc_mod_tegra_param(modifier); - if (tiling->value > 5) - return -EINVAL; + tiling->value = 0; + break; + + case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1): + tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; + tiling->value = 1; + break; + + case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2): + tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; + tiling->value = 2; + break; + + case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3): + tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; + tiling->value = 3; + break; + + case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4): + tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; + tiling->value = 4; + break; + + case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5): + tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; + tiling->value = 5; break; default: -- cgit v1.2.3 From 71835caa00e8a64ada3c2e30c56468c39c81f60c Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 14 Nov 2017 16:09:30 +0100 Subject: drm/tegra: fb: Force alpha formats Tegra20 and Tegra30 don't actually support the 24-bit RGB formats that don't have an alpha component. In order to allow the fbdev emulation to run on those chips, force the 32-bit RGBA formats. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/fb.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers/gpu/drm/tegra/fb.c') diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index 406e895d82cc..1af4ef9241f1 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -253,6 +253,19 @@ static int tegra_fbdev_probe(struct drm_fb_helper *helper, cmd.height = sizes->surface_height; cmd.pitches[0] = round_up(sizes->surface_width * bytes_per_pixel, tegra->pitch_align); + + /* + * Early generations of Tegra (Tegra20 and Tegra30) do not support any + * of the X* or *X formats, only their A* or *A equivalents. Force the + * legacy framebuffer format to include an alpha component so that the + * framebuffer emulation can be supported on all generations. + */ + if (sizes->surface_bpp == 32 && sizes->surface_depth == 24) + sizes->surface_depth = 32; + + if (sizes->surface_bpp == 16 && sizes->surface_depth == 15) + sizes->surface_depth = 16; + cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth); -- cgit v1.2.3 From ebae8d07435ae91314f4a28d69b530d09c625815 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 20 Dec 2017 09:39:14 +0100 Subject: drm/tegra: dc: Implement legacy blending This implements alpha blending on legacy display controllers (Tegra20, Tegra30 and Tegra114). While it's theoretically possible to support the zpos property to enable userspace to specify the Z-order of each plane individually, this is not currently supported and the same fixed Z- order as previously defined is used. Reverts commit 71835caa00e8 ("drm/tegra: fb: Force alpha formats") since the opaque formats are now supported. Reported-by: Dmitry Osipenko Fixes: 7772fdaef939 ("drm/tegra: Support ARGB and ABGR formats") Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 81 +++++++++++++++++++++---- drivers/gpu/drm/tegra/dc.h | 12 ++++ drivers/gpu/drm/tegra/fb.c | 12 ---- drivers/gpu/drm/tegra/plane.c | 138 ++++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/tegra/plane.h | 8 +++ 5 files changed, 226 insertions(+), 25 deletions(-) (limited to 'drivers/gpu/drm/tegra/fb.c') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 2a0c1e93f82e..4507063029e0 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -154,30 +154,53 @@ static inline u32 compute_initial_dda(unsigned int in) static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane) { + u32 background[3] = { + BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE, + BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE, + BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE, + }; + u32 foreground = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255) | + BLEND_COLOR_KEY_NONE; + u32 blendnokey = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255); + struct tegra_plane_state *state; + unsigned int i; + + state = to_tegra_plane_state(plane->base.state); + + /* alpha contribution is 1 minus sum of overlapping windows */ + for (i = 0; i < 3; i++) { + if (state->dependent[i]) + background[i] |= BLEND_CONTROL_DEPENDENT; + } + + /* enable alpha blending if pixel format has an alpha component */ + if (!state->opaque) + foreground |= BLEND_CONTROL_ALPHA; + /* * Disable blending and assume Window A is the bottom-most window, * Window C is the top-most window and Window B is in the middle. */ - tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_NOKEY); - tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_1WIN); + tegra_plane_writel(plane, blendnokey, DC_WIN_BLEND_NOKEY); + tegra_plane_writel(plane, foreground, DC_WIN_BLEND_1WIN); switch (plane->index) { case 0: - tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_X); - tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_Y); - tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_3WIN_XY); + tegra_plane_writel(plane, background[0], DC_WIN_BLEND_2WIN_X); + tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y); + tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY); break; case 1: - tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_X); - tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_Y); - tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_3WIN_XY); + tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_X); + tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y); + tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY); break; case 2: - tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_X); - tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_Y); - tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_3WIN_XY); + tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_X); + tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_Y); + tegra_plane_writel(plane, foreground, DC_WIN_BLEND_3WIN_XY); break; } } @@ -353,6 +376,11 @@ static const u32 tegra20_primary_formats[] = { DRM_FORMAT_RGBA5551, DRM_FORMAT_ABGR8888, DRM_FORMAT_ARGB8888, + /* non-native formats */ + DRM_FORMAT_XRGB1555, + DRM_FORMAT_RGBX5551, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_XRGB8888, }; static const u32 tegra114_primary_formats[] = { @@ -409,18 +437,40 @@ static int tegra_plane_atomic_check(struct drm_plane *plane, struct tegra_bo_tiling *tiling = &plane_state->tiling; struct tegra_plane *tegra = to_tegra_plane(plane); struct tegra_dc *dc = to_tegra_dc(state->crtc); + unsigned int format; int err; /* no need for further checks if the plane is being disabled */ if (!state->crtc) return 0; - err = tegra_plane_format(state->fb->format->format, - &plane_state->format, + err = tegra_plane_format(state->fb->format->format, &format, &plane_state->swap); if (err < 0) return err; + /* + * Tegra20 and Tegra30 are special cases here because they support + * only variants of specific formats with an alpha component, but not + * the corresponding opaque formats. However, the opaque formats can + * be emulated by disabling alpha blending for the plane. + */ + if (!dc->soc->supports_blending) { + if (!tegra_plane_format_has_alpha(format)) { + err = tegra_plane_format_get_alpha(format, &format); + if (err < 0) + return err; + + plane_state->opaque = true; + } else { + plane_state->opaque = false; + } + + tegra_plane_check_dependent(tegra, plane_state); + } + + plane_state->format = format; + err = tegra_fb_get_tiling(state->fb, tiling); if (err < 0) return err; @@ -737,6 +787,11 @@ static const u32 tegra20_overlay_formats[] = { DRM_FORMAT_RGBA5551, DRM_FORMAT_ABGR8888, DRM_FORMAT_ARGB8888, + /* non-native formats */ + DRM_FORMAT_XRGB1555, + DRM_FORMAT_RGBX5551, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_XRGB8888, /* planar formats */ DRM_FORMAT_UYVY, DRM_FORMAT_YUYV, diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index e2831e96ea96..096a81ad6d8d 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -649,8 +649,20 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define DC_WIN_DV_CONTROL 0x70e #define DC_WIN_BLEND_NOKEY 0x70f +#define BLEND_WEIGHT1(x) (((x) & 0xff) << 16) +#define BLEND_WEIGHT0(x) (((x) & 0xff) << 8) + #define DC_WIN_BLEND_1WIN 0x710 +#define BLEND_CONTROL_FIX (0 << 2) +#define BLEND_CONTROL_ALPHA (1 << 2) +#define BLEND_COLOR_KEY_NONE (0 << 0) +#define BLEND_COLOR_KEY_0 (1 << 0) +#define BLEND_COLOR_KEY_1 (2 << 0) +#define BLEND_COLOR_KEY_BOTH (3 << 0) + #define DC_WIN_BLEND_2WIN_X 0x711 +#define BLEND_CONTROL_DEPENDENT (2 << 2) + #define DC_WIN_BLEND_2WIN_Y 0x712 #define DC_WIN_BLEND_3WIN_XY 0x713 diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index 1af4ef9241f1..e05fde7172f8 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -254,18 +254,6 @@ static int tegra_fbdev_probe(struct drm_fb_helper *helper, cmd.pitches[0] = round_up(sizes->surface_width * bytes_per_pixel, tegra->pitch_align); - /* - * Early generations of Tegra (Tegra20 and Tegra30) do not support any - * of the X* or *X formats, only their A* or *A equivalents. Force the - * legacy framebuffer format to include an alpha component so that the - * framebuffer emulation can be supported on all generations. - */ - if (sizes->surface_bpp == 32 && sizes->surface_depth == 24) - sizes->surface_depth = 32; - - if (sizes->surface_bpp == 16 && sizes->surface_depth == 15) - sizes->surface_depth = 16; - cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth); diff --git a/drivers/gpu/drm/tegra/plane.c b/drivers/gpu/drm/tegra/plane.c index 9146aead973b..154b4d337d0a 100644 --- a/drivers/gpu/drm/tegra/plane.c +++ b/drivers/gpu/drm/tegra/plane.c @@ -43,6 +43,7 @@ tegra_plane_atomic_duplicate_state(struct drm_plane *plane) { struct tegra_plane_state *state = to_tegra_plane_state(plane->state); struct tegra_plane_state *copy; + unsigned int i; copy = kmalloc(sizeof(*copy), GFP_KERNEL); if (!copy) @@ -52,6 +53,10 @@ tegra_plane_atomic_duplicate_state(struct drm_plane *plane) copy->tiling = state->tiling; copy->format = state->format; copy->swap = state->swap; + copy->opaque = state->opaque; + + for (i = 0; i < 3; i++) + copy->dependent[i] = state->dependent[i]; return ©->base; } @@ -238,3 +243,136 @@ bool tegra_plane_format_is_yuv(unsigned int format, bool *planar) return false; } + +static bool __drm_format_has_alpha(u32 format) +{ + switch (format) { + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_RGBA5551: + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_ARGB8888: + return true; + } + + return false; +} + +/* + * This is applicable to Tegra20 and Tegra30 only where the opaque formats can + * be emulated using the alpha formats and alpha blending disabled. + */ +bool tegra_plane_format_has_alpha(unsigned int format) +{ + switch (format) { + case WIN_COLOR_DEPTH_B5G5R5A1: + case WIN_COLOR_DEPTH_A1B5G5R5: + case WIN_COLOR_DEPTH_R8G8B8A8: + case WIN_COLOR_DEPTH_B8G8R8A8: + return true; + } + + return false; +} + +int tegra_plane_format_get_alpha(unsigned int opaque, unsigned int *alpha) +{ + switch (opaque) { + case WIN_COLOR_DEPTH_B5G5R5X1: + *alpha = WIN_COLOR_DEPTH_B5G5R5A1; + return 0; + + case WIN_COLOR_DEPTH_X1B5G5R5: + *alpha = WIN_COLOR_DEPTH_A1B5G5R5; + return 0; + + case WIN_COLOR_DEPTH_R8G8B8X8: + *alpha = WIN_COLOR_DEPTH_R8G8B8A8; + return 0; + + case WIN_COLOR_DEPTH_B8G8R8X8: + *alpha = WIN_COLOR_DEPTH_B8G8R8A8; + return 0; + } + + return -EINVAL; +} + +unsigned int tegra_plane_get_overlap_index(struct tegra_plane *plane, + struct tegra_plane *other) +{ + unsigned int index = 0, i; + + WARN_ON(plane == other); + + for (i = 0; i < 3; i++) { + if (i == plane->index) + continue; + + if (i == other->index) + break; + + index++; + } + + return index; +} + +void tegra_plane_check_dependent(struct tegra_plane *tegra, + struct tegra_plane_state *state) +{ + struct drm_plane_state *old, *new; + struct drm_plane *plane; + unsigned int zpos[2]; + unsigned int i; + + for (i = 0; i < 3; i++) + state->dependent[i] = false; + + for (i = 0; i < 2; i++) + zpos[i] = 0; + + for_each_oldnew_plane_in_state(state->base.state, plane, old, new, i) { + struct tegra_plane *p = to_tegra_plane(plane); + unsigned index; + + /* skip this plane and planes on different CRTCs */ + if (p == tegra || new->crtc != state->base.crtc) + continue; + + index = tegra_plane_get_overlap_index(tegra, p); + + /* + * If any of the other planes is on top of this plane and uses + * a format with an alpha component, mark this plane as being + * dependent, meaning it's alpha value will be 1 minus the sum + * of alpha components of the overlapping planes. + */ + if (p->index > tegra->index) { + if (__drm_format_has_alpha(new->fb->format->format)) + state->dependent[index] = true; + + /* keep track of the Z position */ + zpos[index] = p->index; + } + } + + /* + * The region where three windows overlap is the intersection of the + * two regions where two windows overlap. It contributes to the area + * if any of the windows on top of it have an alpha component. + */ + for (i = 0; i < 2; i++) + state->dependent[2] = state->dependent[2] || + state->dependent[i]; + + /* + * However, if any of the windows on top of this window is opaque, it + * will completely conceal this window within that area, so avoid the + * window from contributing to the area. + */ + for (i = 0; i < 2; i++) { + if (zpos[i] > tegra->index) + state->dependent[2] = state->dependent[2] && + state->dependent[i]; + } +} diff --git a/drivers/gpu/drm/tegra/plane.h b/drivers/gpu/drm/tegra/plane.h index dca66cb95d25..6938719e7e5d 100644 --- a/drivers/gpu/drm/tegra/plane.h +++ b/drivers/gpu/drm/tegra/plane.h @@ -40,6 +40,10 @@ struct tegra_plane_state { struct tegra_bo_tiling tiling; u32 format; u32 swap; + + /* used for legacy blending support only */ + bool opaque; + bool dependent[3]; }; static inline struct tegra_plane_state * @@ -58,5 +62,9 @@ int tegra_plane_state_add(struct tegra_plane *plane, int tegra_plane_format(u32 fourcc, u32 *format, u32 *swap); bool tegra_plane_format_is_yuv(unsigned int format, bool *planar); +bool tegra_plane_format_has_alpha(unsigned int format); +int tegra_plane_format_get_alpha(unsigned int opaque, unsigned int *alpha); +void tegra_plane_check_dependent(struct tegra_plane *tegra, + struct tegra_plane_state *state); #endif /* TEGRA_PLANE_H */ -- cgit v1.2.3