drm/i915: Compute display surface offset in the plane check hook for SKL+
authorVille Syrjälä <ville.syrjala@linux.intel.com>
Thu, 28 Jan 2016 14:53:54 +0000 (16:53 +0200)
committerVille Syrjälä <ville.syrjala@linux.intel.com>
Thu, 11 Aug 2016 15:35:10 +0000 (18:35 +0300)
SKL has nasty limitations with the display surface offsets:
* source x offset + width must be less than the stride for X tiled
  surfaces or the display engine falls over
* the surface offset requires lots of alignment (256K or 1M)

These facts mean that we can't just pick any suitably aligned tile
boundary as the offset and expect the resulting x offset to be useable.
The solution is to start with the closest boundary as before, but then
keep searching backwards until we find one that works, or don't. This
means we must be prepared to fail, hence the whole surface offset
calculation needs to be moved to the .check_plane() hook from the
.update_plane() hook.

While at it we can check that the source width/height don't exceed
maximum plane size limits.

We'll store the results of the computation in the plane state to make
it easy for the .update_plane() hook to do its thing.

v2: Replace for+break loop with while loop
    Rebase due to drm_plane_state src/dst rects
    Rebase due to plane_check_state()

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: Sivakumar Thulasimani <sivakumar.thulasimani@intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/1470821001-25272-11-git-send-email-ville.syrjala@linux.intel.com
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_sprite.c

index 06ce391..ffb7419 100644 (file)
@@ -2842,6 +2842,117 @@ valid_fb:
                  &obj->frontbuffer_bits);
 }
 
+static int skl_max_plane_width(const struct drm_framebuffer *fb, int plane,
+                              unsigned int rotation)
+{
+       int cpp = drm_format_plane_cpp(fb->pixel_format, plane);
+
+       switch (fb->modifier[plane]) {
+       case DRM_FORMAT_MOD_NONE:
+       case I915_FORMAT_MOD_X_TILED:
+               switch (cpp) {
+               case 8:
+                       return 4096;
+               case 4:
+               case 2:
+               case 1:
+                       return 8192;
+               default:
+                       MISSING_CASE(cpp);
+                       break;
+               }
+               break;
+       case I915_FORMAT_MOD_Y_TILED:
+       case I915_FORMAT_MOD_Yf_TILED:
+               switch (cpp) {
+               case 8:
+                       return 2048;
+               case 4:
+                       return 4096;
+               case 2:
+               case 1:
+                       return 8192;
+               default:
+                       MISSING_CASE(cpp);
+                       break;
+               }
+               break;
+       default:
+               MISSING_CASE(fb->modifier[plane]);
+       }
+
+       return 2048;
+}
+
+static int skl_check_main_surface(struct intel_plane_state *plane_state)
+{
+       const struct drm_i915_private *dev_priv = to_i915(plane_state->base.plane->dev);
+       const struct drm_framebuffer *fb = plane_state->base.fb;
+       unsigned int rotation = plane_state->base.rotation;
+       int x = plane_state->src.x1 >> 16;
+       int y = plane_state->src.y1 >> 16;
+       int w = drm_rect_width(&plane_state->src) >> 16;
+       int h = drm_rect_height(&plane_state->src) >> 16;
+       int max_width = skl_max_plane_width(fb, 0, rotation);
+       int max_height = 4096;
+       u32 alignment, offset;
+
+       if (w > max_width || h > max_height) {
+               DRM_DEBUG_KMS("requested Y/RGB source size %dx%d too big (limit %dx%d)\n",
+                             w, h, max_width, max_height);
+               return -EINVAL;
+       }
+
+       intel_add_fb_offsets(&x, &y, plane_state, 0);
+       offset = intel_compute_tile_offset(&x, &y, plane_state, 0);
+
+       alignment = intel_surf_alignment(dev_priv, fb->modifier[0]);
+
+       /*
+        * When using an X-tiled surface, the plane blows up
+        * if the x offset + width exceed the stride.
+        *
+        * TODO: linear and Y-tiled seem fine, Yf untested,
+        */
+       if (fb->modifier[0] == I915_FORMAT_MOD_X_TILED) {
+               int cpp = drm_format_plane_cpp(fb->pixel_format, 0);
+
+               while ((x + w) * cpp > fb->pitches[0]) {
+                       if (offset == 0) {
+                               DRM_DEBUG_KMS("Unable to find suitable display surface offset\n");
+                               return -EINVAL;
+                       }
+
+                       offset = intel_adjust_tile_offset(&x, &y, plane_state, 0,
+                                                         offset, offset - alignment);
+               }
+       }
+
+       plane_state->main.offset = offset;
+       plane_state->main.x = x;
+       plane_state->main.y = y;
+
+       return 0;
+}
+
+int skl_check_plane_surface(struct intel_plane_state *plane_state)
+{
+       const struct drm_framebuffer *fb = plane_state->base.fb;
+       unsigned int rotation = plane_state->base.rotation;
+       int ret;
+
+       /* Rotate src coordinates to match rotated GTT view */
+       if (intel_rotation_90_or_270(rotation))
+               drm_rect_rotate(&plane_state->src,
+                               fb->width, fb->height, BIT(DRM_ROTATE_270));
+
+       ret = skl_check_main_surface(plane_state);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
 static void i9xx_update_primary_plane(struct drm_plane *primary,
                                      const struct intel_crtc_state *crtc_state,
                                      const struct intel_plane_state *plane_state)
@@ -3221,10 +3332,10 @@ static void skylake_update_primary_plane(struct drm_plane *plane,
        u32 plane_ctl;
        unsigned int rotation = plane_state->base.rotation;
        u32 stride = skl_plane_stride(fb, 0, rotation);
-       u32 surf_addr;
+       u32 surf_addr = plane_state->main.offset;
        int scaler_id = plane_state->scaler_id;
-       int src_x = plane_state->src.x1 >> 16;
-       int src_y = plane_state->src.y1 >> 16;
+       int src_x = plane_state->main.x;
+       int src_y = plane_state->main.y;
        int src_w = drm_rect_width(&plane_state->src) >> 16;
        int src_h = drm_rect_height(&plane_state->src) >> 16;
        int dst_x = plane_state->dst.x1;
@@ -3241,26 +3352,6 @@ static void skylake_update_primary_plane(struct drm_plane *plane,
        plane_ctl |= PLANE_CTL_PLANE_GAMMA_DISABLE;
        plane_ctl |= skl_plane_ctl_rotation(rotation);
 
-       if (intel_rotation_90_or_270(rotation)) {
-               struct drm_rect r = {
-                       .x1 = src_x,
-                       .x2 = src_x + src_w,
-                       .y1 = src_y,
-                       .y2 = src_y + src_h,
-               };
-
-               /* Rotate src coordinates to match rotated GTT view */
-               drm_rect_rotate(&r, fb->width, fb->height, BIT(DRM_ROTATE_270));
-
-               src_x = r.x1;
-               src_y = r.y1;
-               src_w = drm_rect_width(&r);
-               src_h = drm_rect_height(&r);
-       }
-
-       intel_add_fb_offsets(&src_x, &src_y, plane_state, 0);
-       surf_addr = intel_compute_tile_offset(&src_x, &src_y, plane_state, 0);
-
        /* Sizes are 0 based */
        src_w--;
        src_h--;
@@ -14419,13 +14510,15 @@ intel_check_primary_plane(struct drm_plane *plane,
                          struct intel_crtc_state *crtc_state,
                          struct intel_plane_state *state)
 {
+       struct drm_i915_private *dev_priv = to_i915(plane->dev);
        struct drm_crtc *crtc = state->base.crtc;
        struct drm_framebuffer *fb = state->base.fb;
        int min_scale = DRM_PLANE_HELPER_NO_SCALING;
        int max_scale = DRM_PLANE_HELPER_NO_SCALING;
        bool can_position = false;
+       int ret;
 
-       if (INTEL_INFO(plane->dev)->gen >= 9) {
+       if (INTEL_GEN(dev_priv) >= 9) {
                /* use scaler when colorkey is not required */
                if (state->ckey.flags == I915_SET_COLORKEY_NONE) {
                        min_scale = 1;
@@ -14434,12 +14527,25 @@ intel_check_primary_plane(struct drm_plane *plane,
                can_position = true;
        }
 
-       return drm_plane_helper_check_update(plane, crtc, fb, &state->src,
-                                            &state->dst, &state->clip,
-                                            state->base.rotation,
-                                            min_scale, max_scale,
-                                            can_position, true,
-                                            &state->visible);
+       ret = drm_plane_helper_check_update(plane, crtc, fb, &state->src,
+                                           &state->dst, &state->clip,
+                                           state->base.rotation,
+                                           min_scale, max_scale,
+                                           can_position, true,
+                                           &state->visible);
+       if (ret)
+               return ret;
+
+       if (!fb)
+               return 0;
+
+       if (INTEL_GEN(dev_priv) >= 9) {
+               ret = skl_check_plane_surface(state);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
 }
 
 static void intel_begin_crtc_commit(struct drm_crtc *crtc,
index 9b27bd7..2b980f7 100644 (file)
@@ -353,6 +353,11 @@ struct intel_plane_state {
        struct drm_rect clip;
        bool visible;
 
+       struct {
+               u32 offset;
+               int x, y;
+       } main;
+
        /*
         * scaler_id
         *    = -1 : not using a scaler
@@ -1346,6 +1351,7 @@ u32 skl_plane_ctl_tiling(uint64_t fb_modifier);
 u32 skl_plane_ctl_rotation(unsigned int rotation);
 u32 skl_plane_stride(const struct drm_framebuffer *fb, int plane,
                     unsigned int rotation);
+int skl_check_plane_surface(struct intel_plane_state *plane_state);
 
 /* intel_csr.c */
 void intel_csr_ucode_init(struct drm_i915_private *);
index ccab808..c2e2c31 100644 (file)
@@ -207,15 +207,15 @@ skl_update_plane(struct drm_plane *drm_plane,
        const int plane = intel_plane->plane + 1;
        u32 plane_ctl;
        const struct drm_intel_sprite_colorkey *key = &plane_state->ckey;
-       u32 surf_addr;
+       u32 surf_addr = plane_state->main.offset;
        unsigned int rotation = plane_state->base.rotation;
        u32 stride = skl_plane_stride(fb, 0, rotation);
        int crtc_x = plane_state->dst.x1;
        int crtc_y = plane_state->dst.y1;
        uint32_t crtc_w = drm_rect_width(&plane_state->dst);
        uint32_t crtc_h = drm_rect_height(&plane_state->dst);
-       uint32_t x = plane_state->src.x1 >> 16;
-       uint32_t y = plane_state->src.y1 >> 16;
+       uint32_t x = plane_state->main.x;
+       uint32_t y = plane_state->main.y;
        uint32_t src_w = drm_rect_width(&plane_state->src) >> 16;
        uint32_t src_h = drm_rect_height(&plane_state->src) >> 16;
 
@@ -239,26 +239,6 @@ skl_update_plane(struct drm_plane *drm_plane,
        else if (key->flags & I915_SET_COLORKEY_SOURCE)
                plane_ctl |= PLANE_CTL_KEY_ENABLE_SOURCE;
 
-       if (intel_rotation_90_or_270(rotation)) {
-               struct drm_rect r = {
-                       .x1 = x,
-                       .x2 = x + src_w,
-                       .y1 = y,
-                       .y2 = y + src_h,
-               };
-
-               /* Rotate src coordinates to match rotated GTT view */
-               drm_rect_rotate(&r, fb->width, fb->height, BIT(DRM_ROTATE_270));
-
-               x = r.x1;
-               y = r.y1;
-               src_w = drm_rect_width(&r);
-               src_h = drm_rect_height(&r);
-       }
-
-       intel_add_fb_offsets(&x, &y, plane_state, 0);
-       surf_addr = intel_compute_tile_offset(&x, &y, plane_state, 0);
-
        /* Sizes are 0 based */
        src_w--;
        src_h--;
@@ -773,6 +753,7 @@ intel_check_sprite_plane(struct drm_plane *plane,
        int hscale, vscale;
        int max_scale, min_scale;
        bool can_scale;
+       int ret;
 
        if (!fb) {
                state->visible = false;
@@ -927,6 +908,12 @@ intel_check_sprite_plane(struct drm_plane *plane,
        dst->y1 = crtc_y;
        dst->y2 = crtc_y + crtc_h;
 
+       if (INTEL_GEN(dev) >= 9) {
+               ret = skl_check_plane_surface(state);
+               if (ret)
+                       return ret;
+       }
+
        return 0;
 }