drm/i915: Treat fb->offsets[] as a raw byte offset instead of a linear offset
authorVille Syrjälä <ville.syrjala@linux.intel.com>
Thu, 24 Aug 2017 19:10:49 +0000 (22:10 +0300)
committerRodrigo Vivi <rodrigo.vivi@intel.com>
Wed, 30 Aug 2017 19:08:17 +0000 (12:08 -0700)
Userspace wants to treat fb->offsets[] as raw byte offsets into the gem
bo. Adjust the kernel code to match.

Cc: Ben Widawsky <ben@bwidawsk.net>
Cc: Jason Ekstrand <jason@jlekstrand.net>
Cc: Daniel Stone <daniels@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20170824191100.10949-2-ville.syrjala@linux.intel.com
Acked-by: Ben Widawsky <ben@bwidawsk.net>
Fixes: 2e2adb05736c ("drm/i915: Add render decompression support")
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
(cherry picked from commit 303ba6955499ef757ca5ddcc816e370cc7581cb5)
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
drivers/gpu/drm/i915/intel_display.c

index 0e93ec201fe31092af3a6cd1cb0be6fac53fb71e..8578739a0c31c6491e6fbdac9bc4d795154aba00 100644 (file)
@@ -2288,17 +2288,13 @@ void intel_add_fb_offsets(int *x, int *y,
        }
 }
 
-/*
- * Input tile dimensions and pitch must already be
- * rotated to match x and y, and in pixel units.
- */
-static u32 _intel_adjust_tile_offset(int *x, int *y,
-                                    unsigned int tile_width,
-                                    unsigned int tile_height,
-                                    unsigned int tile_size,
-                                    unsigned int pitch_tiles,
-                                    u32 old_offset,
-                                    u32 new_offset)
+static u32 __intel_adjust_tile_offset(int *x, int *y,
+                                     unsigned int tile_width,
+                                     unsigned int tile_height,
+                                     unsigned int tile_size,
+                                     unsigned int pitch_tiles,
+                                     u32 old_offset,
+                                     u32 new_offset)
 {
        unsigned int pitch_pixels = pitch_tiles * tile_width;
        unsigned int tiles;
@@ -2319,18 +2315,13 @@ static u32 _intel_adjust_tile_offset(int *x, int *y,
        return new_offset;
 }
 
-/*
- * Adjust the tile offset by moving the difference into
- * the x/y offsets.
- */
-static u32 intel_adjust_tile_offset(int *x, int *y,
-                                   const struct intel_plane_state *state, int plane,
-                                   u32 old_offset, u32 new_offset)
+static u32 _intel_adjust_tile_offset(int *x, int *y,
+                                    const struct drm_framebuffer *fb, int plane,
+                                    unsigned int rotation,
+                                    u32 old_offset, u32 new_offset)
 {
-       const struct drm_i915_private *dev_priv = to_i915(state->base.plane->dev);
-       const struct drm_framebuffer *fb = state->base.fb;
+       const struct drm_i915_private *dev_priv = to_i915(fb->dev);
        unsigned int cpp = fb->format->cpp[plane];
-       unsigned int rotation = state->base.rotation;
        unsigned int pitch = intel_fb_pitch(fb, plane, rotation);
 
        WARN_ON(new_offset > old_offset);
@@ -2349,9 +2340,9 @@ static u32 intel_adjust_tile_offset(int *x, int *y,
                        pitch_tiles = pitch / (tile_width * cpp);
                }
 
-               _intel_adjust_tile_offset(x, y, tile_width, tile_height,
-                                         tile_size, pitch_tiles,
-                                         old_offset, new_offset);
+               __intel_adjust_tile_offset(x, y, tile_width, tile_height,
+                                          tile_size, pitch_tiles,
+                                          old_offset, new_offset);
        } else {
                old_offset += *y * pitch + *x * cpp;
 
@@ -2362,6 +2353,19 @@ static u32 intel_adjust_tile_offset(int *x, int *y,
        return new_offset;
 }
 
+/*
+ * Adjust the tile offset by moving the difference into
+ * the x/y offsets.
+ */
+static u32 intel_adjust_tile_offset(int *x, int *y,
+                                   const struct intel_plane_state *state, int plane,
+                                   u32 old_offset, u32 new_offset)
+{
+       return _intel_adjust_tile_offset(x, y, state->base.fb, plane,
+                                        state->base.rotation,
+                                        old_offset, new_offset);
+}
+
 /*
  * Computes the linear offset to the base tile and adjusts
  * x, y. bytes per pixel is assumed to be a power-of-two.
@@ -2413,9 +2417,9 @@ static u32 _intel_compute_tile_offset(const struct drm_i915_private *dev_priv,
                offset = (tile_rows * pitch_tiles + tiles) * tile_size;
                offset_aligned = offset & ~alignment;
 
-               _intel_adjust_tile_offset(x, y, tile_width, tile_height,
-                                         tile_size, pitch_tiles,
-                                         offset, offset_aligned);
+               __intel_adjust_tile_offset(x, y, tile_width, tile_height,
+                                          tile_size, pitch_tiles,
+                                          offset, offset_aligned);
        } else {
                offset = *y * pitch + *x * cpp;
                offset_aligned = offset & ~alignment;
@@ -2447,16 +2451,24 @@ u32 intel_compute_tile_offset(int *x, int *y,
                                          rotation, alignment);
 }
 
-/* Convert the fb->offset[] linear offset into x/y offsets */
-static void intel_fb_offset_to_xy(int *x, int *y,
-                                 const struct drm_framebuffer *fb, int plane)
+/* Convert the fb->offset[] into x/y offsets */
+static int intel_fb_offset_to_xy(int *x, int *y,
+                                const struct drm_framebuffer *fb, int plane)
 {
-       unsigned int cpp = fb->format->cpp[plane];
-       unsigned int pitch = fb->pitches[plane];
-       u32 linear_offset = fb->offsets[plane];
+       struct drm_i915_private *dev_priv = to_i915(fb->dev);
+
+       if (fb->modifier != DRM_FORMAT_MOD_LINEAR &&
+           fb->offsets[plane] % intel_tile_size(dev_priv))
+               return -EINVAL;
 
-       *y = linear_offset / pitch;
-       *x = linear_offset % pitch / cpp;
+       *x = 0;
+       *y = 0;
+
+       _intel_adjust_tile_offset(x, y,
+                                 fb, plane, DRM_MODE_ROTATE_0,
+                                 fb->offsets[plane], 0);
+
+       return 0;
 }
 
 static unsigned int intel_fb_modifier_to_tiling(uint64_t fb_modifier)
@@ -2523,12 +2535,18 @@ intel_fill_fb_info(struct drm_i915_private *dev_priv,
                unsigned int cpp, size;
                u32 offset;
                int x, y;
+               int ret;
 
                cpp = fb->format->cpp[i];
                width = drm_framebuffer_plane_width(fb->width, fb, i);
                height = drm_framebuffer_plane_height(fb->height, fb, i);
 
-               intel_fb_offset_to_xy(&x, &y, fb, i);
+               ret = intel_fb_offset_to_xy(&x, &y, fb, i);
+               if (ret) {
+                       DRM_DEBUG_KMS("bad fb plane %d offset: 0x%x\n",
+                                     i, fb->offsets[i]);
+                       return ret;
+               }
 
                if ((fb->modifier == I915_FORMAT_MOD_Y_TILED_CCS ||
                     fb->modifier == I915_FORMAT_MOD_Yf_TILED_CCS) && i == 1) {
@@ -2539,11 +2557,13 @@ intel_fill_fb_info(struct drm_i915_private *dev_priv,
                        int ccs_x, ccs_y;
 
                        intel_tile_dims(fb, i, &tile_width, &tile_height);
+                       tile_width *= hsub;
+                       tile_height *= vsub;
 
-                       ccs_x = (x * hsub) % (tile_width * hsub);
-                       ccs_y = (y * vsub) % (tile_height * vsub);
-                       main_x = intel_fb->normal[0].x % (tile_width * hsub);
-                       main_y = intel_fb->normal[0].y % (tile_height * vsub);
+                       ccs_x = (x * hsub) % tile_width;
+                       ccs_y = (y * vsub) % tile_height;
+                       main_x = intel_fb->normal[0].x % tile_width;
+                       main_y = intel_fb->normal[0].y % tile_height;
 
                        /*
                         * CCS doesn't have its own x/y offset register, so the intra CCS tile
@@ -2632,10 +2652,10 @@ intel_fill_fb_info(struct drm_i915_private *dev_priv,
                         * We only keep the x/y offsets, so push all of the
                         * gtt offset into the x/y offsets.
                         */
-                       _intel_adjust_tile_offset(&x, &y,
-                                                 tile_width, tile_height,
-                                                 tile_size, pitch_tiles,
-                                                 gtt_offset_rotated * tile_size, 0);
+                       __intel_adjust_tile_offset(&x, &y,
+                                                  tile_width, tile_height,
+                                                  tile_size, pitch_tiles,
+                                                  gtt_offset_rotated * tile_size, 0);
 
                        gtt_offset_rotated += rot_info->plane[i].width * rot_info->plane[i].height;