drm/i915: Fix sprite destination colorkeying on SKL+
authorVille Syrjälä <ville.syrjala@linux.intel.com>
Tue, 29 May 2018 18:28:00 +0000 (21:28 +0300)
committerVille Syrjälä <ville.syrjala@linux.intel.com>
Fri, 8 Jun 2018 18:20:21 +0000 (21:20 +0300)
On SKL+ the dst colorkey must be configured on the lower
plane that contains the colorkey. This is in contrast to
most earlier platforms where the dst colorkey is configured
on the plane above.

The hardware will peform dst keying only between two immediately
adjacent (in zorder) planes. Plane 2 will be keyed against plane 1,
plane 3 againts plane 2, and so on. There is no way to key arbitrary
planes against plane 1. Thus offering dst color keying on plane 3+
is pointless. In fact it can be harmful since enabling dst keying on
more than one plane on the same pipe leads to only the top-most of
the planes performing the keying. For any plane lower in zorder the
dst key enable is simply ignored.

v2: s/plane 0/plane 1/ etc. since the hw plane names start from 1
    Don't break dst colorkey on pre-SKL sprites (hunk ended in the
    wrong patch)

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20180529182804.8571-1-ville.syrjala@linux.intel.com
Reviewed-by: Stanislav Lisovskiy <stanislav.lisovskiy@intel.com> #v1
drivers/gpu/drm/i915/intel_sprite.c

index 24f86c4375cf93e7695ec7cb076c8ad2987607d3..d9e7f4fb5096c6216d497616a80192b69397cbdc 100644 (file)
@@ -1175,6 +1175,37 @@ intel_check_sprite_plane(struct intel_plane *plane,
        return 0;
 }
 
+static bool has_dst_key_in_primary_plane(struct drm_i915_private *dev_priv)
+{
+       return INTEL_GEN(dev_priv) >= 9;
+}
+
+static void intel_plane_set_ckey(struct intel_plane_state *plane_state,
+                                const struct drm_intel_sprite_colorkey *set)
+{
+       struct intel_plane *plane = to_intel_plane(plane_state->base.plane);
+       struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
+       struct drm_intel_sprite_colorkey *key = &plane_state->ckey;
+
+       *key = *set;
+
+       /*
+        * We want src key enabled on the
+        * sprite and not on the primary.
+        */
+       if (plane->id == PLANE_PRIMARY &&
+           set->flags & I915_SET_COLORKEY_SOURCE)
+               key->flags = 0;
+
+       /*
+        * On SKL+ we want dst key enabled on
+        * the primary and not on the sprite.
+        */
+       if (INTEL_GEN(dev_priv) >= 9 && plane->id != PLANE_PRIMARY &&
+           set->flags & I915_SET_COLORKEY_DESTINATION)
+               key->flags = 0;
+}
+
 int intel_sprite_set_colorkey_ioctl(struct drm_device *dev, void *data,
                                    struct drm_file *file_priv)
 {
@@ -1204,6 +1235,16 @@ int intel_sprite_set_colorkey_ioctl(struct drm_device *dev, void *data,
        if (!plane || plane->type != DRM_PLANE_TYPE_OVERLAY)
                return -ENOENT;
 
+       /*
+        * SKL+ only plane 2 can do destination keying against plane 1.
+        * Also multiple planes can't do destination keying on the same
+        * pipe simultaneously.
+        */
+       if (INTEL_GEN(dev_priv) >= 9 &&
+           to_intel_plane(plane)->id >= PLANE_SPRITE1 &&
+           set->flags & I915_SET_COLORKEY_DESTINATION)
+               return -EINVAL;
+
        drm_modeset_acquire_init(&ctx, 0);
 
        state = drm_atomic_state_alloc(plane->dev);
@@ -1216,11 +1257,28 @@ int intel_sprite_set_colorkey_ioctl(struct drm_device *dev, void *data,
        while (1) {
                plane_state = drm_atomic_get_plane_state(state, plane);
                ret = PTR_ERR_OR_ZERO(plane_state);
-               if (!ret) {
-                       to_intel_plane_state(plane_state)->ckey = *set;
-                       ret = drm_atomic_commit(state);
+               if (!ret)
+                       intel_plane_set_ckey(to_intel_plane_state(plane_state), set);
+
+               /*
+                * On some platforms we have to configure
+                * the dst colorkey on the primary plane.
+                */
+               if (!ret && has_dst_key_in_primary_plane(dev_priv)) {
+                       struct intel_crtc *crtc =
+                               intel_get_crtc_for_pipe(dev_priv,
+                                                       to_intel_plane(plane)->pipe);
+
+                       plane_state = drm_atomic_get_plane_state(state,
+                                                                crtc->base.primary);
+                       ret = PTR_ERR_OR_ZERO(plane_state);
+                       if (!ret)
+                               intel_plane_set_ckey(to_intel_plane_state(plane_state), set);
                }
 
+               if (!ret)
+                       ret = drm_atomic_commit(state);
+
                if (ret != -EDEADLK)
                        break;