diff options
Diffstat (limited to 'drivers/gpu/drm/i915/display/skl_universal_plane.c')
| -rw-r--r-- | drivers/gpu/drm/i915/display/skl_universal_plane.c | 3247 |
1 files changed, 3247 insertions, 0 deletions
diff --git a/drivers/gpu/drm/i915/display/skl_universal_plane.c b/drivers/gpu/drm/i915/display/skl_universal_plane.c new file mode 100644 index 000000000000..ee8e24497d2c --- /dev/null +++ b/drivers/gpu/drm/i915/display/skl_universal_plane.c @@ -0,0 +1,3247 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2020 Intel Corporation + */ + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_blend.h> +#include <drm/drm_damage_helper.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_print.h> + +#include "pxp/intel_pxp.h" +#include "intel_bo.h" +#include "intel_color.h" +#include "intel_color_pipeline.h" +#include "intel_de.h" +#include "intel_display_irq.h" +#include "intel_display_regs.h" +#include "intel_display_types.h" +#include "intel_display_utils.h" +#include "intel_dpt.h" +#include "intel_fb.h" +#include "intel_fbc.h" +#include "intel_frontbuffer.h" +#include "intel_panic.h" +#include "intel_plane.h" +#include "intel_psr.h" +#include "intel_psr_regs.h" +#include "intel_step.h" +#include "skl_scaler.h" +#include "skl_universal_plane.h" +#include "skl_universal_plane_regs.h" +#include "skl_watermark.h" + +static const u32 skl_plane_formats[] = { + DRM_FORMAT_C8, + DRM_FORMAT_RGB565, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_XRGB2101010, + DRM_FORMAT_XBGR2101010, + DRM_FORMAT_XRGB16161616F, + DRM_FORMAT_XBGR16161616F, + DRM_FORMAT_YUYV, + DRM_FORMAT_YVYU, + DRM_FORMAT_UYVY, + DRM_FORMAT_VYUY, + DRM_FORMAT_XYUV8888, +}; + +static const u32 skl_planar_formats[] = { + DRM_FORMAT_C8, + DRM_FORMAT_RGB565, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_XRGB2101010, + DRM_FORMAT_XBGR2101010, + DRM_FORMAT_XRGB16161616F, + DRM_FORMAT_XBGR16161616F, + DRM_FORMAT_YUYV, + DRM_FORMAT_YVYU, + DRM_FORMAT_UYVY, + DRM_FORMAT_VYUY, + DRM_FORMAT_NV12, + DRM_FORMAT_XYUV8888, +}; + +static const u32 glk_planar_formats[] = { + DRM_FORMAT_C8, + DRM_FORMAT_RGB565, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_XRGB2101010, + DRM_FORMAT_XBGR2101010, + DRM_FORMAT_XRGB16161616F, + DRM_FORMAT_XBGR16161616F, + DRM_FORMAT_YUYV, + DRM_FORMAT_YVYU, + DRM_FORMAT_UYVY, + DRM_FORMAT_VYUY, + DRM_FORMAT_NV12, + DRM_FORMAT_XYUV8888, + DRM_FORMAT_P010, + DRM_FORMAT_P012, + DRM_FORMAT_P016, +}; + +static const u32 icl_sdr_y_plane_formats[] = { + DRM_FORMAT_C8, + DRM_FORMAT_RGB565, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_XRGB2101010, + DRM_FORMAT_XBGR2101010, + DRM_FORMAT_ARGB2101010, + DRM_FORMAT_ABGR2101010, + DRM_FORMAT_YUYV, + DRM_FORMAT_YVYU, + DRM_FORMAT_UYVY, + DRM_FORMAT_VYUY, + DRM_FORMAT_Y210, + DRM_FORMAT_Y212, + DRM_FORMAT_Y216, + DRM_FORMAT_XYUV8888, + DRM_FORMAT_XVYU2101010, +}; + +static const u32 icl_sdr_uv_plane_formats[] = { + DRM_FORMAT_C8, + DRM_FORMAT_RGB565, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_XRGB2101010, + DRM_FORMAT_XBGR2101010, + DRM_FORMAT_ARGB2101010, + DRM_FORMAT_ABGR2101010, + DRM_FORMAT_YUYV, + DRM_FORMAT_YVYU, + DRM_FORMAT_UYVY, + DRM_FORMAT_VYUY, + DRM_FORMAT_NV12, + DRM_FORMAT_P010, + DRM_FORMAT_P012, + DRM_FORMAT_P016, + DRM_FORMAT_Y210, + DRM_FORMAT_Y212, + DRM_FORMAT_Y216, + DRM_FORMAT_XYUV8888, + DRM_FORMAT_XVYU2101010, +}; + +static const u32 icl_hdr_plane_formats[] = { + DRM_FORMAT_C8, + DRM_FORMAT_RGB565, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_XRGB2101010, + DRM_FORMAT_XBGR2101010, + DRM_FORMAT_ARGB2101010, + DRM_FORMAT_ABGR2101010, + DRM_FORMAT_XRGB16161616F, + DRM_FORMAT_XBGR16161616F, + DRM_FORMAT_ARGB16161616F, + DRM_FORMAT_ABGR16161616F, + DRM_FORMAT_YUYV, + DRM_FORMAT_YVYU, + DRM_FORMAT_UYVY, + DRM_FORMAT_VYUY, + DRM_FORMAT_NV12, + DRM_FORMAT_P010, + DRM_FORMAT_P012, + DRM_FORMAT_P016, + DRM_FORMAT_Y210, + DRM_FORMAT_Y212, + DRM_FORMAT_Y216, + DRM_FORMAT_XYUV8888, + DRM_FORMAT_XVYU2101010, + DRM_FORMAT_XVYU12_16161616, + DRM_FORMAT_XVYU16161616, +}; + +int skl_format_to_fourcc(int format, bool rgb_order, bool alpha) +{ + switch (format) { + case PLANE_CTL_FORMAT_RGB_565: + return DRM_FORMAT_RGB565; + case PLANE_CTL_FORMAT_NV12: + return DRM_FORMAT_NV12; + case PLANE_CTL_FORMAT_XYUV: + return DRM_FORMAT_XYUV8888; + case PLANE_CTL_FORMAT_P010: + return DRM_FORMAT_P010; + case PLANE_CTL_FORMAT_P012: + return DRM_FORMAT_P012; + case PLANE_CTL_FORMAT_P016: + return DRM_FORMAT_P016; + case PLANE_CTL_FORMAT_Y210: + return DRM_FORMAT_Y210; + case PLANE_CTL_FORMAT_Y212: + return DRM_FORMAT_Y212; + case PLANE_CTL_FORMAT_Y216: + return DRM_FORMAT_Y216; + case PLANE_CTL_FORMAT_Y410: + return DRM_FORMAT_XVYU2101010; + case PLANE_CTL_FORMAT_Y412: + return DRM_FORMAT_XVYU12_16161616; + case PLANE_CTL_FORMAT_Y416: + return DRM_FORMAT_XVYU16161616; + default: + case PLANE_CTL_FORMAT_XRGB_8888: + if (rgb_order) { + if (alpha) + return DRM_FORMAT_ABGR8888; + else + return DRM_FORMAT_XBGR8888; + } else { + if (alpha) + return DRM_FORMAT_ARGB8888; + else + return DRM_FORMAT_XRGB8888; + } + case PLANE_CTL_FORMAT_XRGB_2101010: + if (rgb_order) { + if (alpha) + return DRM_FORMAT_ABGR2101010; + else + return DRM_FORMAT_XBGR2101010; + } else { + if (alpha) + return DRM_FORMAT_ARGB2101010; + else + return DRM_FORMAT_XRGB2101010; + } + case PLANE_CTL_FORMAT_XRGB_16161616F: + if (rgb_order) { + if (alpha) + return DRM_FORMAT_ABGR16161616F; + else + return DRM_FORMAT_XBGR16161616F; + } else { + if (alpha) + return DRM_FORMAT_ARGB16161616F; + else + return DRM_FORMAT_XRGB16161616F; + } + } +} + +static u8 icl_nv12_y_plane_mask(struct intel_display *display) +{ + if (DISPLAY_VER(display) >= 13 || HAS_D12_PLANE_MINIMIZATION(display)) + return BIT(PLANE_4) | BIT(PLANE_5); + else + return BIT(PLANE_6) | BIT(PLANE_7); +} + +bool icl_is_nv12_y_plane(struct intel_display *display, + enum plane_id plane_id) +{ + return DISPLAY_VER(display) >= 11 && + icl_nv12_y_plane_mask(display) & BIT(plane_id); +} + +u8 icl_hdr_plane_mask(void) +{ + return BIT(PLANE_1) | BIT(PLANE_2) | BIT(PLANE_3); +} + +bool icl_is_hdr_plane(struct intel_display *display, enum plane_id plane_id) +{ + return DISPLAY_VER(display) >= 11 && + icl_hdr_plane_mask() & BIT(plane_id); +} + +static int icl_plane_min_cdclk(const struct intel_crtc_state *crtc_state, + const struct intel_plane_state *plane_state) +{ + unsigned int pixel_rate = intel_plane_pixel_rate(crtc_state, plane_state); + + /* two pixels per clock */ + return DIV_ROUND_UP(pixel_rate, 2); +} + +static void +glk_plane_ratio(const struct intel_plane_state *plane_state, + unsigned int *num, unsigned int *den) +{ + const struct drm_framebuffer *fb = plane_state->hw.fb; + + if (fb->format->cpp[0] == 8) { + *num = 10; + *den = 8; + } else { + *num = 1; + *den = 1; + } +} + +static int glk_plane_min_cdclk(const struct intel_crtc_state *crtc_state, + const struct intel_plane_state *plane_state) +{ + unsigned int pixel_rate = intel_plane_pixel_rate(crtc_state, plane_state); + unsigned int num, den; + + glk_plane_ratio(plane_state, &num, &den); + + /* two pixels per clock */ + return DIV_ROUND_UP(pixel_rate * num, 2 * den); +} + +static void +skl_plane_ratio(const struct intel_plane_state *plane_state, + unsigned int *num, unsigned int *den) +{ + const struct drm_framebuffer *fb = plane_state->hw.fb; + + if (fb->format->cpp[0] == 8) { + *num = 9; + *den = 8; + } else { + *num = 1; + *den = 1; + } +} + +static int skl_plane_min_cdclk(const struct intel_crtc_state *crtc_state, + const struct intel_plane_state *plane_state) +{ + unsigned int pixel_rate = intel_plane_pixel_rate(crtc_state, plane_state); + unsigned int num, den; + + skl_plane_ratio(plane_state, &num, &den); + + return DIV_ROUND_UP(pixel_rate * num, den); +} + +static int skl_plane_max_width(const struct drm_framebuffer *fb, + int color_plane, + unsigned int rotation) +{ + int cpp = fb->format->cpp[color_plane]; + + switch (fb->modifier) { + case DRM_FORMAT_MOD_LINEAR: + case I915_FORMAT_MOD_X_TILED: + /* + * Validated limit is 4k, but has 5k should + * work apart from the following features: + * - Ytile (already limited to 4k) + * - FP16 (already limited to 4k) + * - render compression (already limited to 4k) + * - KVMR sprite and cursor (don't care) + * - horizontal panning (TODO verify this) + * - pipe and plane scaling (TODO verify this) + */ + if (cpp == 8) + return 4096; + else + return 5120; + case I915_FORMAT_MOD_Y_TILED_CCS: + case I915_FORMAT_MOD_Yf_TILED_CCS: + /* FIXME AUX plane? */ + case I915_FORMAT_MOD_Y_TILED: + case I915_FORMAT_MOD_Yf_TILED: + if (cpp == 8) + return 2048; + else + return 4096; + default: + MISSING_CASE(fb->modifier); + return 2048; + } +} + +static int glk_plane_max_width(const struct drm_framebuffer *fb, + int color_plane, + unsigned int rotation) +{ + int cpp = fb->format->cpp[color_plane]; + + switch (fb->modifier) { + case DRM_FORMAT_MOD_LINEAR: + case I915_FORMAT_MOD_X_TILED: + if (cpp == 8) + return 4096; + else + return 5120; + case I915_FORMAT_MOD_Y_TILED_CCS: + case I915_FORMAT_MOD_Yf_TILED_CCS: + /* FIXME AUX plane? */ + case I915_FORMAT_MOD_Y_TILED: + case I915_FORMAT_MOD_Yf_TILED: + if (cpp == 8) + return 2048; + else + return 5120; + default: + MISSING_CASE(fb->modifier); + return 2048; + } +} + +static int adl_plane_min_width(const struct drm_framebuffer *fb, + int color_plane, + unsigned int rotation) +{ + return 16 / fb->format->cpp[color_plane]; +} + +static int icl_plane_min_width(const struct drm_framebuffer *fb, + int color_plane, + unsigned int rotation) +{ + /* Wa_14011264657, Wa_14011050563: gen11+ */ + return 16 / fb->format->cpp[color_plane] + 2; +} + +static int xe3_plane_max_width(const struct drm_framebuffer *fb, + int color_plane, + unsigned int rotation) +{ + if (intel_format_info_is_yuv_semiplanar(fb->format, fb->modifier)) + return 4096; + else + return 6144; +} + +static int icl_hdr_plane_max_width(const struct drm_framebuffer *fb, + int color_plane, + unsigned int rotation) +{ + if (intel_format_info_is_yuv_semiplanar(fb->format, fb->modifier)) + return 4096; + else + return 5120; +} + +static int icl_sdr_plane_max_width(const struct drm_framebuffer *fb, + int color_plane, + unsigned int rotation) +{ + return 5120; +} + +static int skl_plane_max_height(const struct drm_framebuffer *fb, + int color_plane, + unsigned int rotation) +{ + return 4096; +} + +static enum intel_fbc_id skl_fbc_id_for_pipe(enum pipe pipe) +{ + return pipe - PIPE_A + INTEL_FBC_A; +} + +static bool skl_plane_has_fbc(struct intel_display *display, + enum intel_fbc_id fbc_id, enum plane_id plane_id) +{ + if ((DISPLAY_RUNTIME_INFO(display)->fbc_mask & BIT(fbc_id)) == 0) + return false; + + if (DISPLAY_VER(display) >= 20) + return icl_is_hdr_plane(display, plane_id); + else + return plane_id == PLANE_1; +} + +static int icl_plane_max_height(const struct drm_framebuffer *fb, + int color_plane, + unsigned int rotation) +{ + return 4320; +} + +static unsigned int +plane_max_stride(struct intel_plane *plane, + const struct drm_format_info *info, + u64 modifier, unsigned int rotation, + unsigned int max_pixels, + unsigned int max_bytes) +{ + int cpp = info->cpp[0]; + + if (drm_rotation_90_or_270(rotation)) + return min(max_pixels, max_bytes / cpp); + else + return min(max_pixels * cpp, max_bytes); +} + +static unsigned int +adl_plane_max_stride(struct intel_plane *plane, + const struct drm_format_info *info, + u64 modifier, unsigned int rotation) +{ + unsigned int max_pixels = 65536; /* PLANE_OFFSET limit */ + unsigned int max_bytes = 128 * 1024; + + return plane_max_stride(plane, info, + modifier, rotation, + max_pixels, max_bytes); +} + +static unsigned int +skl_plane_max_stride(struct intel_plane *plane, + const struct drm_format_info *info, + u64 modifier, unsigned int rotation) +{ + unsigned int max_pixels = 8192; /* PLANE_OFFSET limit */ + unsigned int max_bytes = 32 * 1024; + + return plane_max_stride(plane, info, + modifier, rotation, + max_pixels, max_bytes); +} + +static bool tgl_plane_can_async_flip(u64 modifier) +{ + switch (modifier) { + case DRM_FORMAT_MOD_LINEAR: + case I915_FORMAT_MOD_X_TILED: + case I915_FORMAT_MOD_Y_TILED: + case I915_FORMAT_MOD_4_TILED: + case I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS: + case I915_FORMAT_MOD_4_TILED_MTL_RC_CCS: + case I915_FORMAT_MOD_4_TILED_DG2_RC_CCS: + case I915_FORMAT_MOD_4_TILED_BMG_CCS: + case I915_FORMAT_MOD_4_TILED_LNL_CCS: + return true; + case I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS: + case I915_FORMAT_MOD_4_TILED_MTL_MC_CCS: + case I915_FORMAT_MOD_4_TILED_DG2_MC_CCS: + case I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS_CC: + case I915_FORMAT_MOD_4_TILED_MTL_RC_CCS_CC: + case I915_FORMAT_MOD_4_TILED_DG2_RC_CCS_CC: + return false; + default: + return false; + } +} + +static bool icl_plane_can_async_flip(u64 modifier) +{ + switch (modifier) { + case DRM_FORMAT_MOD_LINEAR: + /* + * FIXME: Async on Linear buffer is supported on ICL + * but with additional alignment and fbc restrictions + * need to be taken care of. + */ + return false; + case I915_FORMAT_MOD_X_TILED: + case I915_FORMAT_MOD_Y_TILED: + case I915_FORMAT_MOD_Yf_TILED: + case I915_FORMAT_MOD_Y_TILED_CCS: + case I915_FORMAT_MOD_Yf_TILED_CCS: + return true; + default: + return false; + } +} + +static bool skl_plane_can_async_flip(u64 modifier) +{ + switch (modifier) { + case DRM_FORMAT_MOD_LINEAR: + return false; + case I915_FORMAT_MOD_X_TILED: + case I915_FORMAT_MOD_Y_TILED: + case I915_FORMAT_MOD_Yf_TILED: + return true; + case I915_FORMAT_MOD_Y_TILED_CCS: + case I915_FORMAT_MOD_Yf_TILED_CCS: + /* + * Display WA #0731: skl + * WaDisableRCWithAsyncFlip: skl + * "When render decompression is enabled, hardware + * internally converts the Async flips to Sync flips." + * + * Display WA #1159: glk + * "Async flip with render compression may result in + * intermittent underrun corruption." + */ + return false; + default: + return false; + } +} + +static u32 tgl_plane_min_alignment(struct intel_plane *plane, + const struct drm_framebuffer *fb, + int color_plane) +{ + struct intel_display *display = to_intel_display(plane); + /* PLANE_SURF GGTT -> DPT alignment */ + int mult = intel_fb_uses_dpt(fb) ? 512 : 1; + + /* AUX_DIST needs only 4K alignment */ + if (intel_fb_is_ccs_aux_plane(fb, color_plane)) + return mult * 4 * 1024; + + /* + * FIXME ADL sees GGTT/DMAR faults with async + * flips unless we align to 16k at least. + * Figure out what's going on here... + */ + if (display->platform.alderlake_p && + intel_plane_can_async_flip(plane, fb->format->format, fb->modifier)) + return mult * 16 * 1024; + + switch (fb->modifier) { + case DRM_FORMAT_MOD_LINEAR: + case I915_FORMAT_MOD_X_TILED: + case I915_FORMAT_MOD_Y_TILED: + case I915_FORMAT_MOD_4_TILED: + return mult * 4 * 1024; + case I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS: + case I915_FORMAT_MOD_4_TILED_MTL_RC_CCS: + case I915_FORMAT_MOD_4_TILED_DG2_RC_CCS: + case I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS: + case I915_FORMAT_MOD_4_TILED_MTL_MC_CCS: + case I915_FORMAT_MOD_4_TILED_DG2_MC_CCS: + case I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS_CC: + case I915_FORMAT_MOD_4_TILED_MTL_RC_CCS_CC: + case I915_FORMAT_MOD_4_TILED_DG2_RC_CCS_CC: + case I915_FORMAT_MOD_4_TILED_BMG_CCS: + case I915_FORMAT_MOD_4_TILED_LNL_CCS: + /* + * Align to at least 4x1 main surface + * tiles (16K) to match 64B of AUX. + */ + return max(mult * 4 * 1024, 16 * 1024); + default: + MISSING_CASE(fb->modifier); + return 0; + } +} + +static u32 skl_plane_min_alignment(struct intel_plane *plane, + const struct drm_framebuffer *fb, + int color_plane) +{ + /* + * AUX_DIST needs only 4K alignment, + * as does ICL UV PLANE_SURF. + */ + if (color_plane != 0) + return 4 * 1024; + + /* + * VT-d needs at least 256k alignment, + * but that's already covered below. + */ + switch (fb->modifier) { + case DRM_FORMAT_MOD_LINEAR: + case I915_FORMAT_MOD_X_TILED: + return 256 * 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; + } +} + +/* Preoffset values for YUV to RGB Conversion */ +#define PREOFF_YUV_TO_RGB_HI 0x1800 +#define PREOFF_YUV_TO_RGB_ME 0x0000 +#define PREOFF_YUV_TO_RGB_LO 0x1800 + +#define ROFF(x) (((x) & 0xffff) << 16) +#define GOFF(x) (((x) & 0xffff) << 0) +#define BOFF(x) (((x) & 0xffff) << 16) + +/* + * Programs the input color space conversion stage for ICL HDR planes. + * Note that it is assumed that this stage always happens after YUV + * range correction. Thus, the input to this stage is assumed to be + * in full-range YCbCr. + */ +static void +icl_program_input_csc(struct intel_dsb *dsb, + struct intel_plane *plane, + const struct intel_plane_state *plane_state) +{ + struct intel_display *display = to_intel_display(plane); + enum pipe pipe = plane->pipe; + enum plane_id plane_id = plane->id; + + static const u16 input_csc_matrix[][9] = { + /* + * BT.601 full range YCbCr -> full range RGB + * The matrix required is : + * [1.000, 0.000, 1.371, + * 1.000, -0.336, -0.698, + * 1.000, 1.732, 0.0000] + */ + [DRM_COLOR_YCBCR_BT601] = { + 0x7AF8, 0x7800, 0x0, + 0x8B28, 0x7800, 0x9AC0, + 0x0, 0x7800, 0x7DD8, + }, + /* + * BT.709 full range YCbCr -> full range RGB + * The matrix required is : + * [1.000, 0.000, 1.574, + * 1.000, -0.187, -0.468, + * 1.000, 1.855, 0.0000] + */ + [DRM_COLOR_YCBCR_BT709] = { + 0x7C98, 0x7800, 0x0, + 0x9EF8, 0x7800, 0xAC00, + 0x0, 0x7800, 0x7ED8, + }, + /* + * BT.2020 full range YCbCr -> full range RGB + * The matrix required is : + * [1.000, 0.000, 1.474, + * 1.000, -0.1645, -0.5713, + * 1.000, 1.8814, 0.0000] + */ + [DRM_COLOR_YCBCR_BT2020] = { + 0x7BC8, 0x7800, 0x0, + 0x8928, 0x7800, 0xAA88, + 0x0, 0x7800, 0x7F10, + }, + }; + const u16 *csc = input_csc_matrix[plane_state->hw.color_encoding]; + + intel_de_write_dsb(display, dsb, PLANE_INPUT_CSC_COEFF(pipe, plane_id, 0), + ROFF(csc[0]) | GOFF(csc[1])); + intel_de_write_dsb(display, dsb, PLANE_INPUT_CSC_COEFF(pipe, plane_id, 1), + BOFF(csc[2])); + intel_de_write_dsb(display, dsb, PLANE_INPUT_CSC_COEFF(pipe, plane_id, 2), + ROFF(csc[3]) | GOFF(csc[4])); + intel_de_write_dsb(display, dsb, PLANE_INPUT_CSC_COEFF(pipe, plane_id, 3), + BOFF(csc[5])); + intel_de_write_dsb(display, dsb, PLANE_INPUT_CSC_COEFF(pipe, plane_id, 4), + ROFF(csc[6]) | GOFF(csc[7])); + intel_de_write_dsb(display, dsb, PLANE_INPUT_CSC_COEFF(pipe, plane_id, 5), + BOFF(csc[8])); + + intel_de_write_dsb(display, dsb, PLANE_INPUT_CSC_PREOFF(pipe, plane_id, 0), + PREOFF_YUV_TO_RGB_HI); + intel_de_write_dsb(display, dsb, PLANE_INPUT_CSC_PREOFF(pipe, plane_id, 1), + PREOFF_YUV_TO_RGB_ME); + intel_de_write_dsb(display, dsb, PLANE_INPUT_CSC_PREOFF(pipe, plane_id, 2), + PREOFF_YUV_TO_RGB_LO); + intel_de_write_dsb(display, dsb, + PLANE_INPUT_CSC_POSTOFF(pipe, plane_id, 0), 0x0); + intel_de_write_dsb(display, dsb, + PLANE_INPUT_CSC_POSTOFF(pipe, plane_id, 1), 0x0); + intel_de_write_dsb(display, dsb, + PLANE_INPUT_CSC_POSTOFF(pipe, plane_id, 2), 0x0); +} + +static unsigned int skl_plane_stride_mult(const struct drm_framebuffer *fb, + int color_plane, unsigned int rotation) +{ + /* + * The stride is either expressed as a multiple of 64 bytes chunks for + * linear buffers or in number of tiles for tiled buffers. + */ + if (is_surface_linear(fb, color_plane)) + return 64; + else if (drm_rotation_90_or_270(rotation)) + return intel_tile_height(fb, color_plane); + else + return intel_tile_width_bytes(fb, color_plane); +} + +static u32 skl_plane_stride(const struct intel_plane_state *plane_state, + int color_plane) +{ + const struct drm_framebuffer *fb = plane_state->hw.fb; + unsigned int rotation = plane_state->hw.rotation; + u32 stride = plane_state->view.color_plane[color_plane].scanout_stride; + + if (color_plane >= fb->format->num_planes) + return 0; + + return stride / skl_plane_stride_mult(fb, color_plane, rotation); +} + +static u32 skl_plane_ddb_reg_val(const struct skl_ddb_entry *entry) +{ + if (!entry->end) + return 0; + + return PLANE_BUF_END(entry->end - 1) | + PLANE_BUF_START(entry->start); +} + +static u32 xe3_plane_min_ddb_reg_val(const u16 *min_ddb, + const u16 *interim_ddb) +{ + u32 val = 0; + + if (*min_ddb) + val |= PLANE_MIN_DBUF_BLOCKS(*min_ddb); + + if (*interim_ddb) + val |= PLANE_INTERIM_DBUF_BLOCKS(*interim_ddb); + + val |= val ? PLANE_AUTO_MIN_DBUF_EN : 0; + + return val; +} + +static u32 skl_plane_wm_reg_val(const struct skl_wm_level *level) +{ + u32 val = 0; + + if (level->enable) + val |= PLANE_WM_EN; + if (level->ignore_lines) + val |= PLANE_WM_IGNORE_LINES; + if (level->auto_min_alloc_wm_enable) + val |= PLANE_WM_AUTO_MIN_ALLOC_EN; + + val |= REG_FIELD_PREP(PLANE_WM_BLOCKS_MASK, level->blocks); + val |= REG_FIELD_PREP(PLANE_WM_LINES_MASK, level->lines); + + return val; +} + +static void skl_write_plane_wm(struct intel_dsb *dsb, + struct intel_plane *plane, + const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(plane); + enum plane_id plane_id = plane->id; + enum pipe pipe = plane->pipe; + const struct skl_pipe_wm *pipe_wm = &crtc_state->wm.skl.optimal; + const struct skl_ddb_entry *ddb = + &crtc_state->wm.skl.plane_ddb[plane_id]; + const struct skl_ddb_entry *ddb_y = + &crtc_state->wm.skl.plane_ddb_y[plane_id]; + const u16 *min_ddb = &crtc_state->wm.skl.plane_min_ddb[plane_id]; + const u16 *interim_ddb = + &crtc_state->wm.skl.plane_interim_ddb[plane_id]; + int level; + + for (level = 0; level < display->wm.num_levels; level++) + intel_de_write_dsb(display, dsb, PLANE_WM(pipe, plane_id, level), + skl_plane_wm_reg_val(skl_plane_wm_level(pipe_wm, plane_id, level))); + + intel_de_write_dsb(display, dsb, PLANE_WM_TRANS(pipe, plane_id), + skl_plane_wm_reg_val(skl_plane_trans_wm(pipe_wm, plane_id))); + + if (HAS_HW_SAGV_WM(display)) { + const struct skl_plane_wm *wm = &pipe_wm->planes[plane_id]; + + intel_de_write_dsb(display, dsb, PLANE_WM_SAGV(pipe, plane_id), + skl_plane_wm_reg_val(&wm->sagv.wm0)); + intel_de_write_dsb(display, dsb, PLANE_WM_SAGV_TRANS(pipe, plane_id), + skl_plane_wm_reg_val(&wm->sagv.trans_wm)); + } + + intel_de_write_dsb(display, dsb, PLANE_BUF_CFG(pipe, plane_id), + skl_plane_ddb_reg_val(ddb)); + + if (DISPLAY_VER(display) < 11) + intel_de_write_dsb(display, dsb, PLANE_NV12_BUF_CFG(pipe, plane_id), + skl_plane_ddb_reg_val(ddb_y)); + + if (DISPLAY_VER(display) >= 30) + intel_de_write_dsb(display, dsb, PLANE_MIN_BUF_CFG(pipe, plane_id), + xe3_plane_min_ddb_reg_val(min_ddb, interim_ddb)); +} + +static void +skl_plane_disable_arm(struct intel_dsb *dsb, + struct intel_plane *plane, + const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(plane); + enum plane_id plane_id = plane->id; + enum pipe pipe = plane->pipe; + + skl_write_plane_wm(dsb, plane, crtc_state); + + intel_de_write_dsb(display, dsb, PLANE_CTL(pipe, plane_id), 0); + intel_de_write_dsb(display, dsb, PLANE_SURF(pipe, plane_id), 0); +} + +static void icl_plane_disable_sel_fetch_arm(struct intel_dsb *dsb, + struct intel_plane *plane, + const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(plane); + enum pipe pipe = plane->pipe; + + if (!crtc_state->enable_psr2_sel_fetch) + return; + + intel_de_write_dsb(display, dsb, SEL_FETCH_PLANE_CTL(pipe, plane->id), 0); +} + +static void x3p_lpd_plane_update_pixel_normalizer(struct intel_dsb *dsb, + struct intel_plane *plane, + bool enable) +{ + struct intel_display *display = to_intel_display(plane); + enum intel_fbc_id fbc_id = skl_fbc_id_for_pipe(plane->pipe); + u32 val; + + /* Only HDR planes have pixel normalizer and don't matter if no FBC */ + if (!skl_plane_has_fbc(display, fbc_id, plane->id)) + return; + + val = enable ? PLANE_PIXEL_NORMALIZE_NORM_FACTOR(PLANE_PIXEL_NORMALIZE_NORM_FACTOR_1_0) | + PLANE_PIXEL_NORMALIZE_ENABLE : 0; + + intel_de_write_dsb(display, dsb, + PLANE_PIXEL_NORMALIZE(plane->pipe, plane->id), val); +} + +static void +icl_plane_disable_arm(struct intel_dsb *dsb, + struct intel_plane *plane, + const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(plane); + enum plane_id plane_id = plane->id; + enum pipe pipe = plane->pipe; + + if (icl_is_hdr_plane(display, plane_id)) + intel_de_write_dsb(display, dsb, PLANE_CUS_CTL(pipe, plane_id), 0); + + skl_write_plane_wm(dsb, plane, crtc_state); + + icl_plane_disable_sel_fetch_arm(dsb, plane, crtc_state); + + if (DISPLAY_VER(display) >= 35) + x3p_lpd_plane_update_pixel_normalizer(dsb, plane, false); + + intel_de_write_dsb(display, dsb, PLANE_CTL(pipe, plane_id), 0); + intel_de_write_dsb(display, dsb, PLANE_SURF(pipe, plane_id), 0); +} + +static bool +skl_plane_get_hw_state(struct intel_plane *plane, + enum pipe *pipe) +{ + struct intel_display *display = to_intel_display(plane); + enum intel_display_power_domain power_domain; + enum plane_id plane_id = plane->id; + intel_wakeref_t wakeref; + bool ret; + + power_domain = POWER_DOMAIN_PIPE(plane->pipe); + wakeref = intel_display_power_get_if_enabled(display, power_domain); + if (!wakeref) + return false; + + ret = intel_de_read(display, PLANE_CTL(plane->pipe, plane_id)) & PLANE_CTL_ENABLE; + + *pipe = plane->pipe; + + intel_display_power_put(display, power_domain, wakeref); + + return ret; +} + +static u32 skl_plane_ctl_format(u32 pixel_format) +{ + switch (pixel_format) { + case DRM_FORMAT_C8: + return PLANE_CTL_FORMAT_INDEXED; + case DRM_FORMAT_RGB565: + return PLANE_CTL_FORMAT_RGB_565; + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + return PLANE_CTL_FORMAT_XRGB_8888 | PLANE_CTL_ORDER_RGBX; + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + return PLANE_CTL_FORMAT_XRGB_8888; + case DRM_FORMAT_XBGR2101010: + case DRM_FORMAT_ABGR2101010: + return PLANE_CTL_FORMAT_XRGB_2101010 | PLANE_CTL_ORDER_RGBX; + case DRM_FORMAT_XRGB2101010: + case DRM_FORMAT_ARGB2101010: + return PLANE_CTL_FORMAT_XRGB_2101010; + case DRM_FORMAT_XBGR16161616F: + case DRM_FORMAT_ABGR16161616F: + return PLANE_CTL_FORMAT_XRGB_16161616F | PLANE_CTL_ORDER_RGBX; + case DRM_FORMAT_XRGB16161616F: + case DRM_FORMAT_ARGB16161616F: + return PLANE_CTL_FORMAT_XRGB_16161616F; + case DRM_FORMAT_XYUV8888: + return PLANE_CTL_FORMAT_XYUV; + case DRM_FORMAT_YUYV: + return PLANE_CTL_FORMAT_YUV422 | PLANE_CTL_YUV422_ORDER_YUYV; + case DRM_FORMAT_YVYU: + return PLANE_CTL_FORMAT_YUV422 | PLANE_CTL_YUV422_ORDER_YVYU; + case DRM_FORMAT_UYVY: + return PLANE_CTL_FORMAT_YUV422 | PLANE_CTL_YUV422_ORDER_UYVY; + case DRM_FORMAT_VYUY: + return PLANE_CTL_FORMAT_YUV422 | PLANE_CTL_YUV422_ORDER_VYUY; + case DRM_FORMAT_NV12: + return PLANE_CTL_FORMAT_NV12; + case DRM_FORMAT_P010: + return PLANE_CTL_FORMAT_P010; + case DRM_FORMAT_P012: + return PLANE_CTL_FORMAT_P012; + case DRM_FORMAT_P016: + return PLANE_CTL_FORMAT_P016; + case DRM_FORMAT_Y210: + return PLANE_CTL_FORMAT_Y210; + case DRM_FORMAT_Y212: + return PLANE_CTL_FORMAT_Y212; + case DRM_FORMAT_Y216: + return PLANE_CTL_FORMAT_Y216; + case DRM_FORMAT_XVYU2101010: + return PLANE_CTL_FORMAT_Y410; + case DRM_FORMAT_XVYU12_16161616: + return PLANE_CTL_FORMAT_Y412; + case DRM_FORMAT_XVYU16161616: + return PLANE_CTL_FORMAT_Y416; + default: + MISSING_CASE(pixel_format); + } + + return 0; +} + +static u32 skl_plane_ctl_alpha(const struct intel_plane_state *plane_state) +{ + if (!plane_state->hw.fb->format->has_alpha) + return PLANE_CTL_ALPHA_DISABLE; + + switch (plane_state->hw.pixel_blend_mode) { + case DRM_MODE_BLEND_PIXEL_NONE: + return PLANE_CTL_ALPHA_DISABLE; + case DRM_MODE_BLEND_PREMULTI: + return PLANE_CTL_ALPHA_SW_PREMULTIPLY; + case DRM_MODE_BLEND_COVERAGE: + return PLANE_CTL_ALPHA_HW_PREMULTIPLY; + default: + MISSING_CASE(plane_state->hw.pixel_blend_mode); + return PLANE_CTL_ALPHA_DISABLE; + } +} + +static u32 glk_plane_color_ctl_alpha(const struct intel_plane_state *plane_state) +{ + if (!plane_state->hw.fb->format->has_alpha) + return PLANE_COLOR_ALPHA_DISABLE; + + switch (plane_state->hw.pixel_blend_mode) { + case DRM_MODE_BLEND_PIXEL_NONE: + return PLANE_COLOR_ALPHA_DISABLE; + case DRM_MODE_BLEND_PREMULTI: + return PLANE_COLOR_ALPHA_SW_PREMULTIPLY; + case DRM_MODE_BLEND_COVERAGE: + return PLANE_COLOR_ALPHA_HW_PREMULTIPLY; + default: + MISSING_CASE(plane_state->hw.pixel_blend_mode); + return PLANE_COLOR_ALPHA_DISABLE; + } +} + +static u32 skl_plane_ctl_tiling(u64 fb_modifier) +{ + switch (fb_modifier) { + case DRM_FORMAT_MOD_LINEAR: + break; + case I915_FORMAT_MOD_X_TILED: + return PLANE_CTL_TILED_X; + case I915_FORMAT_MOD_Y_TILED: + return PLANE_CTL_TILED_Y; + case I915_FORMAT_MOD_4_TILED: + return PLANE_CTL_TILED_4; + case I915_FORMAT_MOD_4_TILED_DG2_RC_CCS: + return PLANE_CTL_TILED_4 | + PLANE_CTL_RENDER_DECOMPRESSION_ENABLE | + PLANE_CTL_CLEAR_COLOR_DISABLE; + case I915_FORMAT_MOD_4_TILED_DG2_MC_CCS: + return PLANE_CTL_TILED_4 | + PLANE_CTL_MEDIA_DECOMPRESSION_ENABLE | + PLANE_CTL_CLEAR_COLOR_DISABLE; + case I915_FORMAT_MOD_4_TILED_DG2_RC_CCS_CC: + return PLANE_CTL_TILED_4 | PLANE_CTL_RENDER_DECOMPRESSION_ENABLE; + case I915_FORMAT_MOD_4_TILED_MTL_RC_CCS: + return PLANE_CTL_TILED_4 | + PLANE_CTL_RENDER_DECOMPRESSION_ENABLE | + PLANE_CTL_CLEAR_COLOR_DISABLE; + case I915_FORMAT_MOD_4_TILED_MTL_RC_CCS_CC: + return PLANE_CTL_TILED_4 | PLANE_CTL_RENDER_DECOMPRESSION_ENABLE; + case I915_FORMAT_MOD_4_TILED_MTL_MC_CCS: + return PLANE_CTL_TILED_4 | PLANE_CTL_MEDIA_DECOMPRESSION_ENABLE; + case I915_FORMAT_MOD_4_TILED_BMG_CCS: + case I915_FORMAT_MOD_4_TILED_LNL_CCS: + return PLANE_CTL_TILED_4 | PLANE_CTL_RENDER_DECOMPRESSION_ENABLE; + case I915_FORMAT_MOD_Y_TILED_CCS: + case I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS_CC: + return PLANE_CTL_TILED_Y | PLANE_CTL_RENDER_DECOMPRESSION_ENABLE; + case I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS: + return PLANE_CTL_TILED_Y | + PLANE_CTL_RENDER_DECOMPRESSION_ENABLE | + PLANE_CTL_CLEAR_COLOR_DISABLE; + case I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS: + return PLANE_CTL_TILED_Y | PLANE_CTL_MEDIA_DECOMPRESSION_ENABLE; + case I915_FORMAT_MOD_Yf_TILED: + return PLANE_CTL_TILED_YF; + case I915_FORMAT_MOD_Yf_TILED_CCS: + return PLANE_CTL_TILED_YF | PLANE_CTL_RENDER_DECOMPRESSION_ENABLE; + default: + MISSING_CASE(fb_modifier); + } + + return 0; +} + +static u32 skl_plane_ctl_rotate(unsigned int rotate) +{ + switch (rotate) { + case DRM_MODE_ROTATE_0: + break; + /* + * DRM_MODE_ROTATE_ is counter clockwise to stay compatible with Xrandr + * while i915 HW rotation is clockwise, that's why this swapping. + */ + case DRM_MODE_ROTATE_90: + return PLANE_CTL_ROTATE_270; + case DRM_MODE_ROTATE_180: + return PLANE_CTL_ROTATE_180; + case DRM_MODE_ROTATE_270: + return PLANE_CTL_ROTATE_90; + default: + MISSING_CASE(rotate); + } + + return 0; +} + +static u32 icl_plane_ctl_flip(unsigned int reflect) +{ + switch (reflect) { + case 0: + break; + case DRM_MODE_REFLECT_X: + return PLANE_CTL_FLIP_HORIZONTAL; + case DRM_MODE_REFLECT_Y: + default: + MISSING_CASE(reflect); + } + + return 0; +} + +static u32 adlp_plane_ctl_arb_slots(const struct intel_plane_state *plane_state) +{ + const struct drm_framebuffer *fb = plane_state->hw.fb; + + if (intel_format_info_is_yuv_semiplanar(fb->format, fb->modifier)) { + switch (fb->format->cpp[0]) { + case 2: + return PLANE_CTL_ARB_SLOTS(1); + default: + return PLANE_CTL_ARB_SLOTS(0); + } + } else { + switch (fb->format->cpp[0]) { + case 8: + return PLANE_CTL_ARB_SLOTS(3); + case 4: + return PLANE_CTL_ARB_SLOTS(1); + default: + return PLANE_CTL_ARB_SLOTS(0); + } + } +} + +static u32 skl_plane_ctl_crtc(const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + u32 plane_ctl = 0; + + if (DISPLAY_VER(display) >= 10) + return plane_ctl; + + if (crtc_state->gamma_enable) + plane_ctl |= PLANE_CTL_PIPE_GAMMA_ENABLE; + + if (crtc_state->csc_enable) + plane_ctl |= PLANE_CTL_PIPE_CSC_ENABLE; + + return plane_ctl; +} + +static u32 skl_plane_ctl(const struct intel_plane_state *plane_state) +{ + struct intel_display *display = to_intel_display(plane_state); + const struct drm_framebuffer *fb = plane_state->hw.fb; + unsigned int rotation = plane_state->hw.rotation; + const struct drm_intel_sprite_colorkey *key = &plane_state->ckey; + u32 plane_ctl; + + plane_ctl = PLANE_CTL_ENABLE; + + if (DISPLAY_VER(display) < 10) { + plane_ctl |= skl_plane_ctl_alpha(plane_state); + plane_ctl |= PLANE_CTL_PLANE_GAMMA_DISABLE; + + if (plane_state->hw.color_encoding == DRM_COLOR_YCBCR_BT709) + plane_ctl |= PLANE_CTL_YUV_TO_RGB_CSC_FORMAT_BT709; + + if (plane_state->hw.color_range == DRM_COLOR_YCBCR_FULL_RANGE) + plane_ctl |= PLANE_CTL_YUV_RANGE_CORRECTION_DISABLE; + } + + plane_ctl |= skl_plane_ctl_format(fb->format->format); + plane_ctl |= skl_plane_ctl_tiling(fb->modifier); + plane_ctl |= skl_plane_ctl_rotate(rotation & DRM_MODE_ROTATE_MASK); + + if (DISPLAY_VER(display) >= 11) + plane_ctl |= icl_plane_ctl_flip(rotation & + DRM_MODE_REFLECT_MASK); + + if (key->flags & I915_SET_COLORKEY_DESTINATION) + plane_ctl |= PLANE_CTL_KEY_ENABLE_DESTINATION; + else if (key->flags & I915_SET_COLORKEY_SOURCE) + plane_ctl |= PLANE_CTL_KEY_ENABLE_SOURCE; + + /* Wa_22012358565:adl-p */ + if (DISPLAY_VER(display) == 13) + plane_ctl |= adlp_plane_ctl_arb_slots(plane_state); + + return plane_ctl; +} + +static u32 glk_plane_color_ctl_crtc(const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + u32 plane_color_ctl = 0; + + if (DISPLAY_VER(display) >= 11) + return plane_color_ctl; + + if (crtc_state->gamma_enable) + plane_color_ctl |= PLANE_COLOR_PIPE_GAMMA_ENABLE; + + if (crtc_state->csc_enable) + plane_color_ctl |= PLANE_COLOR_PIPE_CSC_ENABLE; + + return plane_color_ctl; +} + +static u32 glk_plane_color_ctl(const struct intel_plane_state *plane_state) +{ + struct intel_display *display = to_intel_display(plane_state); + const struct drm_framebuffer *fb = plane_state->hw.fb; + struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); + u32 plane_color_ctl = 0; + + plane_color_ctl |= PLANE_COLOR_PLANE_GAMMA_DISABLE; + plane_color_ctl |= glk_plane_color_ctl_alpha(plane_state); + + if (fb->format->is_yuv && !icl_is_hdr_plane(display, plane->id)) { + switch (plane_state->hw.color_encoding) { + case DRM_COLOR_YCBCR_BT709: + plane_color_ctl |= PLANE_COLOR_CSC_MODE_YUV709_TO_RGB709; + break; + case DRM_COLOR_YCBCR_BT2020: + plane_color_ctl |= + PLANE_COLOR_CSC_MODE_YUV2020_TO_RGB2020; + break; + default: + plane_color_ctl |= + PLANE_COLOR_CSC_MODE_YUV601_TO_RGB601; + } + if (plane_state->hw.color_range == DRM_COLOR_YCBCR_FULL_RANGE) + plane_color_ctl |= PLANE_COLOR_YUV_RANGE_CORRECTION_DISABLE; + } else if (fb->format->is_yuv) { + plane_color_ctl |= PLANE_COLOR_INPUT_CSC_ENABLE; + if (plane_state->hw.color_range == DRM_COLOR_YCBCR_FULL_RANGE) + plane_color_ctl |= PLANE_COLOR_YUV_RANGE_CORRECTION_DISABLE; + } + + if (plane_state->force_black) + plane_color_ctl |= PLANE_COLOR_PLANE_CSC_ENABLE; + + if (plane_state->hw.degamma_lut) + plane_color_ctl |= PLANE_COLOR_PRE_CSC_GAMMA_ENABLE; + + if (plane_state->hw.ctm) + plane_color_ctl |= PLANE_COLOR_PLANE_CSC_ENABLE; + + if (plane_state->hw.gamma_lut) { + plane_color_ctl &= ~PLANE_COLOR_PLANE_GAMMA_DISABLE; + if (drm_color_lut32_size(plane_state->hw.gamma_lut) != 32) + plane_color_ctl |= PLANE_COLOR_POST_CSC_GAMMA_MULTSEG_ENABLE; + } + + return plane_color_ctl; +} + +static u32 skl_surf_address(const struct intel_plane_state *plane_state, + int color_plane) +{ + struct intel_display *display = to_intel_display(plane_state); + const struct drm_framebuffer *fb = plane_state->hw.fb; + u32 offset = plane_state->view.color_plane[color_plane].offset; + + if (intel_fb_uses_dpt(fb)) { + drm_WARN_ON(display->drm, offset & 0x1fffff); + return offset >> 9; + } else { + drm_WARN_ON(display->drm, offset & 0xfff); + return offset; + } +} + +static int icl_plane_color_plane(const struct intel_plane_state *plane_state) +{ + if (plane_state->planar_linked_plane && !plane_state->is_y_plane) + return 1; + else + return 0; +} + +static u32 skl_plane_surf_offset(const struct intel_plane_state *plane_state) +{ + int color_plane = icl_plane_color_plane(plane_state); + u32 plane_surf; + + plane_surf = skl_surf_address(plane_state, color_plane); + + if (plane_state->decrypt) + plane_surf |= PLANE_SURF_DECRYPT; + + return plane_surf; +} + +u32 skl_plane_aux_dist(const struct intel_plane_state *plane_state, + int color_plane) +{ + struct intel_display *display = to_intel_display(plane_state); + const struct drm_framebuffer *fb = plane_state->hw.fb; + int aux_plane = skl_main_to_aux_plane(fb, color_plane); + u32 aux_dist; + + if (!aux_plane) + return 0; + + aux_dist = skl_surf_address(plane_state, aux_plane) - + skl_surf_address(plane_state, color_plane); + + if (DISPLAY_VER(display) < 12) + aux_dist |= PLANE_AUX_STRIDE(skl_plane_stride(plane_state, aux_plane)); + + return aux_dist; +} + +static u32 skl_plane_keyval(const struct intel_plane_state *plane_state) +{ + const struct drm_intel_sprite_colorkey *key = &plane_state->ckey; + + return key->min_value; +} + +static u32 skl_plane_keymax(const struct intel_plane_state *plane_state) +{ + const struct drm_intel_sprite_colorkey *key = &plane_state->ckey; + u8 alpha = plane_state->hw.alpha >> 8; + + return (key->max_value & 0xffffff) | PLANE_KEYMAX_ALPHA(alpha); +} + +static u32 skl_plane_keymsk(const struct intel_plane_state *plane_state) +{ + const struct drm_intel_sprite_colorkey *key = &plane_state->ckey; + u8 alpha = plane_state->hw.alpha >> 8; + u32 keymsk; + + keymsk = key->channel_mask & 0x7ffffff; + if (alpha < 0xff) + keymsk |= PLANE_KEYMSK_ALPHA_ENABLE; + + return keymsk; +} + +static void icl_plane_csc_load_black(struct intel_dsb *dsb, + struct intel_plane *plane, + const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(plane); + enum plane_id plane_id = plane->id; + enum pipe pipe = plane->pipe; + + intel_de_write_dsb(display, dsb, PLANE_CSC_COEFF(pipe, plane_id, 0), 0); + intel_de_write_dsb(display, dsb, PLANE_CSC_COEFF(pipe, plane_id, 1), 0); + + intel_de_write_dsb(display, dsb, PLANE_CSC_COEFF(pipe, plane_id, 2), 0); + intel_de_write_dsb(display, dsb, PLANE_CSC_COEFF(pipe, plane_id, 3), 0); + + intel_de_write_dsb(display, dsb, PLANE_CSC_COEFF(pipe, plane_id, 4), 0); + intel_de_write_dsb(display, dsb, PLANE_CSC_COEFF(pipe, plane_id, 5), 0); + + intel_de_write_dsb(display, dsb, PLANE_CSC_PREOFF(pipe, plane_id, 0), 0); + intel_de_write_dsb(display, dsb, PLANE_CSC_PREOFF(pipe, plane_id, 1), 0); + intel_de_write_dsb(display, dsb, PLANE_CSC_PREOFF(pipe, plane_id, 2), 0); + + intel_de_write_dsb(display, dsb, PLANE_CSC_POSTOFF(pipe, plane_id, 0), 0); + intel_de_write_dsb(display, dsb, PLANE_CSC_POSTOFF(pipe, plane_id, 1), 0); + intel_de_write_dsb(display, dsb, PLANE_CSC_POSTOFF(pipe, plane_id, 2), 0); +} + +static void +skl_plane_update_noarm(struct intel_dsb *dsb, + struct intel_plane *plane, + const struct intel_crtc_state *crtc_state, + const struct intel_plane_state *plane_state) +{ + struct intel_display *display = to_intel_display(plane); + enum plane_id plane_id = plane->id; + enum pipe pipe = plane->pipe; + u32 stride = skl_plane_stride(plane_state, 0); + int crtc_x = plane_state->uapi.dst.x1; + int crtc_y = plane_state->uapi.dst.y1; + u32 src_w = drm_rect_width(&plane_state->uapi.src) >> 16; + u32 src_h = drm_rect_height(&plane_state->uapi.src) >> 16; + + /* The scaler will handle the output position */ + if (plane_state->scaler_id >= 0) { + crtc_x = 0; + crtc_y = 0; + } + + intel_de_write_dsb(display, dsb, PLANE_STRIDE(pipe, plane_id), + PLANE_STRIDE_(stride)); + intel_de_write_dsb(display, dsb, PLANE_POS(pipe, plane_id), + PLANE_POS_Y(crtc_y) | PLANE_POS_X(crtc_x)); + intel_de_write_dsb(display, dsb, PLANE_SIZE(pipe, plane_id), + PLANE_HEIGHT(src_h - 1) | PLANE_WIDTH(src_w - 1)); + + skl_write_plane_wm(dsb, plane, crtc_state); +} + +static void +skl_plane_update_arm(struct intel_dsb *dsb, + struct intel_plane *plane, + const struct intel_crtc_state *crtc_state, + const struct intel_plane_state *plane_state) +{ + struct intel_display *display = to_intel_display(plane); + enum plane_id plane_id = plane->id; + enum pipe pipe = plane->pipe; + u32 x = plane_state->view.color_plane[0].x; + u32 y = plane_state->view.color_plane[0].y; + u32 plane_ctl, plane_color_ctl = 0; + + plane_ctl = plane_state->ctl | + skl_plane_ctl_crtc(crtc_state); + + /* see intel_plane_atomic_calc_changes() */ + if (plane->need_async_flip_toggle_wa && + crtc_state->async_flip_planes & BIT(plane->id)) + plane_ctl |= PLANE_CTL_ASYNC_FLIP; + + if (DISPLAY_VER(display) >= 10) + plane_color_ctl = plane_state->color_ctl | + glk_plane_color_ctl_crtc(crtc_state); + + intel_de_write_dsb(display, dsb, PLANE_KEYVAL(pipe, plane_id), + skl_plane_keyval(plane_state)); + intel_de_write_dsb(display, dsb, PLANE_KEYMSK(pipe, plane_id), + skl_plane_keymsk(plane_state)); + intel_de_write_dsb(display, dsb, PLANE_KEYMAX(pipe, plane_id), + skl_plane_keymax(plane_state)); + + intel_de_write_dsb(display, dsb, PLANE_OFFSET(pipe, plane_id), + PLANE_OFFSET_Y(y) | PLANE_OFFSET_X(x)); + + intel_de_write_dsb(display, dsb, PLANE_AUX_DIST(pipe, plane_id), + skl_plane_aux_dist(plane_state, 0)); + + intel_de_write_dsb(display, dsb, PLANE_AUX_OFFSET(pipe, plane_id), + PLANE_OFFSET_Y(plane_state->view.color_plane[1].y) | + PLANE_OFFSET_X(plane_state->view.color_plane[1].x)); + + if (DISPLAY_VER(display) >= 10) + intel_de_write_dsb(display, dsb, PLANE_COLOR_CTL(pipe, plane_id), + plane_color_ctl); + + /* + * Enable the scaler before the plane so that we don't + * get a catastrophic underrun even if the two operations + * end up happening in two different frames. + * + * TODO: split into noarm+arm pair + */ + if (plane_state->scaler_id >= 0) + skl_program_plane_scaler(dsb, plane, crtc_state, plane_state); + + /* + * The control register self-arms if the plane was previously + * disabled. Try to make the plane enable atomic by writing + * the control register just before the surface register. + */ + intel_de_write_dsb(display, dsb, PLANE_CTL(pipe, plane_id), + plane_ctl); + intel_de_write_dsb(display, dsb, PLANE_SURF(pipe, plane_id), + plane_state->surf); +} + +static void icl_plane_update_sel_fetch_noarm(struct intel_dsb *dsb, + struct intel_plane *plane, + const struct intel_crtc_state *crtc_state, + const struct intel_plane_state *plane_state, + int color_plane) +{ + struct intel_display *display = to_intel_display(plane); + enum pipe pipe = plane->pipe; + const struct drm_rect *clip; + u32 val; + int x, y; + + if (!crtc_state->enable_psr2_sel_fetch) + return; + + clip = &plane_state->psr2_sel_fetch_area; + + if (crtc_state->enable_psr2_su_region_et) + y = max(0, plane_state->uapi.dst.y1 - crtc_state->psr2_su_area.y1); + else + y = (clip->y1 + plane_state->uapi.dst.y1); + val = y << 16; + val |= plane_state->uapi.dst.x1; + intel_de_write_dsb(display, dsb, SEL_FETCH_PLANE_POS(pipe, plane->id), val); + + x = plane_state->view.color_plane[color_plane].x; + + /* + * From Bspec: UV surface Start Y Position = half of Y plane Y + * start position. + */ + if (!color_plane) + y = plane_state->view.color_plane[color_plane].y + clip->y1; + else + y = plane_state->view.color_plane[color_plane].y + clip->y1 / 2; + + val = y << 16 | x; + + intel_de_write_dsb(display, dsb, SEL_FETCH_PLANE_OFFSET(pipe, plane->id), val); + + /* Sizes are 0 based */ + val = (drm_rect_height(clip) - 1) << 16; + val |= (drm_rect_width(&plane_state->uapi.src) >> 16) - 1; + intel_de_write_dsb(display, dsb, SEL_FETCH_PLANE_SIZE(pipe, plane->id), val); +} + +static void +icl_plane_update_noarm(struct intel_dsb *dsb, + struct intel_plane *plane, + const struct intel_crtc_state *crtc_state, + const struct intel_plane_state *plane_state) +{ + struct intel_display *display = to_intel_display(plane); + enum plane_id plane_id = plane->id; + enum pipe pipe = plane->pipe; + int color_plane = icl_plane_color_plane(plane_state); + u32 stride = skl_plane_stride(plane_state, color_plane); + const struct drm_framebuffer *fb = plane_state->hw.fb; + int crtc_x = plane_state->uapi.dst.x1; + int crtc_y = plane_state->uapi.dst.y1; + int x = plane_state->view.color_plane[color_plane].x; + int y = plane_state->view.color_plane[color_plane].y; + int src_w = drm_rect_width(&plane_state->uapi.src) >> 16; + int src_h = drm_rect_height(&plane_state->uapi.src) >> 16; + u32 plane_color_ctl; + + plane_color_ctl = plane_state->color_ctl | + glk_plane_color_ctl_crtc(crtc_state); + + intel_color_plane_program_pipeline(dsb, plane_state); + + /* The scaler will handle the output position */ + if (plane_state->scaler_id >= 0) { + crtc_x = 0; + crtc_y = 0; + } + + intel_de_write_dsb(display, dsb, PLANE_STRIDE(pipe, plane_id), + PLANE_STRIDE_(stride)); + intel_de_write_dsb(display, dsb, PLANE_POS(pipe, plane_id), + PLANE_POS_Y(crtc_y) | PLANE_POS_X(crtc_x)); + intel_de_write_dsb(display, dsb, PLANE_SIZE(pipe, plane_id), + PLANE_HEIGHT(src_h - 1) | PLANE_WIDTH(src_w - 1)); + + intel_de_write_dsb(display, dsb, PLANE_KEYVAL(pipe, plane_id), + skl_plane_keyval(plane_state)); + intel_de_write_dsb(display, dsb, PLANE_KEYMSK(pipe, plane_id), + skl_plane_keymsk(plane_state)); + intel_de_write_dsb(display, dsb, PLANE_KEYMAX(pipe, plane_id), + skl_plane_keymax(plane_state)); + + intel_de_write_dsb(display, dsb, PLANE_OFFSET(pipe, plane_id), + PLANE_OFFSET_Y(y) | PLANE_OFFSET_X(x)); + + if (intel_fb_is_rc_ccs_cc_modifier(fb->modifier)) { + intel_de_write_dsb(display, dsb, PLANE_CC_VAL(pipe, plane_id, 0), + lower_32_bits(plane_state->ccval)); + intel_de_write_dsb(display, dsb, PLANE_CC_VAL(pipe, plane_id, 1), + upper_32_bits(plane_state->ccval)); + } + + /* FLAT CCS doesn't need to program AUX_DIST */ + if (HAS_AUX_CCS(display)) + intel_de_write_dsb(display, dsb, PLANE_AUX_DIST(pipe, plane_id), + skl_plane_aux_dist(plane_state, color_plane)); + + if (icl_is_hdr_plane(display, plane_id)) + intel_de_write_dsb(display, dsb, PLANE_CUS_CTL(pipe, plane_id), + plane_state->cus_ctl); + + intel_de_write_dsb(display, dsb, PLANE_COLOR_CTL(pipe, plane_id), + plane_color_ctl); + + if (fb->format->is_yuv && icl_is_hdr_plane(display, plane_id)) + icl_program_input_csc(dsb, plane, plane_state); + + skl_write_plane_wm(dsb, plane, crtc_state); + + /* + * FIXME: pxp session invalidation can hit any time even at time of commit + * or after the commit, display content will be garbage. + */ + if (plane_state->force_black) + icl_plane_csc_load_black(dsb, plane, crtc_state); + + icl_plane_update_sel_fetch_noarm(dsb, plane, crtc_state, plane_state, color_plane); +} + +static void icl_plane_update_sel_fetch_arm(struct intel_dsb *dsb, + struct intel_plane *plane, + const struct intel_crtc_state *crtc_state, + const struct intel_plane_state *plane_state) +{ + struct intel_display *display = to_intel_display(plane); + enum pipe pipe = plane->pipe; + + if (!crtc_state->enable_psr2_sel_fetch) + return; + + if (drm_rect_height(&plane_state->psr2_sel_fetch_area) > 0) + intel_de_write_dsb(display, dsb, SEL_FETCH_PLANE_CTL(pipe, plane->id), + SEL_FETCH_PLANE_CTL_ENABLE); + else + icl_plane_disable_sel_fetch_arm(dsb, plane, crtc_state); +} + +static void +icl_plane_update_arm(struct intel_dsb *dsb, + struct intel_plane *plane, + const struct intel_crtc_state *crtc_state, + const struct intel_plane_state *plane_state) +{ + struct intel_display *display = to_intel_display(plane); + enum plane_id plane_id = plane->id; + enum pipe pipe = plane->pipe; + u32 plane_ctl; + + plane_ctl = plane_state->ctl | + skl_plane_ctl_crtc(crtc_state); + + /* + * Enable the scaler before the plane so that we don't + * get a catastrophic underrun even if the two operations + * end up happening in two different frames. + * + * TODO: split into noarm+arm pair + */ + if (plane_state->scaler_id >= 0) + skl_program_plane_scaler(dsb, plane, crtc_state, plane_state); + + icl_plane_update_sel_fetch_arm(dsb, plane, crtc_state, plane_state); + + intel_color_plane_commit_arm(dsb, plane_state); + + /* + * In order to have FBC for fp16 formats pixel normalizer block must be + * active. Check if pixel normalizer block need to be enabled for FBC. + * If needed, use normalization factor as 1.0 and enable the block. + */ + if (intel_fbc_is_enable_pixel_normalizer(plane_state)) + x3p_lpd_plane_update_pixel_normalizer(dsb, plane, true); + + /* + * The control register self-arms if the plane was previously + * disabled. Try to make the plane enable atomic by writing + * the control register just before the surface register. + */ + intel_de_write_dsb(display, dsb, PLANE_CTL(pipe, plane_id), + plane_ctl); + intel_de_write_dsb(display, dsb, PLANE_SURF(pipe, plane_id), + plane_state->surf); +} + +static void skl_plane_capture_error(struct intel_crtc *crtc, + struct intel_plane *plane, + struct intel_plane_error *error) +{ + struct intel_display *display = to_intel_display(plane); + + error->ctl = intel_de_read(display, PLANE_CTL(crtc->pipe, plane->id)); + error->surf = intel_de_read(display, PLANE_SURF(crtc->pipe, plane->id)); + error->surflive = intel_de_read(display, PLANE_SURFLIVE(crtc->pipe, plane->id)); +} + +static void +skl_plane_async_flip(struct intel_dsb *dsb, + struct intel_plane *plane, + const struct intel_crtc_state *crtc_state, + const struct intel_plane_state *plane_state, + bool async_flip) +{ + struct intel_display *display = to_intel_display(plane); + enum plane_id plane_id = plane->id; + enum pipe pipe = plane->pipe; + u32 plane_ctl = plane_state->ctl; + u32 plane_surf = plane_state->surf; + + plane_ctl |= skl_plane_ctl_crtc(crtc_state); + + if (async_flip) { + if (DISPLAY_VER(display) >= 30) + plane_surf |= PLANE_SURF_ASYNC_UPDATE; + else + plane_ctl |= PLANE_CTL_ASYNC_FLIP; + } + + intel_de_write_dsb(display, dsb, PLANE_CTL(pipe, plane_id), + plane_ctl); + intel_de_write_dsb(display, dsb, PLANE_SURF(pipe, plane_id), + plane_surf); +} + +static bool intel_format_is_p01x(u32 format) +{ + switch (format) { + case DRM_FORMAT_P010: + case DRM_FORMAT_P012: + case DRM_FORMAT_P016: + return true; + default: + return false; + } +} + +static int skl_plane_check_fb(const struct intel_crtc_state *crtc_state, + 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; + + if (!fb) + return 0; + + if (rotation & ~(DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180) && + intel_fb_is_ccs_modifier(fb->modifier)) { + drm_dbg_kms(display->drm, + "[PLANE:%d:%s] RC support only with 0/180 degree rotation (%x)\n", + plane->base.base.id, plane->base.name, rotation); + return -EINVAL; + } + + if (rotation & DRM_MODE_REFLECT_X && + fb->modifier == DRM_FORMAT_MOD_LINEAR && + DISPLAY_VER(display) < 35) { + drm_dbg_kms(display->drm, + "[PLANE:%d:%s] horizontal flip is not supported with linear surface formats\n", + plane->base.base.id, plane->base.name); + return -EINVAL; + } + + /* + * Display20 onward tile4 hflip is not supported + */ + if (rotation & DRM_MODE_REFLECT_X && + intel_fb_is_tile4_modifier(fb->modifier) && + DISPLAY_VER(display) >= 20) { + drm_dbg_kms(display->drm, + "[PLANE:%d:%s] horizontal flip is not supported with tile4 surface formats\n", + plane->base.base.id, plane->base.name); + return -EINVAL; + } + + if (drm_rotation_90_or_270(rotation)) { + if (!intel_fb_supports_90_270_rotation(to_intel_framebuffer(fb))) { + drm_dbg_kms(display->drm, + "[PLANE:%d:%s] Y/Yf tiling required for 90/270!\n", + plane->base.base.id, plane->base.name); + return -EINVAL; + } + + /* + * 90/270 is not allowed with RGB64 16:16:16:16 and + * Indexed 8-bit. RGB 16-bit 5:6:5 is allowed gen11 onwards. + */ + switch (fb->format->format) { + case DRM_FORMAT_RGB565: + if (DISPLAY_VER(display) >= 11) + break; + fallthrough; + case DRM_FORMAT_C8: + case DRM_FORMAT_XRGB16161616F: + case DRM_FORMAT_XBGR16161616F: + case DRM_FORMAT_ARGB16161616F: + case DRM_FORMAT_ABGR16161616F: + case DRM_FORMAT_Y210: + case DRM_FORMAT_Y212: + case DRM_FORMAT_Y216: + case DRM_FORMAT_XVYU12_16161616: + case DRM_FORMAT_XVYU16161616: + drm_dbg_kms(display->drm, + "[PLANE:%d:%s] unsupported pixel format %p4cc for 90/270!\n", + plane->base.base.id, plane->base.name, &fb->format->format); + return -EINVAL; + default: + break; + } + } + + /* Y-tiling is not supported in IF-ID Interlace mode */ + if (crtc_state->hw.adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE && + fb->modifier != DRM_FORMAT_MOD_LINEAR && + fb->modifier != I915_FORMAT_MOD_X_TILED) { + drm_dbg_kms(display->drm, + "[PLANE:%d:%s] Y/Yf tiling not supported in IF-ID mode\n", + plane->base.base.id, plane->base.name); + return -EINVAL; + } + + /* Wa_1606054188:tgl,adl-s */ + if ((display->platform.alderlake_s || display->platform.tigerlake) && + plane_state->ckey.flags & I915_SET_COLORKEY_SOURCE && + intel_format_is_p01x(fb->format->format)) { + drm_dbg_kms(display->drm, + "[PLANE:%d:%s] source color keying not supported with P01x formats\n", + plane->base.base.id, plane->base.name); + return -EINVAL; + } + + return 0; +} + +static int skl_plane_check_dst_coordinates(const struct intel_crtc_state *crtc_state, + 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); + int crtc_x = plane_state->uapi.dst.x1; + int crtc_w = drm_rect_width(&plane_state->uapi.dst); + int pipe_src_w = drm_rect_width(&crtc_state->pipe_src); + + /* + * Display WA #1175: glk + * Planes other than the cursor may cause FIFO underflow and display + * corruption if starting less than 4 pixels from the right edge of + * the screen. + * Besides the above WA fix the similar problem, where planes other + * than the cursor ending less than 4 pixels from the left edge of the + * screen may cause FIFO underflow and display corruption. + */ + if (DISPLAY_VER(display) == 10 && + (crtc_x + crtc_w < 4 || crtc_x > pipe_src_w - 4)) { + drm_dbg_kms(display->drm, + "[PLANE:%d:%s] requested plane X %s position %d invalid (valid range %d-%d)\n", + plane->base.base.id, plane->base.name, + crtc_x + crtc_w < 4 ? "end" : "start", + crtc_x + crtc_w < 4 ? crtc_x + crtc_w : crtc_x, + 4, pipe_src_w - 4); + return -ERANGE; + } + + return 0; +} + +static int skl_plane_check_nv12_rotation(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; + int src_w = drm_rect_width(&plane_state->uapi.src) >> 16; + + /* Display WA #1106 */ + if (intel_format_info_is_yuv_semiplanar(fb->format, fb->modifier) && + src_w & 3 && + (rotation == DRM_MODE_ROTATE_270 || + rotation == (DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90))) { + drm_dbg_kms(display->drm, + "[PLANE:%d:%s] src width must be multiple of 4 for rotated planar YUV\n", + plane->base.base.id, plane->base.name); + return -EINVAL; + } + + return 0; +} + +static int skl_plane_max_scale(struct intel_display *display, + const struct drm_framebuffer *fb) +{ + /* + * We don't yet know the final source width nor + * whether we can use the HQ scaler mode. Assume + * the best case. + * FIXME need to properly check this later. + */ + if (DISPLAY_VER(display) >= 10 || + !intel_format_info_is_yuv_semiplanar(fb->format, fb->modifier)) + return 0x30000 - 1; + else + return 0x20000 - 1; +} + +static int intel_plane_min_width(struct intel_plane *plane, + const struct drm_framebuffer *fb, + int color_plane, + unsigned int rotation) +{ + if (plane->min_width) + return plane->min_width(fb, color_plane, rotation); + else + return 1; +} + +static int intel_plane_min_height(struct intel_plane *plane, + const struct drm_framebuffer *fb, + int color_plane, + unsigned int rotation) +{ + return 1; +} + +static int intel_plane_max_width(struct intel_plane *plane, + const struct drm_framebuffer *fb, + int color_plane, + unsigned int rotation) +{ + if (plane->max_width) + return plane->max_width(fb, color_plane, rotation); + else + return INT_MAX; +} + +static int intel_plane_max_height(struct intel_plane *plane, + const struct drm_framebuffer *fb, + int color_plane, + unsigned int rotation) +{ + if (plane->max_height) + return plane->max_height(fb, color_plane, rotation); + else + return INT_MAX; +} + +static bool +skl_check_main_ccs_coordinates(struct intel_plane_state *plane_state, + int main_x, int main_y, u32 main_offset, + int ccs_plane) +{ + struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); + const struct drm_framebuffer *fb = plane_state->hw.fb; + int aux_x = plane_state->view.color_plane[ccs_plane].x; + int aux_y = plane_state->view.color_plane[ccs_plane].y; + u32 aux_offset = plane_state->view.color_plane[ccs_plane].offset; + unsigned int alignment = plane->min_alignment(plane, fb, ccs_plane); + int hsub; + int vsub; + + intel_fb_plane_get_subsampling(&hsub, &vsub, fb, ccs_plane); + while (aux_offset >= main_offset && aux_y <= main_y) { + int x, y; + + if (aux_x == main_x && aux_y == main_y) + break; + + if (aux_offset == 0) + break; + + x = aux_x / hsub; + y = aux_y / vsub; + aux_offset = intel_plane_adjust_aligned_offset(&x, &y, + plane_state, + ccs_plane, + aux_offset, + aux_offset - alignment); + aux_x = x * hsub + aux_x % hsub; + aux_y = y * vsub + aux_y % vsub; + } + + if (aux_x != main_x || aux_y != main_y) + return false; + + plane_state->view.color_plane[ccs_plane].offset = aux_offset; + plane_state->view.color_plane[ccs_plane].x = aux_x; + plane_state->view.color_plane[ccs_plane].y = aux_y; + + return true; +} + + +int skl_calc_main_surface_offset(const struct intel_plane_state *plane_state, + int *x, int *y, u32 *offset) +{ + 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; + int aux_plane = skl_main_to_aux_plane(fb, 0); + u32 aux_offset = plane_state->view.color_plane[aux_plane].offset; + unsigned int alignment = plane->min_alignment(plane, fb, 0); + int w = drm_rect_width(&plane_state->uapi.src) >> 16; + + intel_add_fb_offsets(x, y, plane_state, 0); + *offset = intel_plane_compute_aligned_offset(x, y, plane_state, 0); + if (drm_WARN_ON(display->drm, alignment && !is_power_of_2(alignment))) + return -EINVAL; + + /* + * AUX surface offset is specified as the distance from the + * main surface offset, and it must be non-negative. Make + * sure that is what we will get. + */ + if (aux_plane && *offset > aux_offset) + *offset = intel_plane_adjust_aligned_offset(x, y, plane_state, 0, + *offset, + aux_offset & ~(alignment - 1)); + + /* + * When using an X-tiled surface, the plane blows up + * if the x offset + width exceed the stride. + * + * TODO: linear and Y-tiled seem fine, Yf untested, + */ + if (fb->modifier == I915_FORMAT_MOD_X_TILED) { + int cpp = fb->format->cpp[0]; + + while ((*x + w) * cpp > plane_state->view.color_plane[0].mapping_stride) { + if (*offset == 0) { + drm_dbg_kms(display->drm, + "[PLANE:%d:%s] unable to find suitable display surface offset due to X-tiling\n", + plane->base.base.id, plane->base.name); + return -EINVAL; + } + + *offset = intel_plane_adjust_aligned_offset(x, y, plane_state, 0, + *offset, + *offset - alignment); + } + } + + return 0; +} + +static int skl_check_main_surface(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; + int x = plane_state->uapi.src.x1 >> 16; + int y = plane_state->uapi.src.y1 >> 16; + int w = drm_rect_width(&plane_state->uapi.src) >> 16; + int h = drm_rect_height(&plane_state->uapi.src) >> 16; + int min_width = intel_plane_min_width(plane, fb, 0, rotation); + int min_height = intel_plane_min_height(plane, fb, 0, rotation); + int max_width = intel_plane_max_width(plane, fb, 0, rotation); + int max_height = intel_plane_max_height(plane, fb, 0, rotation); + unsigned int alignment = plane->min_alignment(plane, fb, 0); + int aux_plane = skl_main_to_aux_plane(fb, 0); + u32 offset; + int ret; + + if (w > max_width || w < min_width || h > max_height || h < min_height) { + drm_dbg_kms(display->drm, + "[PLANE:%d:%s] requested Y/RGB source size %dx%d outside limits (min: %dx%d max: %dx%d)\n", + plane->base.base.id, plane->base.name, + w, h, min_width, min_height, max_width, max_height); + return -EINVAL; + } + + ret = skl_calc_main_surface_offset(plane_state, &x, &y, &offset); + if (ret) + return ret; + + /* + * CCS AUX surface doesn't have its own x/y offsets, we must make sure + * they match with the main surface x/y offsets. On DG2 + * there's no aux plane on fb so skip this checking. + */ + if (intel_fb_is_ccs_modifier(fb->modifier) && aux_plane) { + while (!skl_check_main_ccs_coordinates(plane_state, x, y, + offset, aux_plane)) { + if (offset == 0) + break; + + offset = intel_plane_adjust_aligned_offset(&x, &y, plane_state, 0, + offset, offset - alignment); + } + + if (x != plane_state->view.color_plane[aux_plane].x || + y != plane_state->view.color_plane[aux_plane].y) { + drm_dbg_kms(display->drm, + "[PLANE:%d:%s] unable to find suitable display surface offset due to CCS\n", + plane->base.base.id, plane->base.name); + return -EINVAL; + } + } + + if (DISPLAY_VER(display) >= 13) + drm_WARN_ON(display->drm, x > 65535 || y > 65535); + else + drm_WARN_ON(display->drm, x > 8191 || y > 8191); + + plane_state->view.color_plane[0].offset = offset; + plane_state->view.color_plane[0].x = x; + plane_state->view.color_plane[0].y = y; + + /* + * Put the final coordinates back so that the src + * coordinate checks will see the right values. + */ + drm_rect_translate_to(&plane_state->uapi.src, + x << 16, y << 16); + + return 0; +} + +static int skl_check_nv12_aux_surface(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; + int uv_plane = 1; + int ccs_plane = intel_fb_is_ccs_modifier(fb->modifier) ? + skl_main_to_aux_plane(fb, uv_plane) : 0; + int min_width = intel_plane_min_width(plane, fb, uv_plane, rotation); + int min_height = intel_plane_min_height(plane, fb, uv_plane, rotation); + int max_width = intel_plane_max_width(plane, fb, uv_plane, rotation); + int max_height = intel_plane_max_height(plane, fb, uv_plane, rotation); + int x = plane_state->uapi.src.x1 >> 17; + int y = plane_state->uapi.src.y1 >> 17; + int w = drm_rect_width(&plane_state->uapi.src) >> 17; + int h = drm_rect_height(&plane_state->uapi.src) >> 17; + u32 offset; + + /* FIXME not quite sure how/if these apply to the chroma plane */ + if (w > max_width || w < min_width || h > max_height || h < min_height) { + drm_dbg_kms(display->drm, + "[PLANE:%d:%s] requested CbCr source size %dx%d outside limits (min: %dx%d max: %dx%d)\n", + plane->base.base.id, plane->base.name, + w, h, min_width, min_height, max_width, max_height); + return -EINVAL; + } + + intel_add_fb_offsets(&x, &y, plane_state, uv_plane); + offset = intel_plane_compute_aligned_offset(&x, &y, + plane_state, uv_plane); + + if (ccs_plane) { + u32 aux_offset = plane_state->view.color_plane[ccs_plane].offset; + unsigned int alignment = plane->min_alignment(plane, fb, uv_plane); + + if (offset > aux_offset) + offset = intel_plane_adjust_aligned_offset(&x, &y, + plane_state, + uv_plane, + offset, + aux_offset & ~(alignment - 1)); + + while (!skl_check_main_ccs_coordinates(plane_state, x, y, + offset, ccs_plane)) { + if (offset == 0) + break; + + offset = intel_plane_adjust_aligned_offset(&x, &y, + plane_state, + uv_plane, + offset, offset - alignment); + } + + if (x != plane_state->view.color_plane[ccs_plane].x || + y != plane_state->view.color_plane[ccs_plane].y) { + drm_dbg_kms(display->drm, + "[PLANE:%d:%s] unable to find suitable display surface offset due to CCS\n", + plane->base.base.id, plane->base.name); + return -EINVAL; + } + } + + if (DISPLAY_VER(display) >= 13) + drm_WARN_ON(display->drm, x > 65535 || y > 65535); + else + drm_WARN_ON(display->drm, x > 8191 || y > 8191); + + plane_state->view.color_plane[uv_plane].offset = offset; + plane_state->view.color_plane[uv_plane].x = x; + plane_state->view.color_plane[uv_plane].y = y; + + return 0; +} + +static int skl_check_ccs_aux_surface(struct intel_plane_state *plane_state) +{ + const struct drm_framebuffer *fb = plane_state->hw.fb; + int src_x = plane_state->uapi.src.x1 >> 16; + int src_y = plane_state->uapi.src.y1 >> 16; + u32 offset; + int ccs_plane; + + for (ccs_plane = 0; ccs_plane < fb->format->num_planes; ccs_plane++) { + int main_hsub, main_vsub; + int hsub, vsub; + int x, y; + + if (!intel_fb_is_ccs_aux_plane(fb, ccs_plane)) + continue; + + intel_fb_plane_get_subsampling(&main_hsub, &main_vsub, fb, + skl_ccs_to_main_plane(fb, ccs_plane)); + intel_fb_plane_get_subsampling(&hsub, &vsub, fb, ccs_plane); + + hsub *= main_hsub; + vsub *= main_vsub; + x = src_x / hsub; + y = src_y / vsub; + + intel_add_fb_offsets(&x, &y, plane_state, ccs_plane); + + offset = intel_plane_compute_aligned_offset(&x, &y, + plane_state, + ccs_plane); + + plane_state->view.color_plane[ccs_plane].offset = offset; + plane_state->view.color_plane[ccs_plane].x = (x * hsub + src_x % hsub) / main_hsub; + plane_state->view.color_plane[ccs_plane].y = (y * vsub + src_y % vsub) / main_vsub; + } + + return 0; +} + +static int skl_check_plane_surface(struct intel_plane_state *plane_state) +{ + const struct drm_framebuffer *fb = plane_state->hw.fb; + int ret; + + ret = intel_plane_compute_gtt(plane_state); + if (ret) + return ret; + + if (!plane_state->uapi.visible) + return 0; + + /* + * Handle the AUX surface first since the main surface setup depends on + * it. + */ + if (intel_fb_is_ccs_modifier(fb->modifier)) { + ret = skl_check_ccs_aux_surface(plane_state); + if (ret) + return ret; + } + + if (intel_format_info_is_yuv_semiplanar(fb->format, + fb->modifier)) { + ret = skl_check_nv12_aux_surface(plane_state); + if (ret) + return ret; + } + + ret = skl_check_main_surface(plane_state); + if (ret) + return ret; + + return 0; +} + +static bool skl_fb_scalable(const struct drm_framebuffer *fb) +{ + struct intel_display *display; + + if (!fb) + return false; + + display = to_intel_display(fb->dev); + + switch (fb->format->format) { + case DRM_FORMAT_C8: + return false; + case DRM_FORMAT_XRGB16161616F: + case DRM_FORMAT_ARGB16161616F: + case DRM_FORMAT_XBGR16161616F: + case DRM_FORMAT_ABGR16161616F: + return DISPLAY_VER(display) >= 11; + default: + return true; + } +} + +static void check_protection(struct intel_plane_state *plane_state) +{ + struct intel_display *display = to_intel_display(plane_state); + const struct drm_framebuffer *fb = plane_state->hw.fb; + struct drm_gem_object *obj = intel_fb_bo(fb); + + if (DISPLAY_VER(display) < 11) + return; + + plane_state->decrypt = intel_pxp_key_check(obj, false) == 0; + plane_state->force_black = intel_bo_is_protected(obj) && + !plane_state->decrypt; +} + +static void +make_damage_viewport_relative(struct intel_plane_state *plane_state) +{ + const struct drm_framebuffer *fb = plane_state->hw.fb; + const struct drm_rect *src = &plane_state->uapi.src; + unsigned int rotation = plane_state->hw.rotation; + struct drm_rect *damage = &plane_state->damage; + + if (!drm_rect_visible(damage)) + return; + + if (!fb || !plane_state->uapi.visible) { + plane_state->damage = DRM_RECT_INIT(0, 0, 0, 0); + return; + } + + if (drm_rotation_90_or_270(rotation)) { + drm_rect_rotate(damage, fb->width, fb->height, + DRM_MODE_ROTATE_270); + drm_rect_translate(damage, -(src->y1 >> 16), -(src->x1 >> 16)); + } else { + drm_rect_translate(damage, -(src->x1 >> 16), -(src->y1 >> 16)); + } +} + +static void clip_damage(struct intel_plane_state *plane_state) +{ + struct drm_rect *damage = &plane_state->damage; + struct drm_rect src; + + if (!drm_rect_visible(damage)) + return; + + drm_rect_fp_to_int(&src, &plane_state->uapi.src); + drm_rect_translate(damage, src.x1, src.y1); + drm_rect_intersect(damage, &src); +} + +static int skl_plane_check(struct intel_crtc_state *crtc_state, + 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; + int min_scale = DRM_PLANE_NO_SCALING; + int max_scale = DRM_PLANE_NO_SCALING; + int ret; + + ret = skl_plane_check_fb(crtc_state, plane_state); + if (ret) + return ret; + + /* use scaler when colorkey is not required */ + if (!plane_state->ckey.flags && skl_fb_scalable(fb)) { + min_scale = 1; + max_scale = skl_plane_max_scale(display, fb); + } + + ret = intel_plane_check_clipping(plane_state, crtc_state, + min_scale, max_scale, true); + if (ret) + return ret; + + make_damage_viewport_relative(plane_state); + + ret = skl_check_plane_surface(plane_state); + if (ret) + return ret; + + if (!plane_state->uapi.visible) + return 0; + + ret = skl_plane_check_dst_coordinates(crtc_state, plane_state); + if (ret) + return ret; + + ret = intel_plane_check_src_coordinates(plane_state); + if (ret) + return ret; + + clip_damage(plane_state); + + ret = skl_plane_check_nv12_rotation(plane_state); + if (ret) + return ret; + + check_protection(plane_state); + + /* HW only has 8 bits pixel precision, disable plane if invisible */ + if (!(plane_state->hw.alpha >> 8)) { + plane_state->uapi.visible = false; + plane_state->damage = DRM_RECT_INIT(0, 0, 0, 0); + } + + plane_state->ctl = skl_plane_ctl(plane_state); + + if (DISPLAY_VER(display) >= 10) + plane_state->color_ctl = glk_plane_color_ctl(plane_state); + + if (intel_format_info_is_yuv_semiplanar(fb->format, fb->modifier) && + icl_is_hdr_plane(display, plane->id)) + /* Enable and use MPEG-2 chroma siting */ + plane_state->cus_ctl = PLANE_CUS_ENABLE | + PLANE_CUS_HPHASE_0 | + PLANE_CUS_VPHASE_SIGN_NEGATIVE | PLANE_CUS_VPHASE_0_25; + else + plane_state->cus_ctl = 0; + + return 0; +} + +void icl_link_nv12_planes(struct intel_plane_state *uv_plane_state, + struct intel_plane_state *y_plane_state) +{ + struct intel_display *display = to_intel_display(uv_plane_state); + struct intel_plane *uv_plane = to_intel_plane(uv_plane_state->uapi.plane); + struct intel_plane *y_plane = to_intel_plane(y_plane_state->uapi.plane); + + drm_WARN_ON(display->drm, icl_is_nv12_y_plane(display, uv_plane->id)); + drm_WARN_ON(display->drm, !icl_is_nv12_y_plane(display, y_plane->id)); + + y_plane_state->ctl |= PLANE_CTL_YUV420_Y_PLANE; + + if (icl_is_hdr_plane(display, uv_plane->id)) { + switch (y_plane->id) { + case PLANE_7: + uv_plane_state->cus_ctl |= PLANE_CUS_Y_PLANE_7_ICL; + break; + case PLANE_6: + uv_plane_state->cus_ctl |= PLANE_CUS_Y_PLANE_6_ICL; + break; + case PLANE_5: + uv_plane_state->cus_ctl |= PLANE_CUS_Y_PLANE_5_RKL; + break; + case PLANE_4: + uv_plane_state->cus_ctl |= PLANE_CUS_Y_PLANE_4_RKL; + break; + default: + MISSING_CASE(y_plane->id); + } + } +} + +static struct intel_fbc *skl_plane_fbc(struct intel_display *display, + enum pipe pipe, enum plane_id plane_id) +{ + enum intel_fbc_id fbc_id = skl_fbc_id_for_pipe(pipe); + + if (skl_plane_has_fbc(display, fbc_id, plane_id)) + return display->fbc[fbc_id]; + else + return NULL; +} + +static bool skl_plane_has_planar(struct intel_display *display, + enum pipe pipe, enum plane_id plane_id) +{ + /* Display WA #0870: skl, bxt */ + if (display->platform.skylake || display->platform.broxton) + return false; + + if (pipe == PIPE_C) + return false; + + return plane_id == PLANE_1 || plane_id == PLANE_2; +} + +static const u32 *skl_get_plane_formats(struct intel_display *display, + enum pipe pipe, enum plane_id plane_id, + int *num_formats) +{ + if (skl_plane_has_planar(display, pipe, plane_id)) { + *num_formats = ARRAY_SIZE(skl_planar_formats); + return skl_planar_formats; + } else { + *num_formats = ARRAY_SIZE(skl_plane_formats); + return skl_plane_formats; + } +} + +static bool glk_plane_has_planar(struct intel_display *display, + enum pipe pipe, enum plane_id plane_id) +{ + return plane_id == PLANE_1 || plane_id == PLANE_2; +} + +static const u32 *glk_get_plane_formats(struct intel_display *display, + enum pipe pipe, enum plane_id plane_id, + int *num_formats) +{ + if (glk_plane_has_planar(display, pipe, plane_id)) { + *num_formats = ARRAY_SIZE(glk_planar_formats); + return glk_planar_formats; + } else { + *num_formats = ARRAY_SIZE(skl_plane_formats); + return skl_plane_formats; + } +} + +static const u32 *icl_get_plane_formats(struct intel_display *display, + enum pipe pipe, enum plane_id plane_id, + int *num_formats) +{ + if (icl_is_hdr_plane(display, plane_id)) { + *num_formats = ARRAY_SIZE(icl_hdr_plane_formats); + return icl_hdr_plane_formats; + } else if (icl_is_nv12_y_plane(display, plane_id)) { + *num_formats = ARRAY_SIZE(icl_sdr_y_plane_formats); + return icl_sdr_y_plane_formats; + } else { + *num_formats = ARRAY_SIZE(icl_sdr_uv_plane_formats); + return icl_sdr_uv_plane_formats; + } +} + +static bool skl_plane_format_mod_supported(struct drm_plane *_plane, + u32 format, u64 modifier) +{ + struct intel_plane *plane = to_intel_plane(_plane); + + if (!intel_fb_plane_supports_modifier(plane, modifier)) + return false; + + switch (format) { + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_ABGR8888: + if (intel_fb_is_ccs_modifier(modifier)) + return true; + fallthrough; + case DRM_FORMAT_RGB565: + case DRM_FORMAT_XRGB2101010: + case DRM_FORMAT_XBGR2101010: + case DRM_FORMAT_ARGB2101010: + case DRM_FORMAT_ABGR2101010: + case DRM_FORMAT_YUYV: + case DRM_FORMAT_YVYU: + case DRM_FORMAT_UYVY: + case DRM_FORMAT_VYUY: + case DRM_FORMAT_NV12: + case DRM_FORMAT_XYUV8888: + case DRM_FORMAT_P010: + case DRM_FORMAT_P012: + case DRM_FORMAT_P016: + case DRM_FORMAT_XVYU2101010: + if (modifier == I915_FORMAT_MOD_Yf_TILED) + return true; + fallthrough; + case DRM_FORMAT_C8: + case DRM_FORMAT_XBGR16161616F: + case DRM_FORMAT_ABGR16161616F: + case DRM_FORMAT_XRGB16161616F: + case DRM_FORMAT_ARGB16161616F: + case DRM_FORMAT_Y210: + case DRM_FORMAT_Y212: + case DRM_FORMAT_Y216: + case DRM_FORMAT_XVYU12_16161616: + case DRM_FORMAT_XVYU16161616: + if (modifier == DRM_FORMAT_MOD_LINEAR || + modifier == I915_FORMAT_MOD_X_TILED || + modifier == I915_FORMAT_MOD_Y_TILED) + return true; + fallthrough; + default: + return false; + } +} + +static bool icl_plane_format_mod_supported(struct drm_plane *_plane, + u32 format, u64 modifier) +{ + struct intel_plane *plane = to_intel_plane(_plane); + + if (!intel_fb_plane_supports_modifier(plane, modifier)) + return false; + + switch (format) { + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_XRGB2101010: + case DRM_FORMAT_XBGR2101010: + case DRM_FORMAT_ARGB2101010: + case DRM_FORMAT_ABGR2101010: + if (intel_fb_is_ccs_modifier(modifier)) + return true; + fallthrough; + case DRM_FORMAT_RGB565: + case DRM_FORMAT_YUYV: + case DRM_FORMAT_YVYU: + case DRM_FORMAT_UYVY: + case DRM_FORMAT_VYUY: + case DRM_FORMAT_NV12: + case DRM_FORMAT_XYUV8888: + case DRM_FORMAT_P010: + case DRM_FORMAT_P012: + case DRM_FORMAT_P016: + case DRM_FORMAT_XVYU2101010: + if (modifier == I915_FORMAT_MOD_Yf_TILED) + return true; + fallthrough; + case DRM_FORMAT_C8: + case DRM_FORMAT_XBGR16161616F: + case DRM_FORMAT_ABGR16161616F: + case DRM_FORMAT_XRGB16161616F: + case DRM_FORMAT_ARGB16161616F: + case DRM_FORMAT_Y210: + case DRM_FORMAT_Y212: + case DRM_FORMAT_Y216: + case DRM_FORMAT_XVYU12_16161616: + case DRM_FORMAT_XVYU16161616: + if (modifier == DRM_FORMAT_MOD_LINEAR || + modifier == I915_FORMAT_MOD_X_TILED || + modifier == I915_FORMAT_MOD_Y_TILED) + return true; + fallthrough; + default: + return false; + } +} + +static bool tgl_plane_format_mod_supported(struct drm_plane *_plane, + u32 format, u64 modifier) +{ + struct intel_plane *plane = to_intel_plane(_plane); + + if (!intel_fb_plane_supports_modifier(plane, modifier)) + return false; + + switch (format) { + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_XRGB2101010: + case DRM_FORMAT_XBGR2101010: + case DRM_FORMAT_ARGB2101010: + case DRM_FORMAT_ABGR2101010: + case DRM_FORMAT_XBGR16161616F: + case DRM_FORMAT_ABGR16161616F: + case DRM_FORMAT_XRGB16161616F: + case DRM_FORMAT_ARGB16161616F: + if (intel_fb_is_ccs_modifier(modifier)) + return true; + fallthrough; + case DRM_FORMAT_YUYV: + case DRM_FORMAT_YVYU: + case DRM_FORMAT_UYVY: + case DRM_FORMAT_VYUY: + case DRM_FORMAT_NV12: + case DRM_FORMAT_XYUV8888: + case DRM_FORMAT_P010: + case DRM_FORMAT_P012: + case DRM_FORMAT_P016: + if (intel_fb_is_mc_ccs_modifier(modifier)) + return true; + fallthrough; + case DRM_FORMAT_RGB565: + case DRM_FORMAT_XVYU2101010: + case DRM_FORMAT_C8: + case DRM_FORMAT_Y210: + case DRM_FORMAT_Y212: + case DRM_FORMAT_Y216: + case DRM_FORMAT_XVYU12_16161616: + case DRM_FORMAT_XVYU16161616: + if (!intel_fb_is_ccs_modifier(modifier)) + return true; + fallthrough; + default: + return false; + } +} + +static const struct drm_plane_funcs skl_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = intel_plane_destroy, + .atomic_duplicate_state = intel_plane_duplicate_state, + .atomic_destroy_state = intel_plane_destroy_state, + .format_mod_supported = skl_plane_format_mod_supported, + .format_mod_supported_async = intel_plane_format_mod_supported_async, +}; + +static const struct drm_plane_funcs icl_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = intel_plane_destroy, + .atomic_duplicate_state = intel_plane_duplicate_state, + .atomic_destroy_state = intel_plane_destroy_state, + .format_mod_supported = icl_plane_format_mod_supported, + .format_mod_supported_async = intel_plane_format_mod_supported_async, +}; + +static const struct drm_plane_funcs tgl_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = intel_plane_destroy, + .atomic_duplicate_state = intel_plane_duplicate_state, + .atomic_destroy_state = intel_plane_destroy_state, + .format_mod_supported = tgl_plane_format_mod_supported, + .format_mod_supported_async = intel_plane_format_mod_supported_async, +}; + +static void +skl_plane_enable_flip_done(struct intel_plane *plane) +{ + struct intel_display *display = to_intel_display(plane); + enum pipe pipe = plane->pipe; + + spin_lock_irq(&display->irq.lock); + bdw_enable_pipe_irq(display, pipe, GEN9_PIPE_PLANE_FLIP_DONE(plane->id)); + spin_unlock_irq(&display->irq.lock); +} + +static void +skl_plane_disable_flip_done(struct intel_plane *plane) +{ + struct intel_display *display = to_intel_display(plane); + enum pipe pipe = plane->pipe; + + spin_lock_irq(&display->irq.lock); + bdw_disable_pipe_irq(display, pipe, GEN9_PIPE_PLANE_FLIP_DONE(plane->id)); + spin_unlock_irq(&display->irq.lock); +} + +static bool skl_plane_has_rc_ccs(struct intel_display *display, + enum pipe pipe, enum plane_id plane_id) +{ + if (pipe == PIPE_C) + return false; + + return plane_id == PLANE_1 || plane_id == PLANE_2; +} + +static u8 skl_plane_caps(struct intel_display *display, + enum pipe pipe, enum plane_id plane_id) +{ + u8 caps = INTEL_PLANE_CAP_TILING_X | + INTEL_PLANE_CAP_TILING_Y | + INTEL_PLANE_CAP_TILING_Yf; + + if (skl_plane_has_rc_ccs(display, pipe, plane_id)) + caps |= INTEL_PLANE_CAP_CCS_RC; + + return caps; +} + +static bool glk_plane_has_rc_ccs(struct intel_display *display, + enum pipe pipe) +{ + return pipe != PIPE_C; +} + +static u8 glk_plane_caps(struct intel_display *display, + enum pipe pipe, enum plane_id plane_id) +{ + u8 caps = INTEL_PLANE_CAP_TILING_X | + INTEL_PLANE_CAP_TILING_Y | + INTEL_PLANE_CAP_TILING_Yf; + + if (glk_plane_has_rc_ccs(display, pipe)) + caps |= INTEL_PLANE_CAP_CCS_RC; + + return caps; +} + +static u8 icl_plane_caps(struct intel_display *display, + enum pipe pipe, enum plane_id plane_id) +{ + return INTEL_PLANE_CAP_TILING_X | + INTEL_PLANE_CAP_TILING_Y | + INTEL_PLANE_CAP_TILING_Yf | + INTEL_PLANE_CAP_CCS_RC; +} + +static bool tgl_plane_has_mc_ccs(struct intel_display *display, + enum plane_id plane_id) +{ + /* Wa_14010477008 */ + if (display->platform.dg1 || display->platform.rocketlake || + (display->platform.tigerlake && IS_DISPLAY_STEP(display, STEP_A0, STEP_D0))) + return false; + + return plane_id < PLANE_6; +} + +static u8 tgl_plane_caps(struct intel_display *display, + enum pipe pipe, enum plane_id plane_id) +{ + u8 caps = INTEL_PLANE_CAP_TILING_X | + INTEL_PLANE_CAP_CCS_RC | + INTEL_PLANE_CAP_CCS_RC_CC; + + if (HAS_4TILE(display)) + caps |= INTEL_PLANE_CAP_TILING_4; + else + caps |= INTEL_PLANE_CAP_TILING_Y; + + if (tgl_plane_has_mc_ccs(display, plane_id)) + caps |= INTEL_PLANE_CAP_CCS_MC; + + if (DISPLAY_VER(display) >= 14 && display->platform.dgfx) + caps |= INTEL_PLANE_CAP_NEED64K_PHYS; + + return caps; +} + +static void skl_disable_tiling(struct intel_plane *plane) +{ + struct intel_plane_state *state = to_intel_plane_state(plane->base.state); + struct intel_display *display = to_intel_display(plane); + const struct drm_framebuffer *fb = state->hw.fb; + u32 plane_ctl; + + plane_ctl = intel_de_read(display, PLANE_CTL(plane->pipe, plane->id)); + + if (intel_fb_uses_dpt(fb)) { + /* if DPT is enabled, keep tiling, but disable compression */ + plane_ctl &= ~PLANE_CTL_RENDER_DECOMPRESSION_ENABLE; + } else { + /* if DPT is not supported, disable tiling, and update stride */ + u32 stride = state->view.color_plane[0].scanout_stride / 64; + + plane_ctl &= ~PLANE_CTL_TILED_MASK; + intel_de_write_fw(display, PLANE_STRIDE(plane->pipe, plane->id), + PLANE_STRIDE_(stride)); + } + intel_de_write_fw(display, PLANE_CTL(plane->pipe, plane->id), plane_ctl); + + intel_de_write_fw(display, PLANE_SURF(plane->pipe, plane->id), + state->surf); +} + +struct intel_plane * +skl_universal_plane_create(struct intel_display *display, + enum pipe pipe, enum plane_id plane_id) +{ + const struct drm_plane_funcs *plane_funcs; + struct intel_plane *plane; + enum drm_plane_type plane_type; + unsigned int supported_rotations; + unsigned int supported_csc; + const u64 *modifiers; + const u32 *formats; + int num_formats; + int ret; + u8 caps; + + plane = intel_plane_alloc(); + if (IS_ERR(plane)) + return plane; + + plane->pipe = pipe; + plane->id = plane_id; + plane->frontbuffer_bit = INTEL_FRONTBUFFER(pipe, plane_id); + + intel_fbc_add_plane(skl_plane_fbc(display, pipe, plane_id), plane); + + if (DISPLAY_VER(display) >= 30) { + plane->min_width = adl_plane_min_width; + plane->max_width = xe3_plane_max_width; + plane->max_height = icl_plane_max_height; + plane->min_cdclk = icl_plane_min_cdclk; + } else if (DISPLAY_VER(display) >= 11) { + if (DISPLAY_VER(display) >= 14 || display->platform.alderlake_p) + plane->min_width = adl_plane_min_width; + else + plane->min_width = icl_plane_min_width; + if (icl_is_hdr_plane(display, plane_id)) + plane->max_width = icl_hdr_plane_max_width; + else + plane->max_width = icl_sdr_plane_max_width; + plane->max_height = icl_plane_max_height; + plane->min_cdclk = icl_plane_min_cdclk; + } else if (DISPLAY_VER(display) >= 10) { + plane->max_width = glk_plane_max_width; + plane->max_height = skl_plane_max_height; + plane->min_cdclk = glk_plane_min_cdclk; + } else { + plane->max_width = skl_plane_max_width; + plane->max_height = skl_plane_max_height; + plane->min_cdclk = skl_plane_min_cdclk; + } + plane->disable_tiling = skl_disable_tiling; + + plane->surf_offset = skl_plane_surf_offset; + + if (DISPLAY_VER(display) >= 13) + plane->max_stride = adl_plane_max_stride; + else + plane->max_stride = skl_plane_max_stride; + + if (DISPLAY_VER(display) >= 12) + plane->min_alignment = tgl_plane_min_alignment; + else + plane->min_alignment = skl_plane_min_alignment; + + if (intel_scanout_needs_vtd_wa(display)) + plane->vtd_guard = DISPLAY_VER(display) >= 10 ? 168 : 136; + + if (DISPLAY_VER(display) >= 11) { + plane->update_noarm = icl_plane_update_noarm; + plane->update_arm = icl_plane_update_arm; + plane->disable_arm = icl_plane_disable_arm; + } else { + plane->update_noarm = skl_plane_update_noarm; + plane->update_arm = skl_plane_update_arm; + plane->disable_arm = skl_plane_disable_arm; + } + plane->capture_error = skl_plane_capture_error; + plane->get_hw_state = skl_plane_get_hw_state; + plane->check_plane = skl_plane_check; + + if (HAS_ASYNC_FLIPS(display) && plane_id == PLANE_1) { + plane->need_async_flip_toggle_wa = IS_DISPLAY_VER(display, 9, 10); + plane->async_flip = skl_plane_async_flip; + plane->enable_flip_done = skl_plane_enable_flip_done; + plane->disable_flip_done = skl_plane_disable_flip_done; + + if (DISPLAY_VER(display) >= 12) + plane->can_async_flip = tgl_plane_can_async_flip; + else if (DISPLAY_VER(display) == 11) + plane->can_async_flip = icl_plane_can_async_flip; + else + plane->can_async_flip = skl_plane_can_async_flip; + } + + if (DISPLAY_VER(display) >= 11) + formats = icl_get_plane_formats(display, pipe, + plane_id, &num_formats); + else if (DISPLAY_VER(display) >= 10) + formats = glk_get_plane_formats(display, pipe, + plane_id, &num_formats); + else + formats = skl_get_plane_formats(display, pipe, + plane_id, &num_formats); + + if (DISPLAY_VER(display) >= 12) + plane_funcs = &tgl_plane_funcs; + else if (DISPLAY_VER(display) == 11) + plane_funcs = &icl_plane_funcs; + else + plane_funcs = &skl_plane_funcs; + + if (plane_id == PLANE_1) + plane_type = DRM_PLANE_TYPE_PRIMARY; + else + plane_type = DRM_PLANE_TYPE_OVERLAY; + + if (DISPLAY_VER(display) >= 12) + caps = tgl_plane_caps(display, pipe, plane_id); + else if (DISPLAY_VER(display) == 11) + caps = icl_plane_caps(display, pipe, plane_id); + else if (DISPLAY_VER(display) == 10) + caps = glk_plane_caps(display, pipe, plane_id); + else + caps = skl_plane_caps(display, pipe, plane_id); + + /* FIXME: xe has problems with AUX */ + if (!IS_ENABLED(I915) && HAS_AUX_CCS(display)) + caps &= ~(INTEL_PLANE_CAP_CCS_RC | + INTEL_PLANE_CAP_CCS_RC_CC | + INTEL_PLANE_CAP_CCS_MC); + + modifiers = intel_fb_plane_get_modifiers(display, caps); + + ret = drm_universal_plane_init(display->drm, &plane->base, + 0, plane_funcs, + formats, num_formats, modifiers, + plane_type, + "plane %d%c", plane_id + 1, + pipe_name(pipe)); + + kfree(modifiers); + + if (ret) + goto fail; + + if (DISPLAY_VER(display) >= 13) + supported_rotations = DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180; + else + supported_rotations = + DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_90 | + DRM_MODE_ROTATE_180 | DRM_MODE_ROTATE_270; + + if (DISPLAY_VER(display) >= 11) + supported_rotations |= DRM_MODE_REFLECT_X; + + drm_plane_create_rotation_property(&plane->base, + DRM_MODE_ROTATE_0, + supported_rotations); + + supported_csc = BIT(DRM_COLOR_YCBCR_BT601) | BIT(DRM_COLOR_YCBCR_BT709); + + if (DISPLAY_VER(display) >= 10) + supported_csc |= BIT(DRM_COLOR_YCBCR_BT2020); + + drm_plane_create_color_properties(&plane->base, + supported_csc, + BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) | + BIT(DRM_COLOR_YCBCR_FULL_RANGE), + DRM_COLOR_YCBCR_BT709, + DRM_COLOR_YCBCR_LIMITED_RANGE); + + if (DISPLAY_VER(display) >= 12) + intel_color_pipeline_plane_init(&plane->base, pipe); + + drm_plane_create_alpha_property(&plane->base); + drm_plane_create_blend_mode_property(&plane->base, + BIT(DRM_MODE_BLEND_PIXEL_NONE) | + BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE)); + + drm_plane_create_zpos_immutable_property(&plane->base, plane_id); + + if (DISPLAY_VER(display) >= 12) + drm_plane_enable_fb_damage_clips(&plane->base); + + if (DISPLAY_VER(display) >= 11) + drm_plane_create_scaling_filter_property(&plane->base, + BIT(DRM_SCALING_FILTER_DEFAULT) | + BIT(DRM_SCALING_FILTER_NEAREST_NEIGHBOR)); + + intel_plane_helper_add(plane); + + return plane; + +fail: + intel_plane_free(plane); + + return ERR_PTR(ret); +} + +void +skl_get_initial_plane_config(struct intel_crtc *crtc, + struct intel_initial_plane_config *plane_config) +{ + struct intel_display *display = to_intel_display(crtc); + struct intel_crtc_state *crtc_state = to_intel_crtc_state(crtc->base.state); + struct intel_plane *plane = to_intel_plane(crtc->base.primary); + enum plane_id plane_id = plane->id; + enum pipe pipe; + u32 val, base, offset, stride_mult, tiling, alpha; + int fourcc, pixel_format; + unsigned int aligned_height; + struct drm_framebuffer *fb; + struct intel_framebuffer *intel_fb; + static_assert(PLANE_CTL_TILED_YF == PLANE_CTL_TILED_4); + + if (!plane->get_hw_state(plane, &pipe)) + return; + + drm_WARN_ON(display->drm, pipe != crtc->pipe); + + if (crtc_state->joiner_pipes) { + drm_dbg_kms(display->drm, + "[CRTC:%d:%s] Unsupported joiner configuration for initial FB\n", + crtc->base.base.id, crtc->base.name); + return; + } + + intel_fb = intel_framebuffer_alloc(); + if (!intel_fb) { + drm_dbg_kms(display->drm, "failed to alloc fb\n"); + return; + } + + fb = &intel_fb->base; + + fb->dev = display->drm; + + val = intel_de_read(display, PLANE_CTL(pipe, plane_id)); + + if (DISPLAY_VER(display) >= 11) + pixel_format = val & PLANE_CTL_FORMAT_MASK_ICL; + else + pixel_format = val & PLANE_CTL_FORMAT_MASK_SKL; + + if (DISPLAY_VER(display) >= 10) { + u32 color_ctl; + + color_ctl = intel_de_read(display, PLANE_COLOR_CTL(pipe, plane_id)); + alpha = REG_FIELD_GET(PLANE_COLOR_ALPHA_MASK, color_ctl); + } else { + alpha = REG_FIELD_GET(PLANE_CTL_ALPHA_MASK, val); + } + + fourcc = skl_format_to_fourcc(pixel_format, + val & PLANE_CTL_ORDER_RGBX, alpha); + + tiling = val & PLANE_CTL_TILED_MASK; + switch (tiling) { + case PLANE_CTL_TILED_LINEAR: + fb->modifier = DRM_FORMAT_MOD_LINEAR; + break; + case PLANE_CTL_TILED_X: + fb->modifier = I915_FORMAT_MOD_X_TILED; + break; + case PLANE_CTL_TILED_Y: + if (val & PLANE_CTL_RENDER_DECOMPRESSION_ENABLE) + if (DISPLAY_VER(display) >= 14) + fb->modifier = I915_FORMAT_MOD_4_TILED_MTL_RC_CCS; + else if (DISPLAY_VER(display) >= 12) + fb->modifier = I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS; + else + fb->modifier = I915_FORMAT_MOD_Y_TILED_CCS; + else if (val & PLANE_CTL_MEDIA_DECOMPRESSION_ENABLE) + if (DISPLAY_VER(display) >= 14) + fb->modifier = I915_FORMAT_MOD_4_TILED_MTL_MC_CCS; + else + fb->modifier = I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS; + else + fb->modifier = I915_FORMAT_MOD_Y_TILED; + break; + case PLANE_CTL_TILED_YF: /* aka PLANE_CTL_TILED_4 on XE_LPD+ */ + if (HAS_4TILE(display)) { + u32 rc_mask = PLANE_CTL_RENDER_DECOMPRESSION_ENABLE | + PLANE_CTL_CLEAR_COLOR_DISABLE; + + if ((val & rc_mask) == rc_mask) + fb->modifier = I915_FORMAT_MOD_4_TILED_DG2_RC_CCS; + else if (val & PLANE_CTL_MEDIA_DECOMPRESSION_ENABLE) + fb->modifier = I915_FORMAT_MOD_4_TILED_DG2_MC_CCS; + else if (val & PLANE_CTL_RENDER_DECOMPRESSION_ENABLE) + fb->modifier = I915_FORMAT_MOD_4_TILED_DG2_RC_CCS_CC; + else + fb->modifier = I915_FORMAT_MOD_4_TILED; + } else { + if (val & PLANE_CTL_RENDER_DECOMPRESSION_ENABLE) + fb->modifier = I915_FORMAT_MOD_Yf_TILED_CCS; + else + fb->modifier = I915_FORMAT_MOD_Yf_TILED; + } + break; + default: + MISSING_CASE(tiling); + goto error; + } + + fb->format = drm_get_format_info(display->drm, fourcc, fb->modifier); + + if (!display->params.enable_dpt && + intel_fb_modifier_uses_dpt(display, fb->modifier)) { + drm_dbg_kms(display->drm, "DPT disabled, skipping initial FB\n"); + goto error; + } + + /* + * DRM_MODE_ROTATE_ is counter clockwise to stay compatible with Xrandr + * while i915 HW rotation is clockwise, that's why this swapping. + */ + switch (val & PLANE_CTL_ROTATE_MASK) { + case PLANE_CTL_ROTATE_0: + plane_config->rotation = DRM_MODE_ROTATE_0; + break; + case PLANE_CTL_ROTATE_90: + plane_config->rotation = DRM_MODE_ROTATE_270; + break; + case PLANE_CTL_ROTATE_180: + plane_config->rotation = DRM_MODE_ROTATE_180; + break; + case PLANE_CTL_ROTATE_270: + plane_config->rotation = DRM_MODE_ROTATE_90; + break; + } + + if (DISPLAY_VER(display) >= 11 && val & PLANE_CTL_FLIP_HORIZONTAL) + plane_config->rotation |= DRM_MODE_REFLECT_X; + + /* 90/270 degree rotation would require extra work */ + if (drm_rotation_90_or_270(plane_config->rotation)) + goto error; + + base = intel_de_read(display, PLANE_SURF(pipe, plane_id)) & PLANE_SURF_ADDR_MASK; + plane_config->base = base; + + offset = intel_de_read(display, PLANE_OFFSET(pipe, plane_id)); + drm_WARN_ON(display->drm, offset != 0); + + val = intel_de_read(display, PLANE_SIZE(pipe, plane_id)); + fb->height = REG_FIELD_GET(PLANE_HEIGHT_MASK, val) + 1; + fb->width = REG_FIELD_GET(PLANE_WIDTH_MASK, val) + 1; + + val = intel_de_read(display, PLANE_STRIDE(pipe, plane_id)); + stride_mult = skl_plane_stride_mult(fb, 0, DRM_MODE_ROTATE_0); + + fb->pitches[0] = REG_FIELD_GET(PLANE_STRIDE__MASK, val) * stride_mult; + + aligned_height = intel_fb_align_height(fb, 0, fb->height); + + plane_config->size = fb->pitches[0] * aligned_height; + + drm_dbg_kms(display->drm, + "[CRTC:%d:%s][PLANE:%d:%s] with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n", + crtc->base.base.id, crtc->base.name, + plane->base.base.id, plane->base.name, + fb->width, fb->height, fb->format->cpp[0] * 8, + base, fb->pitches[0], plane_config->size); + + plane_config->fb = intel_fb; + return; + +error: + kfree(intel_fb); +} + +bool skl_fixup_initial_plane_config(struct intel_crtc *crtc, + const struct intel_initial_plane_config *plane_config) +{ + struct intel_display *display = to_intel_display(crtc); + struct intel_plane *plane = to_intel_plane(crtc->base.primary); + const struct intel_plane_state *plane_state = + to_intel_plane_state(plane->base.state); + enum plane_id plane_id = plane->id; + enum pipe pipe = crtc->pipe; + + if (!plane_state->uapi.visible) + return false; + + /* + * We may have moved the surface to a different + * part of ggtt, make the plane aware of that. + */ + if (plane_config->base == plane_state->surf) + return false; + + intel_de_write(display, PLANE_SURF(pipe, plane_id), plane_state->surf); + + return true; +} |
