diff options
Diffstat (limited to 'drivers/gpu/drm/i915/display/intel_fb.c')
| -rw-r--r-- | drivers/gpu/drm/i915/display/intel_fb.c | 1772 |
1 files changed, 1593 insertions, 179 deletions
diff --git a/drivers/gpu/drm/i915/display/intel_fb.c b/drivers/gpu/drm/i915/display/intel_fb.c index c60a81a81c09..b34b4961fe1c 100644 --- a/drivers/gpu/drm/i915/display/intel_fb.c +++ b/drivers/gpu/drm/i915/display/intel_fb.c @@ -3,48 +3,730 @@ * Copyright © 2021 Intel Corporation */ -#include <drm/drm_framebuffer.h> +#include <linux/dma-fence.h> +#include <linux/dma-resv.h> +#include <drm/drm_blend.h> +#include <drm/drm_gem.h> +#include <drm/drm_modeset_helper.h> +#include <drm/drm_print.h> + +#include "intel_bo.h" #include "intel_display.h" +#include "intel_display_core.h" #include "intel_display_types.h" +#include "intel_display_utils.h" +#include "intel_dpt.h" #include "intel_fb.h" +#include "intel_fb_bo.h" +#include "intel_frontbuffer.h" +#include "intel_panic.h" +#include "intel_plane.h" + +#define check_array_bounds(display, a, i) drm_WARN_ON((display)->drm, (i) >= ARRAY_SIZE(a)) + +/* + * 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, }, + { .format = DRM_FORMAT_XRGB2101010, .depth = 30, .num_planes = 2, + .cpp = { 4, 1, }, .hsub = 8, .vsub = 16, }, + { .format = DRM_FORMAT_XBGR2101010, .depth = 30, .num_planes = 2, + .cpp = { 4, 1, }, .hsub = 8, .vsub = 16, }, + { .format = DRM_FORMAT_ARGB2101010, .depth = 30, .num_planes = 2, + .cpp = { 4, 1, }, .hsub = 8, .vsub = 16, .has_alpha = true, }, + { .format = DRM_FORMAT_ABGR2101010, .depth = 30, .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_XRGB2101010, .depth = 30, .num_planes = 2, + .char_per_block = { 4, 1 }, .block_w = { 1, 2 }, .block_h = { 1, 1 }, + .hsub = 1, .vsub = 1, }, + { .format = DRM_FORMAT_XBGR2101010, .depth = 30, .num_planes = 2, + .char_per_block = { 4, 1 }, .block_w = { 1, 2 }, .block_h = { 1, 1 }, + .hsub = 1, .vsub = 1, }, + { .format = DRM_FORMAT_ARGB2101010, .depth = 30, .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_ABGR2101010, .depth = 30, .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_XRGB16161616F, .depth = 0, .num_planes = 2, + .char_per_block = { 8, 1 }, .block_w = { 1, 1 }, .block_h = { 1, 1 }, + .hsub = 1, .vsub = 1, }, + { .format = DRM_FORMAT_XBGR16161616F, .depth = 0, .num_planes = 2, + .char_per_block = { 8, 1 }, .block_w = { 1, 1 }, .block_h = { 1, 1 }, + .hsub = 1, .vsub = 1, }, + { .format = DRM_FORMAT_ARGB16161616F, .depth = 0, .num_planes = 2, + .char_per_block = { 8, 1 }, .block_w = { 1, 1 }, .block_h = { 1, 1 }, + .hsub = 1, .vsub = 1, .has_alpha = true }, + { .format = DRM_FORMAT_ABGR16161616F, .depth = 0, .num_planes = 2, + .char_per_block = { 8, 1 }, .block_w = { 1, 1 }, .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, 0 }, .block_h = { 1, 1, 0 }, + .hsub = 1, .vsub = 1, }, + { .format = DRM_FORMAT_XBGR8888, .depth = 24, .num_planes = 3, + .char_per_block = { 4, 1, 0 }, .block_w = { 1, 2, 0 }, .block_h = { 1, 1, 0 }, + .hsub = 1, .vsub = 1, }, + { .format = DRM_FORMAT_ARGB8888, .depth = 32, .num_planes = 3, + .char_per_block = { 4, 1, 0 }, .block_w = { 1, 2, 0 }, .block_h = { 1, 1, 0 }, + .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, 0 }, .block_h = { 1, 1, 0 }, + .hsub = 1, .vsub = 1, .has_alpha = true }, + { .format = DRM_FORMAT_XRGB2101010, .depth = 30, .num_planes = 3, + .char_per_block = { 4, 1, 0 }, .block_w = { 1, 2, 0 }, .block_h = { 1, 1, 0 }, + .hsub = 1, .vsub = 1, }, + { .format = DRM_FORMAT_XBGR2101010, .depth = 30, .num_planes = 3, + .char_per_block = { 4, 1, 0 }, .block_w = { 1, 2, 0 }, .block_h = { 1, 1, 0 }, + .hsub = 1, .vsub = 1, }, + { .format = DRM_FORMAT_ARGB2101010, .depth = 30, .num_planes = 3, + .char_per_block = { 4, 1, 0 }, .block_w = { 1, 2, 0 }, .block_h = { 1, 1, 0 }, + .hsub = 1, .vsub = 1, .has_alpha = true }, + { .format = DRM_FORMAT_ABGR2101010, .depth = 30, .num_planes = 3, + .char_per_block = { 4, 1, 0 }, .block_w = { 1, 2, 0 }, .block_h = { 1, 1, 0 }, + .hsub = 1, .vsub = 1, .has_alpha = true }, + { .format = DRM_FORMAT_XRGB16161616F, .depth = 0, .num_planes = 3, + .char_per_block = { 8, 1, 0 }, .block_w = { 1, 1, 0 }, .block_h = { 1, 1, 0 }, + .hsub = 1, .vsub = 1, }, + { .format = DRM_FORMAT_XBGR16161616F, .depth = 0, .num_planes = 3, + .char_per_block = { 8, 1, 0 }, .block_w = { 1, 1, 0 }, .block_h = { 1, 1, 0 }, + .hsub = 1, .vsub = 1, }, + { .format = DRM_FORMAT_ARGB16161616F, .depth = 0, .num_planes = 3, + .char_per_block = { 8, 1, 0 }, .block_w = { 1, 1, 0 }, .block_h = { 1, 1, 0 }, + .hsub = 1, .vsub = 1, .has_alpha = true }, + { .format = DRM_FORMAT_ABGR16161616F, .depth = 0, .num_planes = 3, + .char_per_block = { 8, 1, 0 }, .block_w = { 1, 1, 0 }, .block_h = { 1, 1, 0 }, + .hsub = 1, .vsub = 1, .has_alpha = true }, +}; + +static const struct drm_format_info gen12_flat_ccs_cc_formats[] = { + { .format = DRM_FORMAT_XRGB8888, .depth = 24, .num_planes = 2, + .char_per_block = { 4, 0 }, .block_w = { 1, 0 }, .block_h = { 1, 0 }, + .hsub = 1, .vsub = 1, }, + { .format = DRM_FORMAT_XBGR8888, .depth = 24, .num_planes = 2, + .char_per_block = { 4, 0 }, .block_w = { 1, 0 }, .block_h = { 1, 0 }, + .hsub = 1, .vsub = 1, }, + { .format = DRM_FORMAT_ARGB8888, .depth = 32, .num_planes = 2, + .char_per_block = { 4, 0 }, .block_w = { 1, 0 }, .block_h = { 1, 0 }, + .hsub = 1, .vsub = 1, .has_alpha = true }, + { .format = DRM_FORMAT_ABGR8888, .depth = 32, .num_planes = 2, + .char_per_block = { 4, 0 }, .block_w = { 1, 0 }, .block_h = { 1, 0 }, + .hsub = 1, .vsub = 1, .has_alpha = true }, + { .format = DRM_FORMAT_XRGB2101010, .depth = 30, .num_planes = 2, + .char_per_block = { 4, 0 }, .block_w = { 1, 0 }, .block_h = { 1, 0 }, + .hsub = 1, .vsub = 1, }, + { .format = DRM_FORMAT_XBGR2101010, .depth = 30, .num_planes = 2, + .char_per_block = { 4, 0 }, .block_w = { 1, 0 }, .block_h = { 1, 0 }, + .hsub = 1, .vsub = 1, }, + { .format = DRM_FORMAT_ARGB2101010, .depth = 30, .num_planes = 2, + .char_per_block = { 4, 0 }, .block_w = { 1, 0 }, .block_h = { 1, 0 }, + .hsub = 1, .vsub = 1, .has_alpha = true }, + { .format = DRM_FORMAT_ABGR2101010, .depth = 30, .num_planes = 2, + .char_per_block = { 4, 0 }, .block_w = { 1, 0 }, .block_h = { 1, 0 }, + .hsub = 1, .vsub = 1, .has_alpha = true }, + { .format = DRM_FORMAT_XRGB16161616F, .depth = 0, .num_planes = 2, + .char_per_block = { 8, 0 }, .block_w = { 1, 0 }, .block_h = { 1, 0 }, + .hsub = 1, .vsub = 1, }, + { .format = DRM_FORMAT_XBGR16161616F, .depth = 0, .num_planes = 2, + .char_per_block = { 8, 0 }, .block_w = { 1, 0 }, .block_h = { 1, 0 }, + .hsub = 1, .vsub = 1, }, + { .format = DRM_FORMAT_ARGB16161616F, .depth = 0, .num_planes = 2, + .char_per_block = { 8, 0 }, .block_w = { 1, 0 }, .block_h = { 1, 0 }, + .hsub = 1, .vsub = 1, .has_alpha = true }, + { .format = DRM_FORMAT_ABGR16161616F, .depth = 0, .num_planes = 2, + .char_per_block = { 8, 0 }, .block_w = { 1, 0 }, .block_h = { 1, 0 }, + .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 | \ + INTEL_PLANE_CAP_TILING_4) +#define INTEL_PLANE_CAP_TILING_NONE 0 + +static const struct intel_modifier_desc intel_modifiers[] = { + { + .modifier = I915_FORMAT_MOD_4_TILED_LNL_CCS, + .display_ver = { 20, -1 }, + .plane_caps = INTEL_PLANE_CAP_TILING_4, + }, { + .modifier = I915_FORMAT_MOD_4_TILED_BMG_CCS, + .display_ver = { 14, -1 }, + .plane_caps = INTEL_PLANE_CAP_TILING_4 | INTEL_PLANE_CAP_NEED64K_PHYS, + }, { + .modifier = I915_FORMAT_MOD_4_TILED_MTL_MC_CCS, + .display_ver = { 14, 14 }, + .plane_caps = INTEL_PLANE_CAP_TILING_4 | 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_4_TILED_MTL_RC_CCS, + .display_ver = { 14, 14 }, + .plane_caps = INTEL_PLANE_CAP_TILING_4 | INTEL_PLANE_CAP_CCS_RC, + + .ccs.packed_aux_planes = BIT(1), + + FORMAT_OVERRIDE(gen12_ccs_formats), + }, { + .modifier = I915_FORMAT_MOD_4_TILED_MTL_RC_CCS_CC, + .display_ver = { 14, 14 }, + .plane_caps = INTEL_PLANE_CAP_TILING_4 | 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_4_TILED_DG2_MC_CCS, + .display_ver = { 13, 13 }, + .plane_caps = INTEL_PLANE_CAP_TILING_4 | INTEL_PLANE_CAP_CCS_MC, + }, { + .modifier = I915_FORMAT_MOD_4_TILED_DG2_RC_CCS_CC, + .display_ver = { 13, 13 }, + .plane_caps = INTEL_PLANE_CAP_TILING_4 | INTEL_PLANE_CAP_CCS_RC_CC, + + .ccs.cc_planes = BIT(1), + + FORMAT_OVERRIDE(gen12_flat_ccs_cc_formats), + }, { + .modifier = I915_FORMAT_MOD_4_TILED_DG2_RC_CCS, + .display_ver = { 13, 13 }, + .plane_caps = INTEL_PLANE_CAP_TILING_4 | INTEL_PLANE_CAP_CCS_RC, + }, { + .modifier = I915_FORMAT_MOD_4_TILED, + .display_ver = { 13, -1 }, + .plane_caps = INTEL_PLANE_CAP_TILING_4, + }, { + .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 = { 0, 29 }, + .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; +} + +unsigned int intel_fb_modifier_to_tiling(u64 fb_modifier) +{ + const struct intel_modifier_desc *md; + u8 tiling_caps; + + md = lookup_modifier_or_null(fb_modifier); + if (!md) + return I915_TILING_NONE; + + tiling_caps = lookup_modifier_or_null(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_4: + 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; + } +} + +/** + * intel_fb_get_format_info: Get a modifier specific format information + * @pixel_format: pixel format + * @modifier: modifier + * + * Returns: + * Returns the format information for @pixel_format specific to @modifier, + * or %NULL if the modifier doesn't override the format. + */ +const struct drm_format_info * +intel_fb_get_format_info(u32 pixel_format, u64 modifier) +{ + const struct intel_modifier_desc *md = lookup_modifier_or_null(modifier); + + if (!md || !md->formats) + return NULL; + + return lookup_format_info(md->formats, md->format_count, 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_tiled_modifier: Check if a modifier is a tiled modifier type + * @modifier: Modifier to check + * + * Returns: + * Returns %true if @modifier is a tiled modifier. + */ +bool intel_fb_is_tiled_modifier(u64 modifier) +{ + return plane_caps_contain_any(lookup_modifier(modifier)->plane_caps, + INTEL_PLANE_CAP_TILING_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); +} -#define check_array_bounds(i915, a, i) drm_WARN_ON(&(i915)->drm, (i) >= ARRAY_SIZE(a)) +/** + * 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); +} -bool is_ccs_plane(const struct drm_framebuffer *fb, int plane) +/** + * intel_fb_needs_64k_phys: Check if modifier requires 64k physical placement. + * @modifier: Modifier to check + * + * Returns: + * Returns %true if @modifier requires 64k aligned physical pages. + */ +bool intel_fb_needs_64k_phys(u64 modifier) { - if (!is_ccs_modifier(fb->modifier)) + const struct intel_modifier_desc *md = lookup_modifier_or_null(modifier); + + if (!md) return false; - return plane >= fb->format->num_planes / 2; + return plane_caps_contain_any(md->plane_caps, + INTEL_PLANE_CAP_NEED64K_PHYS); } -bool is_gen12_ccs_plane(const struct drm_framebuffer *fb, int plane) +/** + * intel_fb_is_tile4_modifier: Check if a modifier is a tile4 modifier type + * @modifier: Modifier to check + * + * Returns: + * Returns %true if @modifier is a tile4 modifier. + */ +bool intel_fb_is_tile4_modifier(u64 modifier) { - return is_gen12_ccs_modifier(fb->modifier) && is_ccs_plane(fb, plane); + return plane_caps_contain_any(lookup_modifier(modifier)->plane_caps, + INTEL_PLANE_CAP_TILING_4); } -bool is_gen12_ccs_cc_plane(const struct drm_framebuffer *fb, int plane) +static bool check_modifier_display_ver_range(const struct intel_modifier_desc *md, + u8 display_ver_from, u8 display_ver_until) { - return fb->modifier == I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS_CC && - plane == 2; + return md->display_ver.from <= display_ver_until && + display_ver_from <= md->display_ver.until; } -bool is_semiplanar_uv_plane(const struct drm_framebuffer *fb, int color_plane) +static bool plane_has_modifier(struct intel_display *display, + u8 plane_caps, + const struct intel_modifier_desc *md) { - return intel_format_info_is_yuv_semiplanar(fb->format, fb->modifier) && - color_plane == 1; + if (!IS_DISPLAY_VER(display, md->display_ver.from, md->display_ver.until)) + return false; + + if (!plane_caps_contain_all(plane_caps, md->plane_caps)) + return false; + + /* + * Separate AuxCCS and Flat CCS modifiers to be run only on platforms + * where supported. + */ + if (intel_fb_is_ccs_modifier(md->modifier) && + HAS_AUX_CCS(display) != !!md->ccs.packed_aux_planes) + return false; + + if (md->modifier == I915_FORMAT_MOD_4_TILED_BMG_CCS && + (DISPLAY_VER(display) < 14 || !display->platform.dgfx)) + return false; + + if (md->modifier == I915_FORMAT_MOD_4_TILED_LNL_CCS && + (DISPLAY_VER(display) < 20 || display->platform.dgfx)) + return false; + + return true; +} + +/** + * intel_fb_plane_get_modifiers: Get the modifiers for the given platform and plane capabilities + * @display: display instance + * @plane_caps: capabilities for the plane the modifiers are queried for + * + * Returns: + * Returns the list of modifiers allowed by the @display platform and @plane_caps. + * The caller must free the returned buffer. + */ +u64 *intel_fb_plane_get_modifiers(struct intel_display *display, + 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(display, plane_caps, &intel_modifiers[i])) + count++; + } + + list = kmalloc_array(count, sizeof(*list), GFP_KERNEL); + if (drm_WARN_ON(display->drm, !list)) + return NULL; + + p = list; + for (i = 0; i < ARRAY_SIZE(intel_modifiers); i++) { + if (plane_has_modifier(display, 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) +{ + 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) +{ + if (!info->is_yuv) + return false; + + if (hweight8(md->ccs.planar_aux_planes) == 2) + return info->num_planes == 4; + else + return info->num_planes == 2; +} + +/** + * 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 format_is_yuv_semiplanar(lookup_modifier(modifier), info); +} + +static u8 ccs_aux_plane_mask(const struct intel_modifier_desc *md, + const struct drm_format_info *format) +{ + if (format_is_yuv_semiplanar(md, format)) + return md->ccs.planar_aux_planes; + else + return md->ccs.packed_aux_planes; +} + +/** + * 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, 14) && + 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; } 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 +734,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,34 +743,115 @@ 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); + const struct intel_modifier_desc *md = lookup_modifier(fb->modifier); + struct intel_display *display = to_intel_display(fb->dev); - if (is_ccs_modifier(fb->modifier)) + if (md->ccs.packed_aux_planes | md->ccs.planar_aux_planes) return main_to_ccs_plane(fb, main_plane); - else if (DISPLAY_VER(i915) < 11 && - intel_format_info_is_yuv_semiplanar(fb->format, fb->modifier)) + else if (DISPLAY_VER(display) < 11 && + format_is_yuv_semiplanar(md, fb->format)) return 1; else return 0; } -unsigned int intel_tile_size(const struct drm_i915_private *i915) +unsigned int intel_tile_size(struct intel_display *display) { - return DISPLAY_VER(i915) == 2 ? 2048 : 4096; + return DISPLAY_VER(display) == 2 ? 2048 : 4096; +} + +unsigned int +intel_tile_width_bytes(const struct drm_framebuffer *fb, int color_plane) +{ + struct intel_display *display = to_intel_display(fb->dev); + unsigned int cpp = fb->format->cpp[color_plane]; + + switch (fb->modifier) { + case DRM_FORMAT_MOD_LINEAR: + return intel_tile_size(display); + case I915_FORMAT_MOD_X_TILED: + if (DISPLAY_VER(display) == 2) + return 128; + else + return 512; + case I915_FORMAT_MOD_4_TILED_BMG_CCS: + case I915_FORMAT_MOD_4_TILED_LNL_CCS: + case I915_FORMAT_MOD_4_TILED_DG2_RC_CCS: + case I915_FORMAT_MOD_4_TILED_DG2_RC_CCS_CC: + case I915_FORMAT_MOD_4_TILED_DG2_MC_CCS: + case I915_FORMAT_MOD_4_TILED: + /* + * Each 4K tile consists of 64B(8*8) subtiles, with + * same shape as Y Tile(i.e 4*16B OWords) + */ + return 128; + case I915_FORMAT_MOD_Y_TILED_CCS: + if (intel_fb_is_ccs_aux_plane(fb, color_plane)) + return 128; + fallthrough; + case I915_FORMAT_MOD_4_TILED_MTL_RC_CCS: + case I915_FORMAT_MOD_4_TILED_MTL_RC_CCS_CC: + case I915_FORMAT_MOD_4_TILED_MTL_MC_CCS: + 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 (HAS_128B_Y_TILING(display)) + 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) { - if (is_gen12_ccs_plane(fb, color_plane)) - return 1; + struct intel_display *display = to_intel_display(fb->dev); - return intel_tile_size(to_i915(fb->dev)) / + return intel_tile_size(display) / 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 +863,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,16 +887,26 @@ unsigned int intel_tile_row_size(const struct drm_framebuffer *fb, int color_pla return fb->pitches[color_plane] * tile_height; } -unsigned int intel_cursor_alignment(const struct drm_i915_private *i915) +unsigned int +intel_fb_align_height(const struct drm_framebuffer *fb, + int color_plane, unsigned int height) { - if (IS_I830(i915)) - return 16 * 1024; - else if (IS_I85X(i915)) - return 256; - else if (IS_I845G(i915) || IS_I865G(i915)) - return 32; - else - return 4 * 1024; + unsigned int tile_height = intel_tile_height(fb, color_plane); + + return ALIGN(height, tile_height); +} + +bool intel_fb_modifier_uses_dpt(struct intel_display *display, u64 modifier) +{ + return HAS_DPT(display) && modifier != DRM_FORMAT_MOD_LINEAR; +} + +bool intel_fb_uses_dpt(const struct drm_framebuffer *fb) +{ + struct intel_display *display = to_intel_display(fb->dev); + + return display->params.enable_dpt && + intel_fb_modifier_uses_dpt(display, fb->modifier); } void intel_fb_plane_get_subsampling(int *hsub, int *vsub, @@ -138,7 +926,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 +953,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 +994,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, @@ -210,16 +1015,16 @@ static u32 intel_adjust_aligned_offset(int *x, int *y, unsigned int pitch, u32 old_offset, u32 new_offset) { - struct drm_i915_private *i915 = to_i915(fb->dev); + struct intel_display *display = to_intel_display(fb->dev); unsigned int cpp = fb->format->cpp[color_plane]; - drm_WARN_ON(&i915->drm, new_offset > old_offset); + drm_WARN_ON(display->drm, new_offset > old_offset); if (!is_surface_linear(fb, color_plane)) { unsigned int tile_size, tile_width, tile_height; unsigned int pitch_tiles; - tile_size = intel_tile_size(i915); + tile_size = intel_tile_size(display); intel_tile_dims(fb, color_plane, &tile_width, &tile_height); if (drm_rotation_90_or_270(rotation)) { @@ -233,10 +1038,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; @@ -247,13 +1050,13 @@ static u32 intel_adjust_aligned_offset(int *x, int *y, * the x/y offsets. */ u32 intel_plane_adjust_aligned_offset(int *x, int *y, - const struct intel_plane_state *state, + const struct intel_plane_state *plane_state, int color_plane, u32 old_offset, u32 new_offset) { - return intel_adjust_aligned_offset(x, y, state->hw.fb, color_plane, - state->hw.rotation, - state->view.color_plane[color_plane].stride, + return intel_adjust_aligned_offset(x, y, plane_state->hw.fb, color_plane, + plane_state->hw.rotation, + plane_state->view.color_plane[color_plane].mapping_stride, old_offset, new_offset); } @@ -271,13 +1074,13 @@ u32 intel_plane_adjust_aligned_offset(int *x, int *y, * used. This is why the user has to pass in the pitch since it * is specified in the rotated orientation. */ -static u32 intel_compute_aligned_offset(struct drm_i915_private *i915, +static u32 intel_compute_aligned_offset(struct intel_display *display, int *x, int *y, const struct drm_framebuffer *fb, int color_plane, unsigned int pitch, unsigned int rotation, - u32 alignment) + unsigned int alignment) { unsigned int cpp = fb->format->cpp[color_plane]; u32 offset, offset_aligned; @@ -286,7 +1089,7 @@ static u32 intel_compute_aligned_offset(struct drm_i915_private *i915, unsigned int tile_size, tile_width, tile_height; unsigned int tile_rows, tiles, pitch_tiles; - tile_size = intel_tile_size(i915); + tile_size = intel_tile_size(display); intel_tile_dims(fb, color_plane, &tile_width, &tile_height); if (drm_rotation_90_or_270(rotation)) { @@ -327,22 +1130,17 @@ static u32 intel_compute_aligned_offset(struct drm_i915_private *i915, } u32 intel_plane_compute_aligned_offset(int *x, int *y, - const struct intel_plane_state *state, + const struct intel_plane_state *plane_state, int color_plane) { - struct intel_plane *intel_plane = to_intel_plane(state->uapi.plane); - 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; - u32 alignment; - - if (intel_plane->id == PLANE_CURSOR) - alignment = intel_cursor_alignment(i915); - else - alignment = intel_surf_alignment(fb, color_plane); + struct intel_display *display = to_intel_display(plane_state); + struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); + const struct drm_framebuffer *fb = plane_state->hw.fb; + unsigned int rotation = plane_state->hw.rotation; + unsigned int pitch = plane_state->view.color_plane[color_plane].mapping_stride; + unsigned int alignment = plane->min_alignment(plane, fb, color_plane); - return intel_compute_aligned_offset(i915, x, y, fb, color_plane, + return intel_compute_aligned_offset(display, x, y, fb, color_plane, pitch, rotation, alignment); } @@ -351,41 +1149,28 @@ static int intel_fb_offset_to_xy(int *x, int *y, const struct drm_framebuffer *fb, int color_plane) { - struct drm_i915_private *i915 = to_i915(fb->dev); - unsigned int height; - u32 alignment; + struct intel_display *display = to_intel_display(fb->dev); + unsigned int height, alignment, unused; - /* - * 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)) - alignment = intel_tile_row_size(fb, color_plane); - else if (fb->modifier != DRM_FORMAT_MOD_LINEAR) - alignment = intel_tile_size(i915); + if (fb->modifier != DRM_FORMAT_MOD_LINEAR) + alignment = intel_tile_size(display); else alignment = 0; if (alignment != 0 && fb->offsets[color_plane] % alignment) { - drm_dbg_kms(&i915->drm, + drm_dbg_kms(display->drm, "Misaligned offset 0x%08x for color plane %d\n", fb->offsets[color_plane], color_plane); return -EINVAL; } - height = drm_framebuffer_plane_height(fb->height, fb, color_plane); + height = drm_format_info_plane_height(fb->format, fb->height, color_plane); height = ALIGN(height, intel_tile_height(fb, color_plane)); /* Catch potential overflows early */ - if (add_overflows_t(u32, mul_u32_u32(height, fb->pitches[color_plane]), - fb->offsets[color_plane])) { - drm_dbg_kms(&i915->drm, + if (check_add_overflow(mul_u32_u32(height, fb->pitches[color_plane]), + fb->offsets[color_plane], &unused)) { + drm_dbg_kms(display->drm, "Bad offset 0x%08x or pitch %d for color plane %d\n", fb->offsets[color_plane], fb->pitches[color_plane], color_plane); @@ -405,7 +1190,7 @@ static int intel_fb_offset_to_xy(int *x, int *y, static int intel_fb_check_ccs_xy(const struct drm_framebuffer *fb, int ccs_plane, int x, int y) { - struct drm_i915_private *i915 = to_i915(fb->dev); + struct intel_display *display = to_intel_display(fb->dev); const struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); int main_plane; int hsub, vsub; @@ -413,10 +1198,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; @@ -434,13 +1224,12 @@ static int intel_fb_check_ccs_xy(const struct drm_framebuffer *fb, int ccs_plane * x/y offsets must match between CCS and the main surface. */ if (main_x != ccs_x || main_y != ccs_y) { - drm_dbg_kms(&i915->drm, - "Bad CCS x/y (main %d,%d ccs %d,%d) full (main %d,%d ccs %d,%d)\n", - main_x, main_y, - ccs_x, ccs_y, - intel_fb->normal_view.color_plane[main_plane].x, - intel_fb->normal_view.color_plane[main_plane].y, - x, y); + drm_dbg_kms(display->drm, + "Bad CCS x/y (main %d,%d ccs %d,%d) full (main %d,%d ccs %d,%d)\n", + main_x, main_y, ccs_x, ccs_y, + intel_fb->normal_view.color_plane[main_plane].x, + intel_fb->normal_view.color_plane[main_plane].y, + x, y); return -EINVAL; } @@ -449,8 +1238,8 @@ static int intel_fb_check_ccs_xy(const struct drm_framebuffer *fb, int ccs_plane static bool intel_plane_can_remap(const struct intel_plane_state *plane_state) { + struct intel_display *display = to_intel_display(plane_state); struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); - struct drm_i915_private *i915 = to_i915(plane->base.dev); const struct drm_framebuffer *fb = plane_state->hw.fb; int i; @@ -464,19 +1253,19 @@ static bool intel_plane_can_remap(const struct intel_plane_state *plane_state) * Would also need to deal with the fence POT alignment * and gen2 2KiB GTT tile size. */ - if (DISPLAY_VER(i915) < 4) + if (DISPLAY_VER(display) < 4) return false; /* * 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 */ if (fb->modifier == DRM_FORMAT_MOD_LINEAR) { - unsigned int alignment = intel_tile_size(i915) - 1; + unsigned int alignment = intel_tile_size(display) - 1; for (i = 0; i < fb->format->num_planes; i++) { if (fb->pitches[i] & alignment) @@ -489,20 +1278,30 @@ static bool intel_plane_can_remap(const struct intel_plane_state *plane_state) bool intel_fb_needs_pot_stride_remap(const struct intel_framebuffer *fb) { - struct drm_i915_private *i915 = to_i915(fb->base.dev); + struct intel_display *display = to_intel_display(fb->base.dev); - return IS_ALDERLAKE_P(i915) && fb->base.modifier != DRM_FORMAT_MOD_LINEAR && - !is_ccs_modifier(fb->base.modifier); + return (display->platform.alderlake_p || DISPLAY_VER(display) >= 14) && + intel_fb_uses_dpt(&fb->base); +} + +bool intel_plane_uses_fence(const struct intel_plane_state *plane_state) +{ + struct intel_display *display = to_intel_display(plane_state); + struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); + + return DISPLAY_VER(display) < 4 || + (plane->fbc && !plane_state->no_fbc_reason && + plane_state->view.gtt.type == I915_GTT_VIEW_NORMAL); } 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) @@ -527,7 +1326,7 @@ static bool intel_plane_needs_remap(const struct intel_plane_state *plane_state) * unclear in Bspec, for now no checking. */ stride = intel_fb_pitch(fb, 0, rotation); - max_stride = plane->max_stride(plane, fb->base.format->format, + max_stride = plane->max_stride(plane, fb->base.format, fb->base.modifier, rotation); return stride > max_stride; @@ -536,12 +1335,13 @@ static bool intel_plane_needs_remap(const struct intel_plane_state *plane_state) static int convert_plane_offset_to_xy(const struct intel_framebuffer *fb, int color_plane, int plane_width, int *x, int *y) { - struct drm_i915_gem_object *obj = intel_fb_obj(&fb->base); + struct intel_display *display = to_intel_display(fb->base.dev); + struct drm_gem_object *obj = intel_fb_bo(&fb->base); int ret; ret = intel_fb_offset_to_xy(x, y, &fb->base, color_plane); if (ret) { - drm_dbg_kms(fb->base.dev, + drm_dbg_kms(display->drm, "bad fb plane %d offset: 0x%x\n", color_plane, fb->base.offsets[color_plane]); return ret; @@ -560,9 +1360,9 @@ static int convert_plane_offset_to_xy(const struct intel_framebuffer *fb, int co * fb layout agrees with the fence layout. We already check that the * fb stride matches the fence stride elsewhere. */ - if (color_plane == 0 && i915_gem_object_is_tiled(obj) && + if (color_plane == 0 && intel_bo_is_tiled(obj) && (*x + plane_width) * fb->base.format->cpp[color_plane] > fb->base.pitches[color_plane]) { - drm_dbg_kms(fb->base.dev, + drm_dbg_kms(display->drm, "bad fb plane %d offset: 0x%x\n", color_plane, fb->base.offsets[color_plane]); return -EINVAL; @@ -573,11 +1373,11 @@ static int convert_plane_offset_to_xy(const struct intel_framebuffer *fb, int co static u32 calc_plane_aligned_offset(const struct intel_framebuffer *fb, int color_plane, int *x, int *y) { - struct drm_i915_private *i915 = to_i915(fb->base.dev); - unsigned int tile_size = intel_tile_size(i915); + struct intel_display *display = to_intel_display(fb->base.dev); + unsigned int tile_size = intel_tile_size(display); u32 offset; - offset = intel_compute_aligned_offset(i915, x, y, &fb->base, color_plane, + offset = intel_compute_aligned_offset(display, x, y, &fb->base, color_plane, fb->base.pitches[color_plane], DRM_MODE_ROTATE_0, tile_size); @@ -612,14 +1412,32 @@ 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) +{ + struct intel_display *display = to_intel_display(fb->base.dev); + unsigned int stride_tiles; + + if ((display->platform.alderlake_p || DISPLAY_VER(display) >= 14) && + src_stride_tiles < dst_stride_tiles) + 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,9 +1456,29 @@ plane_view_height_tiles(const struct intel_framebuffer *fb, int color_plane, return DIV_ROUND_UP(y + dims->height, dims->tile_height); } -#define assign_chk_ovf(i915, var, val) ({ \ - drm_WARN_ON(&(i915)->drm, overflows_type(val, var)); \ +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 intel_display *display = to_intel_display(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(display)); +} + +#define assign_chk_ovf(display, var, val) ({ \ + drm_WARN_ON((display)->drm, overflows_type(val, var)); \ + (var) = (val); \ +}) + +#define assign_bfld_chk_ovf(display, var, val) ({ \ (var) = (val); \ + drm_WARN_ON((display)->drm, (var) != (val)); \ + (var); \ }) static u32 calc_plane_remap_info(const struct intel_framebuffer *fb, int color_plane, @@ -648,24 +1486,38 @@ static u32 calc_plane_remap_info(const struct intel_framebuffer *fb, int color_p u32 obj_offset, u32 gtt_offset, int x, int y, struct intel_fb_view *view) { - struct drm_i915_private *i915 = to_i915(fb->base.dev); + struct intel_display *display = to_intel_display(fb->base.dev); struct intel_remapped_plane_info *remap_info = &view->gtt.remapped.plane[color_plane]; struct i915_color_plane_view *color_plane_info = &view->color_plane[color_plane]; unsigned int tile_width = dims->tile_width; unsigned int tile_height = dims->tile_height; - unsigned int tile_size = intel_tile_size(i915); + unsigned int tile_size = intel_tile_size(display); struct drm_rect r; - u32 size; + u32 size = 0; - 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)); + assign_bfld_chk_ovf(display, remap_info->offset, obj_offset); - if (view->gtt.type == I915_GGTT_VIEW_ROTATED) { - check_array_bounds(i915, view->gtt.rotated.plane, color_plane); + if (intel_fb_is_gen12_ccs_aux_plane(&fb->base, color_plane)) { + remap_info->linear = 1; - assign_chk_ovf(i915, remap_info->dst_stride, + assign_chk_ovf(display, remap_info->size, + plane_view_linear_tiles(fb, color_plane, dims, x, y)); + } else { + remap_info->linear = 0; + + assign_chk_ovf(display, remap_info->src_stride, + plane_view_src_stride_tiles(fb, color_plane, dims)); + assign_chk_ovf(display, remap_info->width, + plane_view_width_tiles(fb, color_plane, dims, x)); + assign_chk_ovf(display, remap_info->height, + plane_view_height_tiles(fb, color_plane, dims, y)); + } + + if (view->gtt.type == I915_GTT_VIEW_ROTATED) { + drm_WARN_ON(display->drm, remap_info->linear); + check_array_bounds(display, view->gtt.rotated.plane, color_plane); + + assign_chk_ovf(display, remap_info->dst_stride, plane_view_dst_stride_tiles(fb, color_plane, remap_info->height)); /* rotate the x/y offsets to match the GTT view */ @@ -678,27 +1530,61 @@ 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); } else { - drm_WARN_ON(&i915->drm, view->gtt.type != I915_GGTT_VIEW_REMAPPED); + drm_WARN_ON(display->drm, view->gtt.type != I915_GTT_VIEW_REMAPPED); - check_array_bounds(i915, view->gtt.remapped.plane, color_plane); + check_array_bounds(display, 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) { + u32 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->size; + } else { + unsigned int dst_stride; + + /* + * The hardware automagically calculates the CCS AUX surface + * stride from the main surface stride so can't really remap a + * smaller subset (unless we'd remap in whole AUX page units). + */ + if (intel_fb_needs_pot_stride_remap(fb) && + intel_fb_is_ccs_modifier(fb->base.modifier)) + dst_stride = remap_info->src_stride; + else + dst_stride = remap_info->width; + + dst_stride = plane_view_dst_stride_tiles(fb, color_plane, dst_stride); + + assign_chk_ovf(display, 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 = remap_info->dst_stride * remap_info->height; + size += dst_stride * remap_info->height; + } } /* @@ -706,10 +1592,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 +1614,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,45 +1632,95 @@ 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 intel_display *display, + struct intel_fb_view *view, + enum i915_gtt_view_type view_type) { memset(view, 0, sizeof(*view)); view->gtt.type = view_type; + + if (view_type == I915_GTT_VIEW_REMAPPED && + (display->platform.alderlake_p || DISPLAY_VER(display) >= 14)) + view->gtt.remapped.plane_alignment = SZ_2M / PAGE_SIZE; } bool intel_fb_supports_90_270_rotation(const struct intel_framebuffer *fb) { - if (DISPLAY_VER(to_i915(fb->base.dev)) >= 13) + struct intel_display *display = to_intel_display(fb->base.dev); + + if (DISPLAY_VER(display) >= 13) return false; return fb->base.modifier == I915_FORMAT_MOD_Y_TILED || fb->base.modifier == I915_FORMAT_MOD_Yf_TILED; } -int intel_fill_fb_info(struct drm_i915_private *i915, struct intel_framebuffer *fb) +static unsigned int intel_fb_min_alignment(const struct drm_framebuffer *fb) { - struct drm_i915_gem_object *obj = intel_fb_obj(&fb->base); + struct intel_display *display = to_intel_display(fb->dev); + struct intel_plane *plane; + unsigned int min_alignment = 0; + + for_each_intel_plane(display->drm, plane) { + unsigned int plane_min_alignment; + + if (!drm_plane_has_format(&plane->base, fb->format->format, fb->modifier)) + continue; + + plane_min_alignment = plane->min_alignment(plane, fb, 0); + + drm_WARN_ON(display->drm, plane_min_alignment && + !is_power_of_2(plane_min_alignment)); + + if (intel_plane_needs_physical(plane)) + continue; + + min_alignment = max(min_alignment, plane_min_alignment); + } + + return min_alignment; +} + +static unsigned int intel_fb_vtd_guard(const struct drm_framebuffer *fb) +{ + struct intel_display *display = to_intel_display(fb->dev); + struct intel_plane *plane; + unsigned int vtd_guard = 0; + + for_each_intel_plane(display->drm, plane) { + if (!drm_plane_has_format(&plane->base, fb->format->format, fb->modifier)) + continue; + + vtd_guard = max_t(unsigned int, vtd_guard, plane->vtd_guard); + } + + return vtd_guard; +} + +int intel_fill_fb_info(struct intel_display *display, struct intel_framebuffer *fb) +{ + struct drm_gem_object *obj = intel_fb_bo(&fb->base); u32 gtt_offset_rotated = 0; u32 gtt_offset_remapped = 0; unsigned int max_size = 0; int i, num_planes = fb->base.format->num_planes; - unsigned int tile_size = intel_tile_size(i915); + unsigned int tile_size = intel_tile_size(display); - intel_fb_view_init(&fb->normal_view, I915_GGTT_VIEW_NORMAL); + intel_fb_view_init(display, &fb->normal_view, I915_GTT_VIEW_NORMAL); - drm_WARN_ON(&i915->drm, + drm_WARN_ON(display->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(display, &fb->rotated_view, I915_GTT_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(display, &fb->remapped_view, I915_GTT_VIEW_REMAPPED); for (i = 0; i < num_planes; i++) { struct fb_plane_view_dims view_dims; unsigned int width, height; - unsigned int cpp, size; + unsigned int size; u32 offset; int x, y; int ret; @@ -794,13 +1731,26 @@ int intel_fill_fb_info(struct drm_i915_private *i915, struct intel_framebuffer * * arithmetic related to alignment and offset calculation. */ if (is_gen12_ccs_cc_plane(&fb->base, i)) { - if (IS_ALIGNED(fb->base.offsets[i], PAGE_SIZE)) - continue; - else + unsigned int end; + + if (!IS_ALIGNED(fb->base.offsets[i], 64)) { + drm_dbg_kms(display->drm, + "fb misaligned clear color plane %d offset (0x%x)\n", + i, fb->base.offsets[i]); return -EINVAL; + } + + if (check_add_overflow(fb->base.offsets[i], 64, &end)) { + drm_dbg_kms(display->drm, + "fb bad clear color plane %d offset (0x%x)\n", + i, fb->base.offsets[i]); + return -EINVAL; + } + + max_size = max(max_size, DIV_ROUND_UP(end, tile_size)); + continue; } - cpp = fb->base.format->cpp[i]; intel_fb_plane_dims(fb, i, &width, &height); ret = convert_plane_offset_to_xy(fb, i, width, &x, &y); @@ -815,7 +1765,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); @@ -834,20 +1786,53 @@ int intel_fill_fb_info(struct drm_i915_private *i915, struct intel_framebuffer * max_size = max(max_size, offset + size); } - if (mul_u32_u32(max_size, tile_size) > obj->base.size) { - drm_dbg_kms(&i915->drm, + if (mul_u32_u32(max_size, tile_size) > obj->size) { + drm_dbg_kms(display->drm, "fb too big for bo (need %llu bytes, have %zu bytes)\n", - mul_u32_u32(max_size, tile_size), obj->base.size); + mul_u32_u32(max_size, tile_size), obj->size); return -EINVAL; } + fb->min_alignment = intel_fb_min_alignment(&fb->base); + fb->vtd_guard = intel_fb_vtd_guard(&fb->base); + return 0; } +unsigned int intel_fb_view_vtd_guard(const struct drm_framebuffer *fb, + const struct intel_fb_view *view, + unsigned int rotation) +{ + unsigned int vtd_guard; + int color_plane; + + vtd_guard = to_intel_framebuffer(fb)->vtd_guard; + if (!vtd_guard) + return 0; + + for (color_plane = 0; color_plane < fb->format->num_planes; color_plane++) { + unsigned int stride, tile; + + if (intel_fb_is_ccs_aux_plane(fb, color_plane) || + is_gen12_ccs_cc_plane(fb, color_plane)) + continue; + + stride = view->color_plane[color_plane].mapping_stride; + + if (drm_rotation_90_or_270(rotation)) + tile = intel_tile_height(fb, color_plane); + else + tile = intel_tile_width_bytes(fb, color_plane); + + vtd_guard = max(vtd_guard, DIV_ROUND_UP(stride, tile)); + } + + return vtd_guard; +} + static void intel_plane_remap_gtt(struct intel_plane_state *plane_state) { - struct drm_i915_private *i915 = - to_i915(plane_state->uapi.plane->dev); + struct intel_display *display = to_intel_display(plane_state); struct drm_framebuffer *fb = plane_state->hw.fb; struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); unsigned int rotation = plane_state->hw.rotation; @@ -856,16 +1841,16 @@ 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, - drm_rotation_90_or_270(rotation) ? I915_GGTT_VIEW_ROTATED : - I915_GGTT_VIEW_REMAPPED); + intel_fb_view_init(display, &plane_state->view, + drm_rotation_90_or_270(rotation) ? I915_GTT_VIEW_ROTATED : + I915_GTT_VIEW_REMAPPED); src_x = plane_state->uapi.src.x1 >> 16; src_y = plane_state->uapi.src.y1 >> 16; 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(display->drm, intel_fb_is_ccs_modifier(fb->modifier)); /* Make src coordinates relative to the viewport */ drm_rect_translate(&plane_state->uapi.src, @@ -907,6 +1892,42 @@ static void intel_plane_remap_gtt(struct intel_plane_state *plane_state) } } +unsigned int intel_rotation_info_size(const struct intel_rotation_info *rot_info) +{ + unsigned int size = 0; + int i; + + for (i = 0 ; i < ARRAY_SIZE(rot_info->plane); i++) + size += rot_info->plane[i].dst_stride * rot_info->plane[i].width; + + return size; +} + +unsigned int intel_remapped_info_size(const struct intel_remapped_info *rem_info) +{ + unsigned int size = 0; + int i; + + for (i = 0 ; i < ARRAY_SIZE(rem_info->plane); i++) { + unsigned int plane_size; + + if (rem_info->plane[i].linear) + plane_size = rem_info->plane[i].size; + else + plane_size = rem_info->plane[i].dst_stride * rem_info->plane[i].height; + + if (plane_size == 0) + continue; + + if (rem_info->plane_alignment) + size = ALIGN(size, rem_info->plane_alignment); + + size += plane_size; + } + + return size; +} + void intel_fb_fill_view(const struct intel_framebuffer *fb, unsigned int rotation, struct intel_fb_view *view) { @@ -918,8 +1939,106 @@ void intel_fb_fill_view(const struct intel_framebuffer *fb, unsigned int rotatio *view = fb->normal_view; } +/* + * Convert the x/y offsets into a linear offset. + * Only valid with 0/180 degree rotation, which is fine since linear + * offset is only used with linear buffers on pre-hsw and tiled buffers + * with gen2/3, and 90/270 degree rotations isn't supported on any of them. + */ +u32 intel_fb_xy_to_linear(int x, int y, + const struct intel_plane_state *plane_state, + int color_plane) +{ + const struct drm_framebuffer *fb = plane_state->hw.fb; + unsigned int cpp = fb->format->cpp[color_plane]; + unsigned int pitch = plane_state->view.color_plane[color_plane].mapping_stride; + + return y * pitch + x * cpp; +} + +/* + * Add the x/y offsets derived from fb->offsets[] to the user + * specified plane src x/y offsets. The resulting x/y offsets + * specify the start of scanout from the beginning of the gtt mapping. + */ +void intel_add_fb_offsets(int *x, int *y, + const struct intel_plane_state *plane_state, + int color_plane) + +{ + *x += plane_state->view.color_plane[color_plane].x; + *y += plane_state->view.color_plane[color_plane].y; +} + +static +u32 intel_fb_max_stride(struct intel_display *display, + const struct drm_format_info *info, + 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(display) < 4 || intel_fb_is_ccs_modifier(modifier) || + intel_fb_modifier_uses_dpt(display, modifier)) + return intel_plane_fb_max_stride(display, info, modifier); + else if (DISPLAY_VER(display) >= 7) + return 256 * 1024; + else + return 128 * 1024; +} + +static unsigned int +intel_fb_stride_alignment(const struct drm_framebuffer *fb, int color_plane) +{ + struct intel_display *display = to_intel_display(fb->dev); + unsigned int tile_width; + + if (is_surface_linear(fb, color_plane)) { + unsigned int max_stride = intel_plane_fb_max_stride(display, + fb->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(display); + 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(display) >= 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(display) == 9 || display->platform.geminilake) && + 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_display *display = to_intel_display(plane_state); struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); const struct drm_framebuffer *fb = plane_state->hw.fb; unsigned int rotation = plane_state->hw.rotation; @@ -936,14 +2055,15 @@ 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; - max_stride = plane->max_stride(plane, fb->format->format, + stride = plane_state->view.color_plane[0].mapping_stride; + max_stride = plane->max_stride(plane, fb->format, fb->modifier, rotation); if (stride > max_stride) { - DRM_DEBUG_KMS("[FB:%d] stride (%d) exceeds [PLANE:%d:%s] max stride (%d)\n", - fb->base.id, stride, - plane->base.base.id, plane->base.name, max_stride); + drm_dbg_kms(display->drm, + "[FB:%d] stride (%d) exceeds [PLANE:%d:%s] max stride (%d)\n", + fb->base.id, stride, + plane->base.base.id, plane->base.name, max_stride); return -EINVAL; } @@ -981,3 +2101,297 @@ 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_fb_bo_framebuffer_fini(intel_fb_bo(fb)); + + intel_frontbuffer_put(intel_fb->frontbuffer); + + kfree(intel_fb->panic); + kfree(intel_fb); +} + +static int intel_user_framebuffer_create_handle(struct drm_framebuffer *fb, + struct drm_file *file, + unsigned int *handle) +{ + struct drm_gem_object *obj = intel_fb_bo(fb); + struct intel_display *display = to_intel_display(obj->dev); + + if (intel_bo_is_userptr(obj)) { + drm_dbg(display->drm, + "attempting to use a userptr for a framebuffer, denied\n"); + return -EINVAL; + } + + return drm_gem_handle_create(file, obj, handle); +} + +struct frontbuffer_fence_cb { + struct dma_fence_cb base; + struct intel_frontbuffer *front; +}; + +static void intel_user_framebuffer_fence_wake(struct dma_fence *dma, + struct dma_fence_cb *data) +{ + struct frontbuffer_fence_cb *cb = container_of(data, typeof(*cb), base); + + intel_frontbuffer_queue_flush(cb->front); + kfree(cb); + dma_fence_put(dma); +} + +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_gem_object *obj = intel_fb_bo(fb); + struct intel_frontbuffer *front = to_intel_frontbuffer(fb); + struct dma_fence *fence; + struct frontbuffer_fence_cb *cb; + int ret = 0; + + if (!atomic_read(&front->bits)) + return 0; + + if (dma_resv_test_signaled(obj->resv, dma_resv_usage_rw(false))) + goto flush; + + ret = dma_resv_get_singleton(obj->resv, dma_resv_usage_rw(false), + &fence); + if (ret || !fence) + goto flush; + + cb = kmalloc(sizeof(*cb), GFP_KERNEL); + if (!cb) { + dma_fence_put(fence); + ret = -ENOMEM; + goto flush; + } + + cb->front = front; + + intel_frontbuffer_invalidate(front, ORIGIN_DIRTYFB); + + ret = dma_fence_add_callback(fence, &cb->base, + intel_user_framebuffer_fence_wake); + if (ret) { + intel_user_framebuffer_fence_wake(fence, &cb->base); + if (ret == -ENOENT) + ret = 0; + } + + return ret; + +flush: + intel_frontbuffer_flush(front, ORIGIN_DIRTYFB); + return ret; +} + +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_gem_object *obj, + const struct drm_format_info *info, + struct drm_mode_fb_cmd2 *mode_cmd) +{ + struct intel_display *display = to_intel_display(obj->dev); + struct drm_framebuffer *fb = &intel_fb->base; + u32 max_stride; + int ret; + int i; + + intel_fb->panic = intel_panic_alloc(); + if (!intel_fb->panic) + return -ENOMEM; + + /* + * intel_frontbuffer_get() must be done before + * intel_fb_bo_framebuffer_init() to avoid set_tiling vs. addfb race. + */ + intel_fb->frontbuffer = intel_frontbuffer_get(obj); + if (!intel_fb->frontbuffer) { + ret = -ENOMEM; + goto err_free_panic; + } + + ret = intel_fb_bo_framebuffer_init(obj, mode_cmd); + if (ret) + goto err_frontbuffer_put; + + if (!drm_any_plane_has_format(display->drm, + mode_cmd->pixel_format, + mode_cmd->modifier[0])) { + drm_dbg_kms(display->drm, + "unsupported pixel format %p4cc / modifier 0x%llx\n", + &mode_cmd->pixel_format, mode_cmd->modifier[0]); + ret = -EINVAL; + goto err_bo_framebuffer_fini; + } + + max_stride = intel_fb_max_stride(display, info, mode_cmd->modifier[0]); + if (mode_cmd->pitches[0] > max_stride) { + drm_dbg_kms(display->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); + ret = -EINVAL; + goto err_bo_framebuffer_fini; + } + + /* FIXME need to adjust LINOFF/TILEOFF accordingly. */ + if (mode_cmd->offsets[0] != 0) { + drm_dbg_kms(display->drm, + "plane 0 offset (0x%08x) must be 0\n", + mode_cmd->offsets[0]); + ret = -EINVAL; + goto err_bo_framebuffer_fini; + } + + drm_helper_mode_fill_fb_struct(display->drm, fb, info, mode_cmd); + + for (i = 0; i < fb->format->num_planes; i++) { + unsigned int stride_alignment; + + if (mode_cmd->handles[i] != mode_cmd->handles[0]) { + drm_dbg_kms(display->drm, "bad plane %d handle\n", i); + ret = -EINVAL; + goto err_bo_framebuffer_fini; + } + + stride_alignment = intel_fb_stride_alignment(fb, i); + if (fb->pitches[i] & (stride_alignment - 1)) { + drm_dbg_kms(display->drm, + "plane %d pitch (%d) must be at least %u byte aligned\n", + i, fb->pitches[i], stride_alignment); + ret = -EINVAL; + goto err_bo_framebuffer_fini; + } + + if (intel_fb_is_gen12_ccs_aux_plane(fb, i)) { + unsigned int ccs_aux_stride = gen12_ccs_aux_stride(intel_fb, i); + + if (fb->pitches[i] != ccs_aux_stride) { + drm_dbg_kms(display->drm, + "ccs aux plane %d pitch (%d) must be %d\n", + i, fb->pitches[i], ccs_aux_stride); + ret = -EINVAL; + goto err_bo_framebuffer_fini; + } + } + + fb->obj[i] = obj; + } + + ret = intel_fill_fb_info(display, intel_fb); + if (ret) + goto err_bo_framebuffer_fini; + + if (intel_fb_uses_dpt(fb)) { + struct i915_address_space *vm; + + vm = intel_dpt_create(intel_fb); + if (IS_ERR(vm)) { + drm_dbg_kms(display->drm, "failed to create DPT\n"); + ret = PTR_ERR(vm); + goto err_frontbuffer_put; + } + + intel_fb->dpt_vm = vm; + } + + ret = drm_framebuffer_init(display->drm, fb, &intel_fb_funcs); + if (ret) { + drm_err(display->drm, "framebuffer init failed %d\n", ret); + goto err_free_dpt; + } + + return 0; + +err_free_dpt: + if (intel_fb_uses_dpt(fb)) + intel_dpt_destroy(intel_fb->dpt_vm); +err_bo_framebuffer_fini: + intel_fb_bo_framebuffer_fini(obj); +err_frontbuffer_put: + intel_frontbuffer_put(intel_fb->frontbuffer); +err_free_panic: + kfree(intel_fb->panic); + + return ret; +} + +struct drm_framebuffer * +intel_user_framebuffer_create(struct drm_device *dev, + struct drm_file *filp, + const struct drm_format_info *info, + const struct drm_mode_fb_cmd2 *user_mode_cmd) +{ + struct drm_framebuffer *fb; + struct drm_gem_object *obj; + struct drm_mode_fb_cmd2 mode_cmd = *user_mode_cmd; + + obj = intel_fb_bo_lookup_valid_bo(dev, filp, &mode_cmd); + if (IS_ERR(obj)) + return ERR_CAST(obj); + + fb = intel_framebuffer_create(obj, info, &mode_cmd); + drm_gem_object_put(obj); + + return fb; +} + +struct intel_framebuffer *intel_framebuffer_alloc(void) +{ + struct intel_framebuffer *intel_fb; + + intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL); + if (!intel_fb) + return NULL; + + return intel_fb; +} + +struct drm_framebuffer * +intel_framebuffer_create(struct drm_gem_object *obj, + const struct drm_format_info *info, + struct drm_mode_fb_cmd2 *mode_cmd) +{ + struct intel_framebuffer *intel_fb; + int ret; + + intel_fb = intel_framebuffer_alloc(); + if (!intel_fb) + return ERR_PTR(-ENOMEM); + + ret = intel_framebuffer_init(intel_fb, obj, info, mode_cmd); + if (ret) + goto err; + + return &intel_fb->base; + +err: + kfree(intel_fb); + return ERR_PTR(ret); +} + +struct drm_gem_object *intel_fb_bo(const struct drm_framebuffer *fb) +{ + return fb ? fb->obj[0] : NULL; +} |
