diff options
Diffstat (limited to 'drivers/gpu/drm/i915/display/intel_fb.c')
| -rw-r--r-- | drivers/gpu/drm/i915/display/intel_fb.c | 1191 |
1 files changed, 1112 insertions, 79 deletions
diff --git a/drivers/gpu/drm/i915/display/intel_fb.c b/drivers/gpu/drm/i915/display/intel_fb.c index c60a81a81c09..23cfe2e5ce2a 100644 --- a/drivers/gpu/drm/i915/display/intel_fb.c +++ b/drivers/gpu/drm/i915/display/intel_fb.c @@ -4,33 +4,475 @@ */ #include <drm/drm_framebuffer.h> +#include <drm/drm_modeset_helper.h> +#include "i915_drv.h" #include "intel_display.h" #include "intel_display_types.h" +#include "intel_dpt.h" #include "intel_fb.h" #define check_array_bounds(i915, a, i) drm_WARN_ON(&(i915)->drm, (i) >= ARRAY_SIZE(a)) -bool is_ccs_plane(const struct drm_framebuffer *fb, int plane) +/* + * From the Sky Lake PRM: + * "The Color Control Surface (CCS) contains the compression status of + * the cache-line pairs. The compression state of the cache-line pair + * is specified by 2 bits in the CCS. Each CCS cache-line represents + * an area on the main surface of 16 x16 sets of 128 byte Y-tiled + * cache-line-pairs. CCS is always Y tiled." + * + * Since cache line pairs refers to horizontally adjacent cache lines, + * each cache line in the CCS corresponds to an area of 32x16 cache + * lines on the main surface. Since each pixel is 4 bytes, this gives + * us a ratio of one byte in the CCS for each 8x16 pixels in the + * main surface. + */ +static const struct drm_format_info skl_ccs_formats[] = { + { .format = DRM_FORMAT_XRGB8888, .depth = 24, .num_planes = 2, + .cpp = { 4, 1, }, .hsub = 8, .vsub = 16, }, + { .format = DRM_FORMAT_XBGR8888, .depth = 24, .num_planes = 2, + .cpp = { 4, 1, }, .hsub = 8, .vsub = 16, }, + { .format = DRM_FORMAT_ARGB8888, .depth = 32, .num_planes = 2, + .cpp = { 4, 1, }, .hsub = 8, .vsub = 16, .has_alpha = true, }, + { .format = DRM_FORMAT_ABGR8888, .depth = 32, .num_planes = 2, + .cpp = { 4, 1, }, .hsub = 8, .vsub = 16, .has_alpha = true, }, +}; + +/* + * Gen-12 compression uses 4 bits of CCS data for each cache line pair in the + * main surface. And each 64B CCS cache line represents an area of 4x1 Y-tiles + * in the main surface. With 4 byte pixels and each Y-tile having dimensions of + * 32x32 pixels, the ratio turns out to 1B in the CCS for every 2x32 pixels in + * the main surface. + */ +static const struct drm_format_info gen12_ccs_formats[] = { + { .format = DRM_FORMAT_XRGB8888, .depth = 24, .num_planes = 2, + .char_per_block = { 4, 1 }, .block_w = { 1, 2 }, .block_h = { 1, 1 }, + .hsub = 1, .vsub = 1, }, + { .format = DRM_FORMAT_XBGR8888, .depth = 24, .num_planes = 2, + .char_per_block = { 4, 1 }, .block_w = { 1, 2 }, .block_h = { 1, 1 }, + .hsub = 1, .vsub = 1, }, + { .format = DRM_FORMAT_ARGB8888, .depth = 32, .num_planes = 2, + .char_per_block = { 4, 1 }, .block_w = { 1, 2 }, .block_h = { 1, 1 }, + .hsub = 1, .vsub = 1, .has_alpha = true }, + { .format = DRM_FORMAT_ABGR8888, .depth = 32, .num_planes = 2, + .char_per_block = { 4, 1 }, .block_w = { 1, 2 }, .block_h = { 1, 1 }, + .hsub = 1, .vsub = 1, .has_alpha = true }, + { .format = DRM_FORMAT_YUYV, .num_planes = 2, + .char_per_block = { 2, 1 }, .block_w = { 1, 2 }, .block_h = { 1, 1 }, + .hsub = 2, .vsub = 1, .is_yuv = true }, + { .format = DRM_FORMAT_YVYU, .num_planes = 2, + .char_per_block = { 2, 1 }, .block_w = { 1, 2 }, .block_h = { 1, 1 }, + .hsub = 2, .vsub = 1, .is_yuv = true }, + { .format = DRM_FORMAT_UYVY, .num_planes = 2, + .char_per_block = { 2, 1 }, .block_w = { 1, 2 }, .block_h = { 1, 1 }, + .hsub = 2, .vsub = 1, .is_yuv = true }, + { .format = DRM_FORMAT_VYUY, .num_planes = 2, + .char_per_block = { 2, 1 }, .block_w = { 1, 2 }, .block_h = { 1, 1 }, + .hsub = 2, .vsub = 1, .is_yuv = true }, + { .format = DRM_FORMAT_XYUV8888, .num_planes = 2, + .char_per_block = { 4, 1 }, .block_w = { 1, 2 }, .block_h = { 1, 1 }, + .hsub = 1, .vsub = 1, .is_yuv = true }, + { .format = DRM_FORMAT_NV12, .num_planes = 4, + .char_per_block = { 1, 2, 1, 1 }, .block_w = { 1, 1, 4, 4 }, .block_h = { 1, 1, 1, 1 }, + .hsub = 2, .vsub = 2, .is_yuv = true }, + { .format = DRM_FORMAT_P010, .num_planes = 4, + .char_per_block = { 2, 4, 1, 1 }, .block_w = { 1, 1, 2, 2 }, .block_h = { 1, 1, 1, 1 }, + .hsub = 2, .vsub = 2, .is_yuv = true }, + { .format = DRM_FORMAT_P012, .num_planes = 4, + .char_per_block = { 2, 4, 1, 1 }, .block_w = { 1, 1, 2, 2 }, .block_h = { 1, 1, 1, 1 }, + .hsub = 2, .vsub = 2, .is_yuv = true }, + { .format = DRM_FORMAT_P016, .num_planes = 4, + .char_per_block = { 2, 4, 1, 1 }, .block_w = { 1, 1, 2, 2 }, .block_h = { 1, 1, 1, 1 }, + .hsub = 2, .vsub = 2, .is_yuv = true }, +}; + +/* + * Same as gen12_ccs_formats[] above, but with additional surface used + * to pass Clear Color information in plane 2 with 64 bits of data. + */ +static const struct drm_format_info gen12_ccs_cc_formats[] = { + { .format = DRM_FORMAT_XRGB8888, .depth = 24, .num_planes = 3, + .char_per_block = { 4, 1, 0 }, .block_w = { 1, 2, 2 }, .block_h = { 1, 1, 1 }, + .hsub = 1, .vsub = 1, }, + { .format = DRM_FORMAT_XBGR8888, .depth = 24, .num_planes = 3, + .char_per_block = { 4, 1, 0 }, .block_w = { 1, 2, 2 }, .block_h = { 1, 1, 1 }, + .hsub = 1, .vsub = 1, }, + { .format = DRM_FORMAT_ARGB8888, .depth = 32, .num_planes = 3, + .char_per_block = { 4, 1, 0 }, .block_w = { 1, 2, 2 }, .block_h = { 1, 1, 1 }, + .hsub = 1, .vsub = 1, .has_alpha = true }, + { .format = DRM_FORMAT_ABGR8888, .depth = 32, .num_planes = 3, + .char_per_block = { 4, 1, 0 }, .block_w = { 1, 2, 2 }, .block_h = { 1, 1, 1 }, + .hsub = 1, .vsub = 1, .has_alpha = true }, +}; + +struct intel_modifier_desc { + u64 modifier; + struct { + u8 from; + u8 until; + } display_ver; +#define DISPLAY_VER_ALL { 0, -1 } + + const struct drm_format_info *formats; + int format_count; +#define FORMAT_OVERRIDE(format_list) \ + .formats = format_list, \ + .format_count = ARRAY_SIZE(format_list) + + u8 plane_caps; + + struct { + u8 cc_planes:3; + u8 packed_aux_planes:4; + u8 planar_aux_planes:4; + } ccs; +}; + +#define INTEL_PLANE_CAP_CCS_MASK (INTEL_PLANE_CAP_CCS_RC | \ + INTEL_PLANE_CAP_CCS_RC_CC | \ + INTEL_PLANE_CAP_CCS_MC) +#define INTEL_PLANE_CAP_TILING_MASK (INTEL_PLANE_CAP_TILING_X | \ + INTEL_PLANE_CAP_TILING_Y | \ + INTEL_PLANE_CAP_TILING_Yf) +#define INTEL_PLANE_CAP_TILING_NONE 0 + +static const struct intel_modifier_desc intel_modifiers[] = { + { + .modifier = I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS, + .display_ver = { 12, 13 }, + .plane_caps = INTEL_PLANE_CAP_TILING_Y | INTEL_PLANE_CAP_CCS_MC, + + .ccs.packed_aux_planes = BIT(1), + .ccs.planar_aux_planes = BIT(2) | BIT(3), + + FORMAT_OVERRIDE(gen12_ccs_formats), + }, { + .modifier = I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS, + .display_ver = { 12, 13 }, + .plane_caps = INTEL_PLANE_CAP_TILING_Y | INTEL_PLANE_CAP_CCS_RC, + + .ccs.packed_aux_planes = BIT(1), + + FORMAT_OVERRIDE(gen12_ccs_formats), + }, { + .modifier = I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS_CC, + .display_ver = { 12, 13 }, + .plane_caps = INTEL_PLANE_CAP_TILING_Y | INTEL_PLANE_CAP_CCS_RC_CC, + + .ccs.cc_planes = BIT(2), + .ccs.packed_aux_planes = BIT(1), + + FORMAT_OVERRIDE(gen12_ccs_cc_formats), + }, { + .modifier = I915_FORMAT_MOD_Yf_TILED_CCS, + .display_ver = { 9, 11 }, + .plane_caps = INTEL_PLANE_CAP_TILING_Yf | INTEL_PLANE_CAP_CCS_RC, + + .ccs.packed_aux_planes = BIT(1), + + FORMAT_OVERRIDE(skl_ccs_formats), + }, { + .modifier = I915_FORMAT_MOD_Y_TILED_CCS, + .display_ver = { 9, 11 }, + .plane_caps = INTEL_PLANE_CAP_TILING_Y | INTEL_PLANE_CAP_CCS_RC, + + .ccs.packed_aux_planes = BIT(1), + + FORMAT_OVERRIDE(skl_ccs_formats), + }, { + .modifier = I915_FORMAT_MOD_Yf_TILED, + .display_ver = { 9, 11 }, + .plane_caps = INTEL_PLANE_CAP_TILING_Yf, + }, { + .modifier = I915_FORMAT_MOD_Y_TILED, + .display_ver = { 9, 13 }, + .plane_caps = INTEL_PLANE_CAP_TILING_Y, + }, { + .modifier = I915_FORMAT_MOD_X_TILED, + .display_ver = DISPLAY_VER_ALL, + .plane_caps = INTEL_PLANE_CAP_TILING_X, + }, { + .modifier = DRM_FORMAT_MOD_LINEAR, + .display_ver = DISPLAY_VER_ALL, + }, +}; + +static const struct intel_modifier_desc *lookup_modifier_or_null(u64 modifier) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(intel_modifiers); i++) + if (intel_modifiers[i].modifier == modifier) + return &intel_modifiers[i]; + + return NULL; +} + +static const struct intel_modifier_desc *lookup_modifier(u64 modifier) +{ + const struct intel_modifier_desc *md = lookup_modifier_or_null(modifier); + + if (WARN_ON(!md)) + return &intel_modifiers[0]; + + return md; +} + +static const struct drm_format_info * +lookup_format_info(const struct drm_format_info formats[], + int num_formats, u32 format) +{ + int i; + + for (i = 0; i < num_formats; i++) { + if (formats[i].format == format) + return &formats[i]; + } + + return NULL; +} + +/** + * intel_fb_get_format_info: Get a modifier specific format information + * @cmd: FB add command structure + * + * Returns: + * Returns the format information for @cmd->pixel_format specific to @cmd->modifier[0], + * or %NULL if the modifier doesn't override the format. + */ +const struct drm_format_info * +intel_fb_get_format_info(const struct drm_mode_fb_cmd2 *cmd) +{ + const struct intel_modifier_desc *md = lookup_modifier_or_null(cmd->modifier[0]); + + if (!md || !md->formats) + return NULL; + + return lookup_format_info(md->formats, md->format_count, cmd->pixel_format); +} + +static bool plane_caps_contain_any(u8 caps, u8 mask) +{ + return caps & mask; +} + +static bool plane_caps_contain_all(u8 caps, u8 mask) +{ + return (caps & mask) == mask; +} + +/** + * intel_fb_is_ccs_modifier: Check if a modifier is a CCS modifier type + * @modifier: Modifier to check + * + * Returns: + * Returns %true if @modifier is a render, render with color clear or + * media compression modifier. + */ +bool intel_fb_is_ccs_modifier(u64 modifier) +{ + return plane_caps_contain_any(lookup_modifier(modifier)->plane_caps, + INTEL_PLANE_CAP_CCS_MASK); +} + +/** + * intel_fb_is_rc_ccs_cc_modifier: Check if a modifier is an RC CCS CC modifier type + * @modifier: Modifier to check + * + * Returns: + * Returns %true if @modifier is a render with color clear modifier. + */ +bool intel_fb_is_rc_ccs_cc_modifier(u64 modifier) +{ + return plane_caps_contain_any(lookup_modifier(modifier)->plane_caps, + INTEL_PLANE_CAP_CCS_RC_CC); +} + +/** + * intel_fb_is_mc_ccs_modifier: Check if a modifier is an MC CCS modifier type + * @modifier: Modifier to check + * + * Returns: + * Returns %true if @modifier is a media compression modifier. + */ +bool intel_fb_is_mc_ccs_modifier(u64 modifier) +{ + return plane_caps_contain_any(lookup_modifier(modifier)->plane_caps, + INTEL_PLANE_CAP_CCS_MC); +} + +static bool check_modifier_display_ver_range(const struct intel_modifier_desc *md, + u8 display_ver_from, u8 display_ver_until) +{ + return md->display_ver.from <= display_ver_until && + display_ver_from <= md->display_ver.until; +} + +static bool plane_has_modifier(struct drm_i915_private *i915, + u8 plane_caps, + const struct intel_modifier_desc *md) +{ + if (!IS_DISPLAY_VER(i915, md->display_ver.from, md->display_ver.until)) + return false; + + if (!plane_caps_contain_all(plane_caps, md->plane_caps)) + return false; + + return true; +} + +/** + * intel_fb_plane_get_modifiers: Get the modifiers for the given platform and plane capabilities + * @i915: i915 device instance + * @plane_caps: capabilities for the plane the modifiers are queried for + * + * Returns: + * Returns the list of modifiers allowed by the @i915 platform and @plane_caps. + * The caller must free the returned buffer. + */ +u64 *intel_fb_plane_get_modifiers(struct drm_i915_private *i915, + u8 plane_caps) +{ + u64 *list, *p; + int count = 1; /* +1 for invalid modifier terminator */ + int i; + + for (i = 0; i < ARRAY_SIZE(intel_modifiers); i++) { + if (plane_has_modifier(i915, plane_caps, &intel_modifiers[i])) + count++; + } + + list = kmalloc_array(count, sizeof(*list), GFP_KERNEL); + if (drm_WARN_ON(&i915->drm, !list)) + return NULL; + + p = list; + for (i = 0; i < ARRAY_SIZE(intel_modifiers); i++) { + if (plane_has_modifier(i915, plane_caps, &intel_modifiers[i])) + *p++ = intel_modifiers[i].modifier; + } + *p++ = DRM_FORMAT_MOD_INVALID; + + return list; +} + +/** + * intel_fb_plane_supports_modifier: Determine if a modifier is supported by the given plane + * @plane: Plane to check the modifier support for + * @modifier: The modifier to check the support for + * + * Returns: + * %true if the @modifier is supported on @plane. + */ +bool intel_fb_plane_supports_modifier(struct intel_plane *plane, u64 modifier) { - if (!is_ccs_modifier(fb->modifier)) + int i; + + for (i = 0; i < plane->base.modifier_count; i++) + if (plane->base.modifiers[i] == modifier) + return true; + + return false; +} + +static bool format_is_yuv_semiplanar(const struct intel_modifier_desc *md, + const struct drm_format_info *info) +{ + int yuv_planes; + + if (!info->is_yuv) return false; - return plane >= fb->format->num_planes / 2; + if (plane_caps_contain_any(md->plane_caps, INTEL_PLANE_CAP_CCS_MASK)) + yuv_planes = 4; + else + yuv_planes = 2; + + return info->num_planes == yuv_planes; } -bool is_gen12_ccs_plane(const struct drm_framebuffer *fb, int plane) +/** + * intel_format_info_is_yuv_semiplanar: Check if the given format is YUV semiplanar + * @info: format to check + * @modifier: modifier used with the format + * + * Returns: + * %true if @info / @modifier is YUV semiplanar. + */ +bool intel_format_info_is_yuv_semiplanar(const struct drm_format_info *info, + u64 modifier) { - return is_gen12_ccs_modifier(fb->modifier) && is_ccs_plane(fb, plane); + return format_is_yuv_semiplanar(lookup_modifier(modifier), info); } -bool is_gen12_ccs_cc_plane(const struct drm_framebuffer *fb, int plane) +static u8 ccs_aux_plane_mask(const struct intel_modifier_desc *md, + const struct drm_format_info *format) { - return fb->modifier == I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS_CC && - plane == 2; + if (format_is_yuv_semiplanar(md, format)) + return md->ccs.planar_aux_planes; + else + return md->ccs.packed_aux_planes; } -bool is_semiplanar_uv_plane(const struct drm_framebuffer *fb, int color_plane) +/** + * intel_fb_is_ccs_aux_plane: Check if a framebuffer color plane is a CCS AUX plane + * @fb: Framebuffer + * @color_plane: color plane index to check + * + * Returns: + * Returns %true if @fb's color plane at index @color_plane is a CCS AUX plane. + */ +bool intel_fb_is_ccs_aux_plane(const struct drm_framebuffer *fb, int color_plane) +{ + const struct intel_modifier_desc *md = lookup_modifier(fb->modifier); + + return ccs_aux_plane_mask(md, fb->format) & BIT(color_plane); +} + +/** + * intel_fb_is_gen12_ccs_aux_plane: Check if a framebuffer color plane is a GEN12 CCS AUX plane + * @fb: Framebuffer + * @color_plane: color plane index to check + * + * Returns: + * Returns %true if @fb's color plane at index @color_plane is a GEN12 CCS AUX plane. + */ +static bool intel_fb_is_gen12_ccs_aux_plane(const struct drm_framebuffer *fb, int color_plane) +{ + const struct intel_modifier_desc *md = lookup_modifier(fb->modifier); + + return check_modifier_display_ver_range(md, 12, 13) && + ccs_aux_plane_mask(md, fb->format) & BIT(color_plane); +} + +/** + * intel_fb_rc_ccs_cc_plane: Get the CCS CC color plane index for a framebuffer + * @fb: Framebuffer + * + * Returns: + * Returns the index of the color clear plane for @fb, or -1 if @fb is not a + * framebuffer using a render compression/color clear modifier. + */ +int intel_fb_rc_ccs_cc_plane(const struct drm_framebuffer *fb) +{ + const struct intel_modifier_desc *md = lookup_modifier(fb->modifier); + + if (!md->ccs.cc_planes) + return -1; + + drm_WARN_ON_ONCE(fb->dev, hweight8(md->ccs.cc_planes) > 1); + + return ilog2((int)md->ccs.cc_planes); +} + +static bool is_gen12_ccs_cc_plane(const struct drm_framebuffer *fb, int color_plane) +{ + return intel_fb_rc_ccs_cc_plane(fb) == color_plane; +} + +static bool is_semiplanar_uv_plane(const struct drm_framebuffer *fb, int color_plane) { return intel_format_info_is_yuv_semiplanar(fb->format, fb->modifier) && color_plane == 1; @@ -39,12 +481,13 @@ bool is_semiplanar_uv_plane(const struct drm_framebuffer *fb, int color_plane) bool is_surface_linear(const struct drm_framebuffer *fb, int color_plane) { return fb->modifier == DRM_FORMAT_MOD_LINEAR || - is_gen12_ccs_plane(fb, color_plane); + intel_fb_is_gen12_ccs_aux_plane(fb, color_plane) || + is_gen12_ccs_cc_plane(fb, color_plane); } int main_to_ccs_plane(const struct drm_framebuffer *fb, int main_plane) { - drm_WARN_ON(fb->dev, !is_ccs_modifier(fb->modifier) || + drm_WARN_ON(fb->dev, !intel_fb_is_ccs_modifier(fb->modifier) || (main_plane && main_plane >= fb->format->num_planes / 2)); return fb->format->num_planes / 2 + main_plane; @@ -52,7 +495,7 @@ int main_to_ccs_plane(const struct drm_framebuffer *fb, int main_plane) int skl_ccs_to_main_plane(const struct drm_framebuffer *fb, int ccs_plane) { - drm_WARN_ON(fb->dev, !is_ccs_modifier(fb->modifier) || + drm_WARN_ON(fb->dev, !intel_fb_is_ccs_modifier(fb->modifier) || ccs_plane < fb->format->num_planes / 2); if (is_gen12_ccs_cc_plane(fb, ccs_plane)) @@ -61,11 +504,20 @@ int skl_ccs_to_main_plane(const struct drm_framebuffer *fb, int ccs_plane) return ccs_plane - fb->format->num_planes / 2; } +static unsigned int gen12_ccs_aux_stride(struct intel_framebuffer *fb, int ccs_plane) +{ + int main_plane = skl_ccs_to_main_plane(&fb->base, ccs_plane); + unsigned int main_stride = fb->base.pitches[main_plane]; + unsigned int main_tile_width = intel_tile_width_bytes(&fb->base, main_plane); + + return DIV_ROUND_UP(main_stride, 4 * main_tile_width) * 64; +} + int skl_main_to_aux_plane(const struct drm_framebuffer *fb, int main_plane) { struct drm_i915_private *i915 = to_i915(fb->dev); - if (is_ccs_modifier(fb->modifier)) + if (intel_fb_is_ccs_modifier(fb->modifier)) return main_to_ccs_plane(fb, main_plane); else if (DISPLAY_VER(i915) < 11 && intel_format_info_is_yuv_semiplanar(fb->format, fb->modifier)) @@ -79,16 +531,71 @@ unsigned int intel_tile_size(const struct drm_i915_private *i915) return DISPLAY_VER(i915) == 2 ? 2048 : 4096; } -unsigned int intel_tile_height(const struct drm_framebuffer *fb, int color_plane) +unsigned int +intel_tile_width_bytes(const struct drm_framebuffer *fb, int color_plane) { - if (is_gen12_ccs_plane(fb, color_plane)) - return 1; + struct drm_i915_private *dev_priv = to_i915(fb->dev); + unsigned int cpp = fb->format->cpp[color_plane]; + + switch (fb->modifier) { + case DRM_FORMAT_MOD_LINEAR: + return intel_tile_size(dev_priv); + case I915_FORMAT_MOD_X_TILED: + if (DISPLAY_VER(dev_priv) == 2) + return 128; + else + return 512; + case I915_FORMAT_MOD_Y_TILED_CCS: + if (intel_fb_is_ccs_aux_plane(fb, color_plane)) + return 128; + fallthrough; + case I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS: + case I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS_CC: + case I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS: + if (intel_fb_is_ccs_aux_plane(fb, color_plane) || + is_gen12_ccs_cc_plane(fb, color_plane)) + return 64; + fallthrough; + case I915_FORMAT_MOD_Y_TILED: + if (DISPLAY_VER(dev_priv) == 2 || HAS_128_BYTE_Y_TILING(dev_priv)) + return 128; + else + return 512; + case I915_FORMAT_MOD_Yf_TILED_CCS: + if (intel_fb_is_ccs_aux_plane(fb, color_plane)) + return 128; + fallthrough; + case I915_FORMAT_MOD_Yf_TILED: + switch (cpp) { + case 1: + return 64; + case 2: + case 4: + return 128; + case 8: + case 16: + return 256; + default: + MISSING_CASE(cpp); + return cpp; + } + break; + default: + MISSING_CASE(fb->modifier); + return cpp; + } +} +unsigned int intel_tile_height(const struct drm_framebuffer *fb, int color_plane) +{ return intel_tile_size(to_i915(fb->dev)) / intel_tile_width_bytes(fb, color_plane); } -/* Return the tile dimensions in pixel units */ +/* + * Return the tile dimensions in pixel units, based on the (2 or 4 kbyte) GTT + * page tile size. + */ static void intel_tile_dims(const struct drm_framebuffer *fb, int color_plane, unsigned int *tile_width, unsigned int *tile_height) @@ -100,6 +607,21 @@ static void intel_tile_dims(const struct drm_framebuffer *fb, int color_plane, *tile_height = intel_tile_height(fb, color_plane); } +/* + * Return the tile dimensions in pixel units, based on the tile block size. + * The block covers the full GTT page sized tile on all tiled surfaces and + * it's a 64 byte portion of the tile on TGL+ CCS surfaces. + */ +static void intel_tile_block_dims(const struct drm_framebuffer *fb, int color_plane, + unsigned int *tile_width, + unsigned int *tile_height) +{ + intel_tile_dims(fb, color_plane, tile_width, tile_height); + + if (intel_fb_is_gen12_ccs_aux_plane(fb, color_plane)) + *tile_height = 1; +} + unsigned int intel_tile_row_size(const struct drm_framebuffer *fb, int color_plane) { unsigned int tile_width, tile_height; @@ -109,6 +631,44 @@ unsigned int intel_tile_row_size(const struct drm_framebuffer *fb, int color_pla return fb->pitches[color_plane] * tile_height; } +unsigned int +intel_fb_align_height(const struct drm_framebuffer *fb, + int color_plane, unsigned int height) +{ + unsigned int tile_height = intel_tile_height(fb, color_plane); + + return ALIGN(height, tile_height); +} + +static unsigned int intel_fb_modifier_to_tiling(u64 fb_modifier) +{ + u8 tiling_caps = lookup_modifier(fb_modifier)->plane_caps & + INTEL_PLANE_CAP_TILING_MASK; + + switch (tiling_caps) { + case INTEL_PLANE_CAP_TILING_Y: + return I915_TILING_Y; + case INTEL_PLANE_CAP_TILING_X: + return I915_TILING_X; + case INTEL_PLANE_CAP_TILING_Yf: + case INTEL_PLANE_CAP_TILING_NONE: + return I915_TILING_NONE; + default: + MISSING_CASE(tiling_caps); + return I915_TILING_NONE; + } +} + +static bool intel_modifier_uses_dpt(struct drm_i915_private *i915, u64 modifier) +{ + return DISPLAY_VER(i915) >= 13 && modifier != DRM_FORMAT_MOD_LINEAR; +} + +bool intel_fb_uses_dpt(const struct drm_framebuffer *fb) +{ + return fb && intel_modifier_uses_dpt(to_i915(fb->dev), fb->modifier); +} + unsigned int intel_cursor_alignment(const struct drm_i915_private *i915) { if (IS_I830(i915)) @@ -121,6 +681,70 @@ unsigned int intel_cursor_alignment(const struct drm_i915_private *i915) return 4 * 1024; } +static unsigned int intel_linear_alignment(const struct drm_i915_private *dev_priv) +{ + if (DISPLAY_VER(dev_priv) >= 9) + return 256 * 1024; + else if (IS_I965G(dev_priv) || IS_I965GM(dev_priv) || + IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) + return 128 * 1024; + else if (DISPLAY_VER(dev_priv) >= 4) + return 4 * 1024; + else + return 0; +} + +unsigned int intel_surf_alignment(const struct drm_framebuffer *fb, + int color_plane) +{ + struct drm_i915_private *dev_priv = to_i915(fb->dev); + + if (intel_fb_uses_dpt(fb)) + return 512 * 4096; + + /* AUX_DIST needs only 4K alignment */ + if (intel_fb_is_ccs_aux_plane(fb, color_plane)) + return 4096; + + if (is_semiplanar_uv_plane(fb, color_plane)) { + /* + * TODO: cross-check wrt. the bspec stride in bytes * 64 bytes + * alignment for linear UV planes on all platforms. + */ + if (DISPLAY_VER(dev_priv) >= 12) { + if (fb->modifier == DRM_FORMAT_MOD_LINEAR) + return intel_linear_alignment(dev_priv); + + return intel_tile_row_size(fb, color_plane); + } + + return 4096; + } + + drm_WARN_ON(&dev_priv->drm, color_plane != 0); + + switch (fb->modifier) { + case DRM_FORMAT_MOD_LINEAR: + return intel_linear_alignment(dev_priv); + case I915_FORMAT_MOD_X_TILED: + if (HAS_ASYNC_FLIPS(dev_priv)) + return 256 * 1024; + return 0; + case I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS: + case I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS: + case I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS_CC: + return 16 * 1024; + case I915_FORMAT_MOD_Y_TILED_CCS: + case I915_FORMAT_MOD_Yf_TILED_CCS: + case I915_FORMAT_MOD_Y_TILED: + case I915_FORMAT_MOD_Yf_TILED: + return 1 * 1024 * 1024; + default: + MISSING_CASE(fb->modifier); + return 0; + } +} + void intel_fb_plane_get_subsampling(int *hsub, int *vsub, const struct drm_framebuffer *fb, int color_plane) @@ -138,7 +762,7 @@ void intel_fb_plane_get_subsampling(int *hsub, int *vsub, * TODO: Deduct the subsampling from the char block for all CCS * formats and planes. */ - if (!is_gen12_ccs_plane(fb, color_plane)) { + if (!intel_fb_is_gen12_ccs_aux_plane(fb, color_plane)) { *hsub = fb->format->hsub; *vsub = fb->format->vsub; @@ -165,15 +789,18 @@ void intel_fb_plane_get_subsampling(int *hsub, int *vsub, static void intel_fb_plane_dims(const struct intel_framebuffer *fb, int color_plane, int *w, int *h) { - int main_plane = is_ccs_plane(&fb->base, color_plane) ? + int main_plane = intel_fb_is_ccs_aux_plane(&fb->base, color_plane) ? skl_ccs_to_main_plane(&fb->base, color_plane) : 0; + unsigned int main_width = fb->base.width; + unsigned int main_height = fb->base.height; int main_hsub, main_vsub; int hsub, vsub; intel_fb_plane_get_subsampling(&main_hsub, &main_vsub, &fb->base, main_plane); intel_fb_plane_get_subsampling(&hsub, &vsub, &fb->base, color_plane); - *w = fb->base.width / main_hsub / hsub; - *h = fb->base.height / main_vsub / vsub; + + *w = DIV_ROUND_UP(main_width, main_hsub * hsub); + *h = DIV_ROUND_UP(main_height, main_vsub * vsub); } static u32 intel_adjust_tile_offset(int *x, int *y, @@ -203,6 +830,20 @@ static u32 intel_adjust_tile_offset(int *x, int *y, return new_offset; } +static u32 intel_adjust_linear_offset(int *x, int *y, + unsigned int cpp, + unsigned int pitch, + u32 old_offset, + u32 new_offset) +{ + old_offset += *y * pitch + *x * cpp; + + *y = (old_offset - new_offset) / pitch; + *x = ((old_offset - new_offset) - *y * pitch) / cpp; + + return new_offset; +} + static u32 intel_adjust_aligned_offset(int *x, int *y, const struct drm_framebuffer *fb, int color_plane, @@ -233,10 +874,8 @@ static u32 intel_adjust_aligned_offset(int *x, int *y, tile_size, pitch_tiles, old_offset, new_offset); } else { - old_offset += *y * pitch + *x * cpp; - - *y = (old_offset - new_offset) / pitch; - *x = ((old_offset - new_offset) - *y * pitch) / cpp; + intel_adjust_linear_offset(x, y, cpp, pitch, + old_offset, new_offset); } return new_offset; @@ -253,7 +892,7 @@ u32 intel_plane_adjust_aligned_offset(int *x, int *y, { return intel_adjust_aligned_offset(x, y, state->hw.fb, color_plane, state->hw.rotation, - state->view.color_plane[color_plane].stride, + state->view.color_plane[color_plane].mapping_stride, old_offset, new_offset); } @@ -334,7 +973,7 @@ u32 intel_plane_compute_aligned_offset(int *x, int *y, struct drm_i915_private *i915 = to_i915(intel_plane->base.dev); const struct drm_framebuffer *fb = state->hw.fb; unsigned int rotation = state->hw.rotation; - int pitch = state->view.color_plane[color_plane].stride; + int pitch = state->view.color_plane[color_plane].mapping_stride; u32 alignment; if (intel_plane->id == PLANE_CURSOR) @@ -355,17 +994,9 @@ static int intel_fb_offset_to_xy(int *x, int *y, unsigned int height; u32 alignment; - /* - * All DPT color planes must be 512*4k aligned (the amount mapped by a - * single DPT page). For ADL_P CCS FBs this only works by requiring - * the allocated offsets to be 2MB aligned. Once supoort to remap - * such FBs is added we can remove this requirement, as then all the - * planes can be remapped to an aligned offset. - */ - if (IS_ALDERLAKE_P(i915) && is_ccs_modifier(fb->modifier)) - alignment = 512 * 4096; - else if (DISPLAY_VER(i915) >= 12 && - is_semiplanar_uv_plane(fb, color_plane)) + if (DISPLAY_VER(i915) >= 12 && + !intel_fb_needs_pot_stride_remap(to_intel_framebuffer(fb)) && + is_semiplanar_uv_plane(fb, color_plane)) alignment = intel_tile_row_size(fb, color_plane); else if (fb->modifier != DRM_FORMAT_MOD_LINEAR) alignment = intel_tile_size(i915); @@ -413,10 +1044,15 @@ static int intel_fb_check_ccs_xy(const struct drm_framebuffer *fb, int ccs_plane int ccs_x, ccs_y; int main_x, main_y; - if (!is_ccs_plane(fb, ccs_plane) || is_gen12_ccs_cc_plane(fb, ccs_plane)) + if (!intel_fb_is_ccs_aux_plane(fb, ccs_plane)) return 0; - intel_tile_dims(fb, ccs_plane, &tile_width, &tile_height); + /* + * While all the tile dimensions are based on a 2k or 4k GTT page size + * here the main and CCS coordinates must match only within a (64 byte + * on TGL+) block inside the tile. + */ + intel_tile_block_dims(fb, ccs_plane, &tile_width, &tile_height); intel_fb_plane_get_subsampling(&hsub, &vsub, fb, ccs_plane); tile_width *= hsub; @@ -471,7 +1107,7 @@ static bool intel_plane_can_remap(const struct intel_plane_state *plane_state) * The new CCS hash mode isn't compatible with remapping as * the virtual address of the pages affects the compressed data. */ - if (is_ccs_modifier(fb->modifier)) + if (intel_fb_is_ccs_modifier(fb->modifier)) return false; /* Linear needs a page aligned stride for remapping */ @@ -491,18 +1127,17 @@ bool intel_fb_needs_pot_stride_remap(const struct intel_framebuffer *fb) { struct drm_i915_private *i915 = to_i915(fb->base.dev); - return IS_ALDERLAKE_P(i915) && fb->base.modifier != DRM_FORMAT_MOD_LINEAR && - !is_ccs_modifier(fb->base.modifier); + return IS_ALDERLAKE_P(i915) && fb->base.modifier != DRM_FORMAT_MOD_LINEAR; } static int intel_fb_pitch(const struct intel_framebuffer *fb, int color_plane, unsigned int rotation) { if (drm_rotation_90_or_270(rotation)) - return fb->rotated_view.color_plane[color_plane].stride; + return fb->rotated_view.color_plane[color_plane].mapping_stride; else if (intel_fb_needs_pot_stride_remap(fb)) - return fb->remapped_view.color_plane[color_plane].stride; + return fb->remapped_view.color_plane[color_plane].mapping_stride; else - return fb->normal_view.color_plane[color_plane].stride; + return fb->normal_view.color_plane[color_plane].mapping_stride; } static bool intel_plane_needs_remap(const struct intel_plane_state *plane_state) @@ -612,14 +1247,30 @@ static unsigned int plane_view_dst_stride_tiles(const struct intel_framebuffer *fb, int color_plane, unsigned int pitch_tiles) { - if (intel_fb_needs_pot_stride_remap(fb)) + if (intel_fb_needs_pot_stride_remap(fb)) { /* * ADL_P, the only platform needing a POT stride has a minimum - * of 8 stride tiles. + * of 8 main surface tiles. */ return roundup_pow_of_two(max(pitch_tiles, 8u)); - else + } else { return pitch_tiles; + } +} + +static unsigned int +plane_view_scanout_stride(const struct intel_framebuffer *fb, int color_plane, + unsigned int tile_width, + unsigned int src_stride_tiles, unsigned int dst_stride_tiles) +{ + unsigned int stride_tiles; + + if (IS_ALDERLAKE_P(to_i915(fb->base.dev))) + stride_tiles = src_stride_tiles; + else + stride_tiles = dst_stride_tiles; + + return stride_tiles * tile_width * fb->base.format->cpp[color_plane]; } static unsigned int @@ -638,11 +1289,31 @@ plane_view_height_tiles(const struct intel_framebuffer *fb, int color_plane, return DIV_ROUND_UP(y + dims->height, dims->tile_height); } +static unsigned int +plane_view_linear_tiles(const struct intel_framebuffer *fb, int color_plane, + const struct fb_plane_view_dims *dims, + int x, int y) +{ + struct drm_i915_private *i915 = to_i915(fb->base.dev); + unsigned int size; + + size = (y + dims->height) * fb->base.pitches[color_plane] + + x * fb->base.format->cpp[color_plane]; + + return DIV_ROUND_UP(size, intel_tile_size(i915)); +} + #define assign_chk_ovf(i915, var, val) ({ \ drm_WARN_ON(&(i915)->drm, overflows_type(val, var)); \ (var) = (val); \ }) +#define assign_bfld_chk_ovf(i915, var, val) ({ \ + (var) = (val); \ + drm_WARN_ON(&(i915)->drm, (var) != (val)); \ + (var); \ +}) + static u32 calc_plane_remap_info(const struct intel_framebuffer *fb, int color_plane, const struct fb_plane_view_dims *dims, u32 obj_offset, u32 gtt_offset, int x, int y, @@ -655,14 +1326,28 @@ static u32 calc_plane_remap_info(const struct intel_framebuffer *fb, int color_p unsigned int tile_height = dims->tile_height; unsigned int tile_size = intel_tile_size(i915); struct drm_rect r; - u32 size; + u32 size = 0; + + assign_bfld_chk_ovf(i915, remap_info->offset, obj_offset); - assign_chk_ovf(i915, remap_info->offset, obj_offset); - assign_chk_ovf(i915, remap_info->src_stride, plane_view_src_stride_tiles(fb, color_plane, dims)); - assign_chk_ovf(i915, remap_info->width, plane_view_width_tiles(fb, color_plane, dims, x)); - assign_chk_ovf(i915, remap_info->height, plane_view_height_tiles(fb, color_plane, dims, y)); + if (intel_fb_is_gen12_ccs_aux_plane(&fb->base, color_plane)) { + remap_info->linear = 1; + + assign_chk_ovf(i915, remap_info->size, + plane_view_linear_tiles(fb, color_plane, dims, x, y)); + } else { + remap_info->linear = 0; + + assign_chk_ovf(i915, remap_info->src_stride, + plane_view_src_stride_tiles(fb, color_plane, dims)); + assign_chk_ovf(i915, remap_info->width, + plane_view_width_tiles(fb, color_plane, dims, x)); + assign_chk_ovf(i915, remap_info->height, + plane_view_height_tiles(fb, color_plane, dims, y)); + } if (view->gtt.type == I915_GGTT_VIEW_ROTATED) { + drm_WARN_ON(&i915->drm, remap_info->linear); check_array_bounds(i915, view->gtt.rotated.plane, color_plane); assign_chk_ovf(i915, remap_info->dst_stride, @@ -678,9 +1363,10 @@ static u32 calc_plane_remap_info(const struct intel_framebuffer *fb, int color_p color_plane_info->x = r.x1; color_plane_info->y = r.y1; - color_plane_info->stride = remap_info->dst_stride * tile_height; + color_plane_info->mapping_stride = remap_info->dst_stride * tile_height; + color_plane_info->scanout_stride = color_plane_info->mapping_stride; - size = remap_info->dst_stride * remap_info->width; + size += remap_info->dst_stride * remap_info->width; /* rotate the tile dimensions to match the GTT view */ swap(tile_width, tile_height); @@ -689,16 +1375,37 @@ static u32 calc_plane_remap_info(const struct intel_framebuffer *fb, int color_p check_array_bounds(i915, view->gtt.remapped.plane, color_plane); - assign_chk_ovf(i915, remap_info->dst_stride, - plane_view_dst_stride_tiles(fb, color_plane, remap_info->width)); + if (view->gtt.remapped.plane_alignment) { + unsigned int aligned_offset = ALIGN(gtt_offset, + view->gtt.remapped.plane_alignment); + + size += aligned_offset - gtt_offset; + gtt_offset = aligned_offset; + } color_plane_info->x = x; color_plane_info->y = y; - color_plane_info->stride = remap_info->dst_stride * tile_width * - fb->base.format->cpp[color_plane]; + if (remap_info->linear) { + color_plane_info->mapping_stride = fb->base.pitches[color_plane]; + color_plane_info->scanout_stride = color_plane_info->mapping_stride; - size = remap_info->dst_stride * remap_info->height; + size += remap_info->size; + } else { + unsigned int dst_stride = plane_view_dst_stride_tiles(fb, color_plane, + remap_info->width); + + assign_chk_ovf(i915, remap_info->dst_stride, dst_stride); + color_plane_info->mapping_stride = dst_stride * + tile_width * + fb->base.format->cpp[color_plane]; + color_plane_info->scanout_stride = + plane_view_scanout_stride(fb, color_plane, tile_width, + remap_info->src_stride, + dst_stride); + + size += dst_stride * remap_info->height; + } } /* @@ -706,10 +1413,16 @@ static u32 calc_plane_remap_info(const struct intel_framebuffer *fb, int color_p * the x/y offsets. x,y will hold the first pixel of the framebuffer * plane from the start of the remapped/rotated gtt mapping. */ - intel_adjust_tile_offset(&color_plane_info->x, &color_plane_info->y, - tile_width, tile_height, - tile_size, remap_info->dst_stride, - gtt_offset * tile_size, 0); + if (remap_info->linear) + intel_adjust_linear_offset(&color_plane_info->x, &color_plane_info->y, + fb->base.format->cpp[color_plane], + color_plane_info->mapping_stride, + gtt_offset * tile_size, 0); + else + intel_adjust_tile_offset(&color_plane_info->x, &color_plane_info->y, + tile_width, tile_height, + tile_size, remap_info->dst_stride, + gtt_offset * tile_size, 0); return size; } @@ -722,15 +1435,10 @@ calc_plane_normal_size(const struct intel_framebuffer *fb, int color_plane, const struct fb_plane_view_dims *dims, int x, int y) { - struct drm_i915_private *i915 = to_i915(fb->base.dev); unsigned int tiles; if (is_surface_linear(&fb->base, color_plane)) { - unsigned int size; - - size = (y + dims->height) * fb->base.pitches[color_plane] + - x * fb->base.format->cpp[color_plane]; - tiles = DIV_ROUND_UP(size, intel_tile_size(i915)); + tiles = plane_view_linear_tiles(fb, color_plane, dims, x, y); } else { tiles = plane_view_src_stride_tiles(fb, color_plane, dims) * plane_view_height_tiles(fb, color_plane, dims, y); @@ -745,10 +1453,14 @@ calc_plane_normal_size(const struct intel_framebuffer *fb, int color_plane, return tiles; } -static void intel_fb_view_init(struct intel_fb_view *view, enum i915_ggtt_view_type view_type) +static void intel_fb_view_init(struct drm_i915_private *i915, struct intel_fb_view *view, + enum i915_ggtt_view_type view_type) { memset(view, 0, sizeof(*view)); view->gtt.type = view_type; + + if (view_type == I915_GGTT_VIEW_REMAPPED && IS_ALDERLAKE_P(i915)) + view->gtt.remapped.plane_alignment = SZ_2M / PAGE_SIZE; } bool intel_fb_supports_90_270_rotation(const struct intel_framebuffer *fb) @@ -769,16 +1481,16 @@ int intel_fill_fb_info(struct drm_i915_private *i915, struct intel_framebuffer * int i, num_planes = fb->base.format->num_planes; unsigned int tile_size = intel_tile_size(i915); - intel_fb_view_init(&fb->normal_view, I915_GGTT_VIEW_NORMAL); + intel_fb_view_init(i915, &fb->normal_view, I915_GGTT_VIEW_NORMAL); drm_WARN_ON(&i915->drm, intel_fb_supports_90_270_rotation(fb) && intel_fb_needs_pot_stride_remap(fb)); if (intel_fb_supports_90_270_rotation(fb)) - intel_fb_view_init(&fb->rotated_view, I915_GGTT_VIEW_ROTATED); + intel_fb_view_init(i915, &fb->rotated_view, I915_GGTT_VIEW_ROTATED); if (intel_fb_needs_pot_stride_remap(fb)) - intel_fb_view_init(&fb->remapped_view, I915_GGTT_VIEW_REMAPPED); + intel_fb_view_init(i915, &fb->remapped_view, I915_GGTT_VIEW_REMAPPED); for (i = 0; i < num_planes; i++) { struct fb_plane_view_dims view_dims; @@ -815,7 +1527,9 @@ int intel_fill_fb_info(struct drm_i915_private *i915, struct intel_framebuffer * */ fb->normal_view.color_plane[i].x = x; fb->normal_view.color_plane[i].y = y; - fb->normal_view.color_plane[i].stride = fb->base.pitches[i]; + fb->normal_view.color_plane[i].mapping_stride = fb->base.pitches[i]; + fb->normal_view.color_plane[i].scanout_stride = + fb->normal_view.color_plane[i].mapping_stride; offset = calc_plane_aligned_offset(fb, i, &x, &y); @@ -856,7 +1570,7 @@ static void intel_plane_remap_gtt(struct intel_plane_state *plane_state) unsigned int src_w, src_h; u32 gtt_offset = 0; - intel_fb_view_init(&plane_state->view, + intel_fb_view_init(i915, &plane_state->view, drm_rotation_90_or_270(rotation) ? I915_GGTT_VIEW_ROTATED : I915_GGTT_VIEW_REMAPPED); @@ -865,7 +1579,7 @@ static void intel_plane_remap_gtt(struct intel_plane_state *plane_state) src_w = drm_rect_width(&plane_state->uapi.src) >> 16; src_h = drm_rect_height(&plane_state->uapi.src) >> 16; - drm_WARN_ON(&i915->drm, is_ccs_modifier(fb->modifier)); + drm_WARN_ON(&i915->drm, intel_fb_is_ccs_modifier(fb->modifier)); /* Make src coordinates relative to the viewport */ drm_rect_translate(&plane_state->uapi.src, @@ -918,6 +1632,71 @@ void intel_fb_fill_view(const struct intel_framebuffer *fb, unsigned int rotatio *view = fb->normal_view; } +static +u32 intel_fb_max_stride(struct drm_i915_private *dev_priv, + u32 pixel_format, u64 modifier) +{ + /* + * Arbitrary limit for gen4+ chosen to match the + * render engine max stride. + * + * The new CCS hash mode makes remapping impossible + */ + if (DISPLAY_VER(dev_priv) < 4 || intel_fb_is_ccs_modifier(modifier) || + intel_modifier_uses_dpt(dev_priv, modifier)) + return intel_plane_fb_max_stride(dev_priv, pixel_format, modifier); + else if (DISPLAY_VER(dev_priv) >= 7) + return 256 * 1024; + else + return 128 * 1024; +} + +static u32 +intel_fb_stride_alignment(const struct drm_framebuffer *fb, int color_plane) +{ + struct drm_i915_private *dev_priv = to_i915(fb->dev); + u32 tile_width; + + if (is_surface_linear(fb, color_plane)) { + u32 max_stride = intel_plane_fb_max_stride(dev_priv, + fb->format->format, + fb->modifier); + + /* + * To make remapping with linear generally feasible + * we need the stride to be page aligned. + */ + if (fb->pitches[color_plane] > max_stride && + !intel_fb_is_ccs_modifier(fb->modifier)) + return intel_tile_size(dev_priv); + else + return 64; + } + + tile_width = intel_tile_width_bytes(fb, color_plane); + if (intel_fb_is_ccs_modifier(fb->modifier)) { + /* + * On TGL the surface stride must be 4 tile aligned, mapped by + * one 64 byte cacheline on the CCS AUX surface. + */ + if (DISPLAY_VER(dev_priv) >= 12) + tile_width *= 4; + /* + * Display WA #0531: skl,bxt,kbl,glk + * + * Render decompression and plane width > 3840 + * combined with horizontal panning requires the + * plane stride to be a multiple of 4. We'll just + * require the entire fb to accommodate that to avoid + * potential runtime errors at plane configuration time. + */ + else if ((DISPLAY_VER(dev_priv) == 9 || IS_GEMINILAKE(dev_priv)) && + color_plane == 0 && fb->width > 3840) + tile_width *= 4; + } + return tile_width; +} + static int intel_plane_check_stride(const struct intel_plane_state *plane_state) { struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); @@ -936,7 +1715,7 @@ static int intel_plane_check_stride(const struct intel_plane_state *plane_state) return 0; /* FIXME other color planes? */ - stride = plane_state->view.color_plane[0].stride; + stride = plane_state->view.color_plane[0].mapping_stride; max_stride = plane->max_stride(plane, fb->format->format, fb->modifier, rotation); @@ -981,3 +1760,257 @@ int intel_plane_compute_gtt(struct intel_plane_state *plane_state) return intel_plane_check_stride(plane_state); } + +static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb) +{ + struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); + + drm_framebuffer_cleanup(fb); + + if (intel_fb_uses_dpt(fb)) + intel_dpt_destroy(intel_fb->dpt_vm); + + intel_frontbuffer_put(intel_fb->frontbuffer); + + kfree(intel_fb); +} + +static int intel_user_framebuffer_create_handle(struct drm_framebuffer *fb, + struct drm_file *file, + unsigned int *handle) +{ + struct drm_i915_gem_object *obj = intel_fb_obj(fb); + struct drm_i915_private *i915 = to_i915(obj->base.dev); + + if (i915_gem_object_is_userptr(obj)) { + drm_dbg(&i915->drm, + "attempting to use a userptr for a framebuffer, denied\n"); + return -EINVAL; + } + + return drm_gem_handle_create(file, &obj->base, handle); +} + +static int intel_user_framebuffer_dirty(struct drm_framebuffer *fb, + struct drm_file *file, + unsigned int flags, unsigned int color, + struct drm_clip_rect *clips, + unsigned int num_clips) +{ + struct drm_i915_gem_object *obj = intel_fb_obj(fb); + + i915_gem_object_flush_if_display(obj); + intel_frontbuffer_flush(to_intel_frontbuffer(fb), ORIGIN_DIRTYFB); + + return 0; +} + +static const struct drm_framebuffer_funcs intel_fb_funcs = { + .destroy = intel_user_framebuffer_destroy, + .create_handle = intel_user_framebuffer_create_handle, + .dirty = intel_user_framebuffer_dirty, +}; + +int intel_framebuffer_init(struct intel_framebuffer *intel_fb, + struct drm_i915_gem_object *obj, + struct drm_mode_fb_cmd2 *mode_cmd) +{ + struct drm_i915_private *dev_priv = to_i915(obj->base.dev); + struct drm_framebuffer *fb = &intel_fb->base; + u32 max_stride; + unsigned int tiling, stride; + int ret = -EINVAL; + int i; + + intel_fb->frontbuffer = intel_frontbuffer_get(obj); + if (!intel_fb->frontbuffer) + return -ENOMEM; + + i915_gem_object_lock(obj, NULL); + tiling = i915_gem_object_get_tiling(obj); + stride = i915_gem_object_get_stride(obj); + i915_gem_object_unlock(obj); + + if (mode_cmd->flags & DRM_MODE_FB_MODIFIERS) { + /* + * If there's a fence, enforce that + * the fb modifier and tiling mode match. + */ + if (tiling != I915_TILING_NONE && + tiling != intel_fb_modifier_to_tiling(mode_cmd->modifier[0])) { + drm_dbg_kms(&dev_priv->drm, + "tiling_mode doesn't match fb modifier\n"); + goto err; + } + } else { + if (tiling == I915_TILING_X) { + mode_cmd->modifier[0] = I915_FORMAT_MOD_X_TILED; + } else if (tiling == I915_TILING_Y) { + drm_dbg_kms(&dev_priv->drm, + "No Y tiling for legacy addfb\n"); + goto err; + } + } + + if (!drm_any_plane_has_format(&dev_priv->drm, + mode_cmd->pixel_format, + mode_cmd->modifier[0])) { + drm_dbg_kms(&dev_priv->drm, + "unsupported pixel format %p4cc / modifier 0x%llx\n", + &mode_cmd->pixel_format, mode_cmd->modifier[0]); + goto err; + } + + /* + * gen2/3 display engine uses the fence if present, + * so the tiling mode must match the fb modifier exactly. + */ + if (DISPLAY_VER(dev_priv) < 4 && + tiling != intel_fb_modifier_to_tiling(mode_cmd->modifier[0])) { + drm_dbg_kms(&dev_priv->drm, + "tiling_mode must match fb modifier exactly on gen2/3\n"); + goto err; + } + + max_stride = intel_fb_max_stride(dev_priv, mode_cmd->pixel_format, + mode_cmd->modifier[0]); + if (mode_cmd->pitches[0] > max_stride) { + drm_dbg_kms(&dev_priv->drm, + "%s pitch (%u) must be at most %d\n", + mode_cmd->modifier[0] != DRM_FORMAT_MOD_LINEAR ? + "tiled" : "linear", + mode_cmd->pitches[0], max_stride); + goto err; + } + + /* + * If there's a fence, enforce that + * the fb pitch and fence stride match. + */ + if (tiling != I915_TILING_NONE && mode_cmd->pitches[0] != stride) { + drm_dbg_kms(&dev_priv->drm, + "pitch (%d) must match tiling stride (%d)\n", + mode_cmd->pitches[0], stride); + goto err; + } + + /* FIXME need to adjust LINOFF/TILEOFF accordingly. */ + if (mode_cmd->offsets[0] != 0) { + drm_dbg_kms(&dev_priv->drm, + "plane 0 offset (0x%08x) must be 0\n", + mode_cmd->offsets[0]); + goto err; + } + + drm_helper_mode_fill_fb_struct(&dev_priv->drm, fb, mode_cmd); + + for (i = 0; i < fb->format->num_planes; i++) { + u32 stride_alignment; + + if (mode_cmd->handles[i] != mode_cmd->handles[0]) { + drm_dbg_kms(&dev_priv->drm, "bad plane %d handle\n", + i); + goto err; + } + + stride_alignment = intel_fb_stride_alignment(fb, i); + if (fb->pitches[i] & (stride_alignment - 1)) { + drm_dbg_kms(&dev_priv->drm, + "plane %d pitch (%d) must be at least %u byte aligned\n", + i, fb->pitches[i], stride_alignment); + goto err; + } + + if (intel_fb_is_gen12_ccs_aux_plane(fb, i)) { + int ccs_aux_stride = gen12_ccs_aux_stride(intel_fb, i); + + if (fb->pitches[i] != ccs_aux_stride) { + drm_dbg_kms(&dev_priv->drm, + "ccs aux plane %d pitch (%d) must be %d\n", + i, + fb->pitches[i], ccs_aux_stride); + goto err; + } + } + + fb->obj[i] = &obj->base; + } + + ret = intel_fill_fb_info(dev_priv, intel_fb); + if (ret) + goto err; + + if (intel_fb_uses_dpt(fb)) { + struct i915_address_space *vm; + + vm = intel_dpt_create(intel_fb); + if (IS_ERR(vm)) { + ret = PTR_ERR(vm); + goto err; + } + + intel_fb->dpt_vm = vm; + } + + ret = drm_framebuffer_init(&dev_priv->drm, fb, &intel_fb_funcs); + if (ret) { + drm_err(&dev_priv->drm, "framebuffer init failed %d\n", ret); + goto err; + } + + return 0; + +err: + intel_frontbuffer_put(intel_fb->frontbuffer); + return ret; +} + +struct drm_framebuffer * +intel_user_framebuffer_create(struct drm_device *dev, + struct drm_file *filp, + const struct drm_mode_fb_cmd2 *user_mode_cmd) +{ + struct drm_framebuffer *fb; + struct drm_i915_gem_object *obj; + struct drm_mode_fb_cmd2 mode_cmd = *user_mode_cmd; + struct drm_i915_private *i915; + + obj = i915_gem_object_lookup(filp, mode_cmd.handles[0]); + if (!obj) + return ERR_PTR(-ENOENT); + + /* object is backed with LMEM for discrete */ + i915 = to_i915(obj->base.dev); + if (HAS_LMEM(i915) && !i915_gem_object_can_migrate(obj, INTEL_REGION_LMEM)) { + /* object is "remote", not in local memory */ + i915_gem_object_put(obj); + return ERR_PTR(-EREMOTE); + } + + fb = intel_framebuffer_create(obj, &mode_cmd); + i915_gem_object_put(obj); + + return fb; +} + +struct drm_framebuffer * +intel_framebuffer_create(struct drm_i915_gem_object *obj, + struct drm_mode_fb_cmd2 *mode_cmd) +{ + struct intel_framebuffer *intel_fb; + int ret; + + intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL); + if (!intel_fb) + return ERR_PTR(-ENOMEM); + + ret = intel_framebuffer_init(intel_fb, obj, mode_cmd); + if (ret) + goto err; + + return &intel_fb->base; + +err: + kfree(intel_fb); + return ERR_PTR(ret); +} |
