drm/tegra: dc: Implement legacy blending
PATCH v2 1/1 drm/tegra: dc: Implement legacy blending
From: Thierry Reding <[email protected]>
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.
---
Changes in v2:
- properly implement blending if windows have different pixel formats
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(-)
drivers/gpu/drm/tegra/dc.c +68 -13
@@ -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,
drivers/gpu/drm/tegra/dc.h +12 -0
@@ -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
drivers/gpu/drm/tegra/fb.c +0 -12
@@ -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);
drivers/gpu/drm/tegra/plane.c +138 -0
153 lines not on this page · raw message
drivers/gpu/drm/tegra/plane.h +8 -0
@@ -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 */
Signature · 1 line
2.15.1
A message that is not in the archive (sent off-list or not yet received): 20171221140317.18447-1-thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
Quoted 234 lines · Thierry Reding
From: Thierry Reding <[email protected]>
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 <[email protected]>Fixes 7772fdaef939 ("drm/tegra: Support ARGB and ABGR formats")Signed-off-by Thierry Reding <[email protected]>---
Changes in v2:
- properly implement blending if windows have different pixel formatsdrivers/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(-)drivers/gpu/drm/tegra/dc.c +68
-13@@ -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,drivers/gpu/drm/tegra/dc.h +12
-0@@ -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 0x713drivers/gpu/drm/tegra/fb.c +0
-12@@ -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);drivers/gpu/drm/tegra/plane.c +5
-0@@ -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)
drivers/gpu/drm/tegra/plane.c bool tegra_plane_format_is_yuv(unsigned int format, bool *planar) · line 253
Quoted 10 lines
drivers/gpu/drm/tegra/plane.c +8
-0return 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 :
DRM_FORMAT_ARGB4444 missing here?
Quoted 7 lines
drivers/gpu/drm/tegra/plane.c +7
-0+return true; + } + +return false; +} + +/*
drivers/gpu/drm/tegra/plane.c bool tegra_plane_format_is_yuv(unsigned int format, bool *planar) · line 270
Quoted 10 lines
drivers/gpu/drm/tegra/plane.c +10
-0+* 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 :
WIN_COLOR_DEPTH_B4G4R4A4 missing here?
Quoted 54 lines
drivers/gpu/drm/tegra/plane.c +54
-0+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;
drivers/gpu/drm/tegra/plane.c bool tegra_plane_format_is_yuv(unsigned int format, bool *planar) · line 334
Quoted 10 lines
drivers/gpu/drm/tegra/plane.c +10
-0+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) {
Somehow this works when 2 windows are blended (primary plane + cursor). But unfortunately this doesn't work at all in a case when 3 windows blended (primary
+ video overlay + cursor), for some reason there is only one plane in the state here, so blending dependency tracking doesn't work at all. I'll continue to look into it, but for now I don't know why it doesn't work as expected. If you have any idea, please tell.
Quoted 34 lines
drivers/gpu/drm/tegra/plane.c +34
-0+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]; + +/*
drivers/gpu/drm/tegra/plane.c bool tegra_plane_format_is_yuv(unsigned int format, bool *planar) · line 378
Quoted 10 lines
drivers/gpu/drm/tegra/plane.c +10
-0+* 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]; + } +}
The above is a bit difficult to follow, could we do something like this?
-unsigned int tegra_plane_get_overlap_index(struct tegra_plane *plane,
- struct tegra_plane *other)
+static unsigned int tegra_plane_get_dependent_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;
+ switch (plane->index) {
+ case 0:
+ return other->index - 1;
- if (i == other->index)
- break;
+ case 1:
+ return other->index ? 1 : 0;
- index++;
+ case 2:
+ return other->index;
}- return index;
+ return 0;
}
void tegra_plane_check_dependent(struct tegra_plane *tegra,
@@ -322,14 +323,13 @@ void tegra_plane_check_dependent(struct tegra_plane *tegra,
{
struct drm_plane_state *old, *new;
struct drm_plane *plane;
- unsigned int zpos[2];
+ bool opaque[3];
unsigned int i;- for (i = 0; i < 3; i++)
+ for (i = 0; i < 3; i++) {
state->dependent[i] = false;-
- for (i = 0; i < 2; i++)
- zpos[i] = 0;
+ opaque[i] = false;
+ }
for_each_oldnew_plane_in_state(state->base.state, plane, old, new, i) {
struct tegra_plane *p = to_tegra_plane(plane);
@@ -339,7 +339,7 @@ void tegra_plane_check_dependent(struct tegra_plane *tegra,
if (p == tegra || new->crtc != state->base.crtc)
continue;- index = tegra_plane_get_overlap_index(tegra, p);
+ index = tegra_plane_get_dependent_index(tegra, p);
/*
* If any of the other planes is on top of this plane and uses
@@ -350,9 +350,8 @@ void tegra_plane_check_dependent(struct tegra_plane *tegra,
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;
+ else
+ opaque[index] = true;
}
}@@ -361,18 +360,16 @@ void tegra_plane_check_dependent(struct tegra_plane *tegra,
* 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];
+ state->dependent[2] = state->dependent[0] || state->dependent[1];
/*
* 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];
- }
+ if (tegra->index == 0 && (opaque[0] || opaque[1]))
+ state->dependent[2] = false;
+
+ if (tegra->index == 1 && opaque[1])
+ state->dependent[2] = false;
}
[snip]
A message that is not in the archive (sent off-list or not yet received): 3ccf0299-95d9-0c46-dd86-de16434b6e42-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
Quoted 16 lines · Dmitry Osipenko
Quoted 15 lines · Thierry Reding
From: Thierry Reding <[email protected]>
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 <[email protected]>Fixes 7772fdaef939 ("drm/tegra: Support ARGB and ABGR formats")Signed-off-by Thierry Reding <[email protected]>---
[snip]
Quoted 15 lines
Quoted 15 lines
drivers/gpu/drm/tegra/plane.c +15
-0+ +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 ; +
Quoted 8 lines
drivers/gpu/drm/tegra/plane.c bool tegra_plane_format_is_yuv(unsigned int format, bool *planar) · line 334
Quoted 1 line
drivers/gpu/drm/tegra/plane.c +1
-0+for_each_oldnew_plane_in_state (state->base.state, plane, old, new, i) {Somehow this works when 2 windows are blended (primary plane + cursor). But unfortunately this doesn't work at all in a case when 3 windows blended (primary
+ video overlay + cursor), for some reason there is only one plane in the state here, so blending dependency tracking doesn't work at all. I'll continue to look into it, but for now I don't know why it doesn't work as expected. If you have any idea, please tell.
Actually, I think this code only works when all planes are updated in a single commit. We need to handle cases when only some of the active planes are adjusting the state.
[snip]
A message that is not in the archive (sent off-list or not yet received): cffaa2f8-42f4-557f-1824-a42fdca54aaa-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
Quoted 44 lines · Dmitry Osipenko
Quoted 16 lines · Dmitry Osipenko
Quoted 15 lines · Thierry Reding
From: Thierry Reding <[email protected]>
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 <[email protected]>Fixes 7772fdaef939 ("drm/tegra: Support ARGB and ABGR formats")Signed-off-by Thierry Reding <[email protected]>---
[snip]
Quoted 15 lines
Quoted 15 lines
(quoted diff) +15
-0+ +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; +Quoted 8 lines
Review
Quoted 1 line
(quoted diff) +1
-0+ for_each_oldnew_plane_in_state(state->base.state, plane, old, new, i) {Somehow this works when 2 windows are blended (primary plane + cursor). But unfortunately this doesn't work at all in a case when 3 windows blended (primary
+ video overlay + cursor), for some reason there is only one plane in the state here, so blending dependency tracking doesn't work at all. I'll continue to look into it, but for now I don't know why it doesn't work as expected. If you have any idea, please tell.
Quoted 6 lines
Actually, I think this code only works when all planes are updated in a single commit. We need to handle cases when only some of the active planes are adjusting the state.
[snip]
I've moved blending state to CRTC and now seems it is really working correctly. I tried to run kms-planes-blend on my Intel desktop, but it crashes my machine and so can't compare the result of the test, it looks correct though. Video overlay + mouse cursor now working fine.
I've amended your patch, see it below.
---------------------------------------
Quoted 1 line
From
e5264125fb81757999f6b11fec0e7cfffb2ca6d6Mon Sep 17 00:00:00 2001
From: Thierry Reding <[email protected]>
Date: Wed, 20 Dec 2017 09:39:14 +0100
Subject: [PATCH] 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.
--- drivers/gpu/drm/tegra/dc.c | 90 ++++++++++++++++++-------- drivers/gpu/drm/tegra/dc.h | 21 ++++++ drivers/gpu/drm/tegra/fb.c | 12 ---- drivers/gpu/drm/tegra/plane.c | 145 ++++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/tegra/plane.h | 4 ++ 5 files changed, 233 insertions(+), 39 deletions(-)
drivers/gpu/drm/tegra/dc.c +63 -27
@@ -154,32 +154,12 @@ static inline u32 compute_initial_dda(unsigned int in)static void tegra_plane_setup_blending_legacy (struct tegra_plane *plane) { -/* -* 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 ); - -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 ); -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 ); -break ; +u32 foreground =BLEND_WEIGHT1 (255 ) |BLEND_WEIGHT0 (255 ) | +BLEND_COLOR_KEY_NONE ; +u32 blendnokey =BLEND_WEIGHT1 (255 ) |BLEND_WEIGHT0 (255 ); -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 ); -break ; - } +tegra_plane_writel (plane, blendnokey,DC_WIN_BLEND_NOKEY ); +tegra_plane_writel (plane, foreground,DC_WIN_BLEND_1WIN ); } static void tegra_plane_setup_blending(struct tegra_plane *plane,
@@ -353,6 +333,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 +394,38 @@ 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; + } + + err =tegra_plane_update_blending_state (tegra, plane_state); +if (err <0 ) +return err; + } + + plane_state->format = format; + err =tegra_fb_get_tiling (state->fb, tiling);if (err <0 )return err;
@@ -737,6 +742,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,
@@ -924,6 +934,7 @@ tegra_crtc_atomic_duplicate_state(struct drm_crtc *crtc) {struct tegra_dc_state *state =to_dc_state (crtc->state);struct tegra_dc_state *copy; +unsigned int i; copy =kmalloc (sizeof (*copy),GFP_KERNEL );if (!copy)
@@ -935,6 +946,9 @@ tegra_crtc_atomic_duplicate_state(struct drm_crtc *crtc) copy->div = state->div; copy->planes = state->planes; +for (i =0 ; i <3 ; i++) + copy->blend[i] = state->blend[i]; +return ©->base; }
@@ -1687,8 +1701,30 @@ static void tegra_crtc_atomic_flush(struct drm_crtc *crtc, {struct tegra_dc_state *state =to_dc_state (crtc->state);struct tegra_dc *dc =to_tegra_dc (crtc); +struct tegra_dc_blend_state *bs; +struct tegra_plane *plane; +struct drm_plane *p;u32 value; +if (!dc->soc->supports_blending) { +drm_for_each_plane (p, crtc->dev) { +if (!(p->possible_crtcs & (1 << dc->pipe))) +continue ; + + plane =to_tegra_plane (p); + bs = &state->blend[plane->index]; + +tegra_plane_writel (plane, bs->to_win_x, +DC_WIN_BLEND_2WIN_X ); + +tegra_plane_writel (plane,bs->to_win_y, +DC_WIN_BLEND_2WIN_Y ); + +tegra_plane_writel (plane, bs->to_win_xy, +DC_WIN_BLEND_3WIN_XY ); + } + } + value = state->planes <<8 |GENERAL_UPDATE ;tegra_dc_writel (dc, value,DC_CMD_STATE_CONTROL ); value =tegra_dc_readl (dc,DC_CMD_STATE_CONTROL );
drivers/gpu/drm/tegra/dc.h +21 -0
@@ -18,6 +18,13 @@struct tegra_output ; +struct tegra_dc_blend_state { +u32 to_win_x; +u32 to_win_y; +u32 to_win_xy; +bool opaque; +}; +struct tegra_dc_state {struct drm_crtc_state base;
@@ -26,6 +33,8 @@ struct tegra_dc_state {unsigned int div;u32 planes; + +struct tegra_dc_blend_state blend[3 ]; };static inline struct tegra_dc_state *to_dc_state (struct drm_crtc_state *state)
@@ -649,8 +658,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
drivers/gpu/drm/tegra/fb.c +0 -12
@@ -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);
drivers/gpu/drm/tegra/plane.c +0 -0
@@ -238,3 +238,148 @@ 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_ARGB4444:
+ 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_B4G4R4A4:
+ case WIN_COLOR_DEPTH_A1B5G5R5:
+ case WIN_COLOR_DEPTH_B5G5R5A1:
+ 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;
+}
+
+int tegra_plane_update_blending_state(struct tegra_plane *tegra,
+ struct tegra_plane_state *state)
+{
+ u32 blend_transparent = BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0);
+ u32 blend_opaque = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255);
+ struct tegra_dc_blend_state *blend_state;
+ struct tegra_dc_blend_state *win_a_state;
+ struct tegra_dc_blend_state *win_b_state;
+ struct tegra_dc_blend_state *win_c_state;
+ struct tegra_dc_state *dc_state;
+ struct drm_crtc_state *crtc_state;
+
+ crtc_state = drm_atomic_get_crtc_state(state->base.state,
+ state->base.crtc);
+ if (IS_ERR(crtc_state))
+ return PTR_ERR(crtc_state);
+
+ dc_state = to_dc_state(crtc_state);
+ blend_state = &dc_state->blend[tegra->index];
+ blend_state->opaque = !__drm_format_has_alpha(
+ state->base.fb->format->format);
+
+ win_a_state = &dc_state->blend[0];
+ win_b_state = &dc_state->blend[1];
+ win_c_state = &dc_state->blend[2];
+
+ /* setup blending state for window A */
+
+ if (win_b_state->opaque) {
+ win_a_state->to_win_x = blend_transparent;
+ } else {
+ if (win_a_state->opaque)
+ win_a_state->to_win_x = BLEND_CONTROL_DEPENDENT;
+ else
+ win_a_state->to_win_x = BLEND_CONTROL_ALPHA;
+ }
+
+ if (win_c_state->opaque) {
+ win_a_state->to_win_y = blend_transparent;
+ } else {
+ if (win_a_state->opaque)
+ win_a_state->to_win_y = BLEND_CONTROL_DEPENDENT;
+ else
+ win_a_state->to_win_y = BLEND_CONTROL_ALPHA;
+ }
+
+ if (win_b_state->opaque || win_c_state->opaque) {
+ win_a_state->to_win_xy = blend_transparent;
+ } else {
+ if (win_a_state->opaque)
+ win_a_state->to_win_xy = BLEND_CONTROL_DEPENDENT;
+ else
+ win_a_state->to_win_xy = BLEND_CONTROL_ALPHA;
+ }
+
+ /* setup blending state for window B */
+
+ if (win_b_state->opaque)
+ win_b_state->to_win_x = blend_opaque;
+ else
+ win_b_state->to_win_x = BLEND_CONTROL_ALPHA;
+
+ if (win_c_state->opaque) {
+ win_b_state->to_win_y = blend_transparent;
+ win_b_state->to_win_xy = blend_transparent;
+ } else {
+ if (win_b_state->opaque) {
+ win_b_state->to_win_y = BLEND_CONTROL_DEPENDENT;
+ win_b_state->to_win_xy = BLEND_CONTROL_DEPENDENT;
+ } else {
+ win_b_state->to_win_y = BLEND_CONTROL_ALPHA;
+ win_b_state->to_win_xy = BLEND_CONTROL_ALPHA;
+ }
+ }
+
+ /* setup blending state for window C */
+
+ if (win_c_state->opaque) {
+ win_c_state->to_win_x = blend_opaque;
+ win_c_state->to_win_y = blend_opaque;
+ win_c_state->to_win_xy = blend_opaque;
+ } else {
+ win_c_state->to_win_x = BLEND_CONTROL_ALPHA;
+ win_c_state->to_win_y = BLEND_CONTROL_ALPHA;
+ win_c_state->to_win_xy = BLEND_CONTROL_ALPHA;
+ }
+
+ return 0;
+}
drivers/gpu/drm/tegra/plane.h +4 -0
@@ -58,5 +58,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); +int tegra_plane_update_blending_state (struct tegra_plane *tegra, +struct tegra_plane_state *state);#endif /* TEGRA_PLANE_H */
Signature · 1 line
2.15.1