* [PATCH v2] drm/tegra: dc: Implement legacy blending
@ 2017-12-21 14:03 Thierry Reding
[not found] ` <20171221140317.18447-1-thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
0 siblings, 1 reply; 4+ messages in thread
From: Thierry Reding @ 2017-12-21 14:03 UTC (permalink / raw)
To: Thierry Reding
Cc: Dmitry Osipenko, dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
linux-tegra-u79uwXL29TY76Z2rM5mHXA
From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
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 <digetx-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Fixes: 7772fdaef939 ("drm/tegra: Support ARGB and ABGR formats")
Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
---
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(-)
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 */
--
2.15.1
^ permalink raw reply related [flat|nested] 4+ messages in thread[parent not found: <20171221140317.18447-1-thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>]
* Re: [PATCH v2] drm/tegra: dc: Implement legacy blending [not found] ` <20171221140317.18447-1-thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> @ 2017-12-21 18:46 ` Dmitry Osipenko [not found] ` <3ccf0299-95d9-0c46-dd86-de16434b6e42-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> 0 siblings, 1 reply; 4+ messages in thread From: Dmitry Osipenko @ 2017-12-21 18:46 UTC (permalink / raw) To: Thierry Reding Cc: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW, linux-tegra-u79uwXL29TY76Z2rM5mHXA On 21.12.2017 17:03, Thierry Reding wrote: > From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org> > > 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 <digetx-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> > Fixes: 7772fdaef939 ("drm/tegra: Support ARGB and ABGR formats") > Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org> > --- > 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(-) > > 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: DRM_FORMAT_ARGB4444 missing here? > + 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: WIN_COLOR_DEPTH_B4G4R4A4 missing here? > + 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) { 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. > + 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]; > + } > +} 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] ^ permalink raw reply [flat|nested] 4+ messages in thread
[parent not found: <3ccf0299-95d9-0c46-dd86-de16434b6e42-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>]
* Re: [PATCH v2] drm/tegra: dc: Implement legacy blending [not found] ` <3ccf0299-95d9-0c46-dd86-de16434b6e42-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> @ 2017-12-21 19:13 ` Dmitry Osipenko [not found] ` <cffaa2f8-42f4-557f-1824-a42fdca54aaa-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> 0 siblings, 1 reply; 4+ messages in thread From: Dmitry Osipenko @ 2017-12-21 19:13 UTC (permalink / raw) To: Thierry Reding Cc: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW, linux-tegra-u79uwXL29TY76Z2rM5mHXA On 21.12.2017 21:46, Dmitry Osipenko wrote: > On 21.12.2017 17:03, Thierry Reding wrote: >> From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org> >> >> 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 <digetx-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> >> Fixes: 7772fdaef939 ("drm/tegra: Support ARGB and ABGR formats") >> Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org> >> --- [snip] >> + >> +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) { > > 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] ^ permalink raw reply [flat|nested] 4+ messages in thread
[parent not found: <cffaa2f8-42f4-557f-1824-a42fdca54aaa-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>]
* Re: [PATCH v2] drm/tegra: dc: Implement legacy blending [not found] ` <cffaa2f8-42f4-557f-1824-a42fdca54aaa-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> @ 2017-12-21 23:52 ` Dmitry Osipenko 0 siblings, 0 replies; 4+ messages in thread From: Dmitry Osipenko @ 2017-12-21 23:52 UTC (permalink / raw) To: Thierry Reding Cc: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW, linux-tegra-u79uwXL29TY76Z2rM5mHXA On 21.12.2017 22:13, Dmitry Osipenko wrote: > On 21.12.2017 21:46, Dmitry Osipenko wrote: >> On 21.12.2017 17:03, Thierry Reding wrote: >>> From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org> >>> >>> 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 <digetx-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> >>> Fixes: 7772fdaef939 ("drm/tegra: Support ARGB and ABGR formats") >>> Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org> >>> --- > > [snip] > >>> + >>> +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) { >> >> 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] > 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. --------------------------------------- From e5264125fb81757999f6b11fec0e7cfffb2ca6d6 Mon Sep 17 00:00:00 2001 From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org> 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. Reported-by: Dmitry Osipenko <digetx-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> Fixes: 7772fdaef939 ("drm/tegra: Support ARGB and ABGR formats") Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org> Signed-off-by: Dmitry Osipenko <digetx-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> --- 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(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 2a0c1e93f82e..fb7c0d211ac0 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -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); diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index e2831e96ea96..2bfe4d46f007 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -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 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..6877fba46984 100644 --- a/drivers/gpu/drm/tegra/plane.c +++ b/drivers/gpu/drm/tegra/plane.c @@ -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; +} diff --git a/drivers/gpu/drm/tegra/plane.h b/drivers/gpu/drm/tegra/plane.h index dca66cb95d25..13d410099772 100644 --- a/drivers/gpu/drm/tegra/plane.h +++ b/drivers/gpu/drm/tegra/plane.h @@ -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 */ -- 2.15.1 ^ permalink raw reply related [flat|nested] 4+ messages in thread
end of thread, other threads:[~2017-12-21 23:52 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-12-21 14:03 [PATCH v2] drm/tegra: dc: Implement legacy blending Thierry Reding
[not found] ` <20171221140317.18447-1-thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2017-12-21 18:46 ` Dmitry Osipenko
[not found] ` <3ccf0299-95d9-0c46-dd86-de16434b6e42-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2017-12-21 19:13 ` Dmitry Osipenko
[not found] ` <cffaa2f8-42f4-557f-1824-a42fdca54aaa-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2017-12-21 23:52 ` Dmitry Osipenko
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox