drm/tegra: dc: Implement legacy blending
authorThierry Reding <treding@nvidia.com>
Wed, 20 Dec 2017 08:39:14 +0000 (09:39 +0100)
committerThierry Reding <treding@nvidia.com>
Thu, 21 Dec 2017 13:55:55 +0000 (14:55 +0100)
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@gmail.com>
Fixes: 7772fdaef939 ("drm/tegra: Support ARGB and ABGR formats")
Signed-off-by: Thierry Reding <treding@nvidia.com>
drivers/gpu/drm/tegra/dc.c
drivers/gpu/drm/tegra/dc.h
drivers/gpu/drm/tegra/fb.c
drivers/gpu/drm/tegra/plane.c
drivers/gpu/drm/tegra/plane.h

index 2a0c1e9..4507063 100644 (file)
@@ -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,
index e2831e9..096a81a 100644 (file)
@@ -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
 
index 1af4ef9..e05fde7 100644 (file)
@@ -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);
 
index 9146aea..154b4d3 100644 (file)
@@ -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 &copy->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];
+       }
+}
index dca66cb..6938719 100644 (file)
@@ -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 */