Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/gpu/drm/i915/display/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 90 kB image not shown  

Quelle  skl_universal_plane.c   Sprache: C

 
// 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 "pxp/intel_pxp.h"
#include "i915_drv.h"
#include "intel_bo.h"
#include "intel_de.h"
#include "intel_display_irq.h"
#include "intel_display_regs.h"
#include "intel_display_types.h"
#include "intel_dpt.h"
#include "intel_fb.h"
#include "intel_fbc.h"
#include "intel_frontbuffer.h"
#include "intel_plane.h"
#include "intel_psr.h"
#include "intel_psr_regs.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 icl_plane_min_width(const struct drm_framebuffer *fb,
          int color_plane,
          unsigned int rotation)
{
 /* Wa_14011264657, Wa_14011050563: gen11+ */
 switch (fb->format->format) {
 case DRM_FORMAT_C8:
  return 18;
 case DRM_FORMAT_RGB565:
  return 10;
 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_XVYU2101010:
 case DRM_FORMAT_Y212:
 case DRM_FORMAT_Y216:
  return 6;
 case DRM_FORMAT_NV12:
  return 20;
 case DRM_FORMAT_P010:
 case DRM_FORMAT_P012:
 case DRM_FORMAT_P016:
  return 12;
 case DRM_FORMAT_XRGB16161616F:
 case DRM_FORMAT_XBGR16161616F:
 case DRM_FORMAT_ARGB16161616F:
 case DRM_FORMAT_ABGR16161616F:
 case DRM_FORMAT_XVYU12_16161616:
 case DRM_FORMAT_XVYU16161616:
  return 4;
 default:
  return 1;
 }
}

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 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,
   u32 pixel_format, u64 modifier,
   unsigned int rotation,
   unsigned int max_pixels,
   unsigned int max_bytes)
{
 const struct drm_format_info *info = drm_format_info(pixel_format);
 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,
       u32 pixel_format, u64 modifier,
       unsigned int rotation)
{
 unsigned int max_pixels = 65536; /* PLANE_OFFSET limit */
 unsigned int max_bytes = 128 * 1024;

 return plane_max_stride(plane, pixel_format,
    modifier, rotation,
    max_pixels, max_bytes);
}

static unsigned int
skl_plane_max_stride(struct intel_plane *plane,
       u32 pixel_format, u64 modifier,
       unsigned int rotation)
{
 unsigned int max_pixels = 8192; /* PLANE_OFFSET limit */
 unsigned int max_bytes = 32 * 1024;

 return plane_max_stride(plane, pixel_format,
    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
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);
 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_crtc_state *crtc_state,
    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_crtc_state *crtc_state,
          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;

 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)) {
  /*
 * The DPT object contains only one vma, so the VMA's offset
 * within the DPT is always 0.
 */

  drm_WARN_ON(display->drm, plane_state->dpt_vma &&
       intel_dpt_offset(plane_state->dpt_vma));
  drm_WARN_ON(display->drm, offset & 0x1fffff);
  return offset >> 9;
 } else {
  drm_WARN_ON(display->drm, offset & 0xfff);
  return offset;
 }
}

static u32 skl_plane_surf(const struct intel_plane_state *plane_state,
     int color_plane)
{
 u32 plane_surf;

 plane_surf = intel_plane_ggtt_offset(plane_state) +
  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 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 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),
      skl_plane_surf(plane_state, 0));
}

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);

 /* 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_FLAT_CCS(to_i915(display->drm)) && DISPLAY_VER(display) < 20)
  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;
 int color_plane = icl_plane_color_plane(plane_state);
 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);

 /*
 * 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),
      skl_plane_surf(plane_state, color_plane));
}

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, plane_surf;

 plane_ctl |= skl_plane_ctl_crtc(crtc_state);
 plane_surf = skl_plane_surf(plane_state, 0);

 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) {
  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.enable &&
     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_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 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 < 1) {
  drm_dbg_kms(display->drm,
       "[PLANE:%d:%s] requested Y/RGB source size %dx%d outside limits (min: %dx1 max: %dx%d)\n",
       plane->base.base.id, plane->base.name,
       w, h, min_width, 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 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 || h > max_height) {
  drm_dbg_kms(display->drm,
       "[PLANE:%d:%s] CbCr source size %dx%d too big (limit %dx%d)\n",
       plane->base.base.id, plane->base.name,
       w, h, 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) &&
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=91 H=96 G=93

¤ Dauer der Verarbeitung: 0.18 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.