Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


SSL intel_color.c

  Interaktion und
PortierbarkeitC
 

/*
 * Copyright © 2016 Intel Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */


#include <drm/drm_print.h>

#include "i915_utils.h"
#include "i9xx_plane_regs.h"
#include "intel_color.h"
#include "intel_color_regs.h"
#include "intel_de.h"
#include "intel_display_types.h"
#include "intel_dsb.h"
#include "intel_vrr.h"

struct intel_color_funcs {
 int (*color_check)(struct intel_atomic_state *state,
      struct intel_crtc *crtc);
 /*
 * Program non-arming double buffered color management registers
 * before vblank evasion. The registers should then latch after
 * the arming register is written (by color_commit_arm()) during
 * the next vblank start, alongside any other double buffered
 * registers involved with the same commit. This hook is optional.
 */

 void (*color_commit_noarm)(struct intel_dsb *dsb,
       const struct intel_crtc_state *crtc_state);
 /*
 * Program arming double buffered color management registers
 * during vblank evasion. The registers (and whatever other registers
 * they arm that were written by color_commit_noarm) should then latch
 * during the next vblank start, alongside any other double buffered
 * registers involved with the same commit.
 */

 void (*color_commit_arm)(struct intel_dsb *dsb,
     const struct intel_crtc_state *crtc_state);
 /*
 * Perform any extra tasks needed after all the
 * double buffered registers have been latched.
 */

 void (*color_post_update)(const struct intel_crtc_state *crtc_state);
 /*
 * Load LUTs (and other single buffered color management
 * registers). Will (hopefully) be called during the vblank
 * following the latching of any double buffered registers
 * involved with the same commit.
 */

 void (*load_luts)(const struct intel_crtc_state *crtc_state);
 /*
 * Read out the LUTs from the hardware into the software state.
 * Used by eg. the hardware state checker.
 */

 void (*read_luts)(struct intel_crtc_state *crtc_state);
 /*
 * Compare the LUTs
 */

 bool (*lut_equal)(const struct intel_crtc_state *crtc_state,
     const struct drm_property_blob *blob1,
     const struct drm_property_blob *blob2,
     bool is_pre_csc_lut);
 /*
 * Read out the CSCs (if any) from the hardware into the
 * software state. Used by eg. the hardware state checker.
 */

 void (*read_csc)(struct intel_crtc_state *crtc_state);
 /*
 * Read config other than LUTs and CSCs, before them. Optional.
 */

 void (*get_config)(struct intel_crtc_state *crtc_state);
};

#define CTM_COEFF_SIGN (1ULL << 63)

#define CTM_COEFF_1_0 (1ULL << 32)
#define CTM_COEFF_2_0 (CTM_COEFF_1_0 << 1)
#define CTM_COEFF_4_0 (CTM_COEFF_2_0 << 1)
#define CTM_COEFF_8_0 (CTM_COEFF_4_0 << 1)
#define CTM_COEFF_0_5 (CTM_COEFF_1_0 >> 1)
#define CTM_COEFF_0_25 (CTM_COEFF_0_5 >> 1)
#define CTM_COEFF_0_125 (CTM_COEFF_0_25 >> 1)

#define CTM_COEFF_LIMITED_RANGE ((235ULL - 16ULL) * CTM_COEFF_1_0 / 255)

#define CTM_COEFF_NEGATIVE(coeff) (((coeff) & CTM_COEFF_SIGN) != 0)
#define CTM_COEFF_ABS(coeff)  ((coeff) & (CTM_COEFF_SIGN - 1))

#define LEGACY_LUT_LENGTH  256

/*
 * ILK+ csc matrix:
 *
 * |R/Cr|   | c0 c1 c2 |   ( |R/Cr|   |preoff0| )   |postoff0|
 * |G/Y | = | c3 c4 c5 | x ( |G/Y | + |preoff1| ) + |postoff1|
 * |B/Cb|   | c6 c7 c8 |   ( |B/Cb|   |preoff2| )   |postoff2|
 *
 * ILK/SNB don't have explicit post offsets, and instead
 * CSC_MODE_YUV_TO_RGB and CSC_BLACK_SCREEN_OFFSET are used:
 *  CSC_MODE_YUV_TO_RGB=0 + CSC_BLACK_SCREEN_OFFSET=0 -> 1/2, 0, 1/2
 *  CSC_MODE_YUV_TO_RGB=0 + CSC_BLACK_SCREEN_OFFSET=1 -> 1/2, 1/16, 1/2
 *  CSC_MODE_YUV_TO_RGB=1 + CSC_BLACK_SCREEN_OFFSET=0 -> 0, 0, 0
 *  CSC_MODE_YUV_TO_RGB=1 + CSC_BLACK_SCREEN_OFFSET=1 -> 1/16, 1/16, 1/16
 */


/*
 * Extract the CSC coefficient from a CTM coefficient (in U32.32 fixed point
 * format). This macro takes the coefficient we want transformed and the
 * number of fractional bits.
 *
 * We only have a 9 bits precision window which slides depending on the value
 * of the CTM coefficient and we write the value from bit 3. We also round the
 * value.
 */

#define ILK_CSC_COEFF_FP(coeff, fbits) \
 (clamp_val(((coeff) >> (32 - (fbits) - 3)) + 4, 0, 0xfff) & 0xff8)

#define ILK_CSC_COEFF_1_0 0x7800
#define ILK_CSC_COEFF_LIMITED_RANGE ((235 - 16) << (12 - 8)) /* exponent 0 */
#define ILK_CSC_POSTOFF_LIMITED_RANGE (16 << (12 - 8))

static const struct intel_csc_matrix ilk_csc_matrix_identity = {
 .preoff = {},
 .coeff = {
  ILK_CSC_COEFF_1_0, 0, 0,
  0, ILK_CSC_COEFF_1_0, 0,
  0, 0, ILK_CSC_COEFF_1_0,
 },
 .postoff = {},
};

/* Full range RGB -> limited range RGB matrix */
static const struct intel_csc_matrix ilk_csc_matrix_limited_range = {
 .preoff = {},
 .coeff = {
  ILK_CSC_COEFF_LIMITED_RANGE, 0, 0,
  0, ILK_CSC_COEFF_LIMITED_RANGE, 0,
  0, 0, ILK_CSC_COEFF_LIMITED_RANGE,
 },
 .postoff = {
  ILK_CSC_POSTOFF_LIMITED_RANGE,
  ILK_CSC_POSTOFF_LIMITED_RANGE,
  ILK_CSC_POSTOFF_LIMITED_RANGE,
 },
};

/* BT.709 full range RGB -> limited range YCbCr matrix */
static const struct intel_csc_matrix ilk_csc_matrix_rgb_to_ycbcr = {
 .preoff = {},
 .coeff = {
  0x1e08, 0x9cc0, 0xb528,
  0x2ba8, 0x09d8, 0x37e8,
  0xbce8, 0x9ad8, 0x1e08,
 },
 .postoff = {
  0x0800, 0x0100, 0x0800,
 },
};

static void intel_csc_clear(struct intel_csc_matrix *csc)
{
 memset(csc, 0, sizeof(*csc));
}

static bool lut_is_legacy(const struct drm_property_blob *lut)
{
 return lut && drm_color_lut_size(lut) == LEGACY_LUT_LENGTH;
}

/*
 * When using limited range, multiply the matrix given by userspace by
 * the matrix that we would use for the limited range.
 */

static u64 *ctm_mult_by_limited(u64 *result, const u64 *input)
{
 int i;

 for (i = 0; i < 9; i++) {
  u64 user_coeff = input[i];
  u32 limited_coeff = CTM_COEFF_LIMITED_RANGE;
  u32 abs_coeff = clamp_val(CTM_COEFF_ABS(user_coeff), 0,
       CTM_COEFF_4_0 - 1) >> 2;

  /*
 * By scaling every co-efficient with limited range (16-235)
 * vs full range (0-255) the final o/p will be scaled down to
 * fit in the limited range supported by the panel.
 */

  result[i] = mul_u32_u32(limited_coeff, abs_coeff) >> 30;
  result[i] |= user_coeff & CTM_COEFF_SIGN;
 }

 return result;
}

static void ilk_update_pipe_csc(struct intel_dsb *dsb,
    struct intel_crtc *crtc,
    const struct intel_csc_matrix *csc)
{
 struct intel_display *display = to_intel_display(crtc->base.dev);
 enum pipe pipe = crtc->pipe;

 intel_de_write_dsb(display, dsb, PIPE_CSC_PREOFF_HI(pipe),
      csc->preoff[0]);
 intel_de_write_dsb(display, dsb, PIPE_CSC_PREOFF_ME(pipe),
      csc->preoff[1]);
 intel_de_write_dsb(display, dsb, PIPE_CSC_PREOFF_LO(pipe),
      csc->preoff[2]);

 intel_de_write_dsb(display, dsb, PIPE_CSC_COEFF_RY_GY(pipe),
      csc->coeff[0] << 16 | csc->coeff[1]);
 intel_de_write_dsb(display, dsb, PIPE_CSC_COEFF_BY(pipe),
      csc->coeff[2] << 16);

 intel_de_write_dsb(display, dsb, PIPE_CSC_COEFF_RU_GU(pipe),
      csc->coeff[3] << 16 | csc->coeff[4]);
 intel_de_write_dsb(display, dsb, PIPE_CSC_COEFF_BU(pipe),
      csc->coeff[5] << 16);

 intel_de_write_dsb(display, dsb, PIPE_CSC_COEFF_RV_GV(pipe),
      csc->coeff[6] << 16 | csc->coeff[7]);
 intel_de_write_dsb(display, dsb, PIPE_CSC_COEFF_BV(pipe),
      csc->coeff[8] << 16);

 if (DISPLAY_VER(display) < 7)
  return;

 intel_de_write_dsb(display, dsb, PIPE_CSC_POSTOFF_HI(pipe),
      csc->postoff[0]);
 intel_de_write_dsb(display, dsb, PIPE_CSC_POSTOFF_ME(pipe),
      csc->postoff[1]);
 intel_de_write_dsb(display, dsb, PIPE_CSC_POSTOFF_LO(pipe),
      csc->postoff[2]);
}

static void ilk_read_pipe_csc(struct intel_crtc *crtc,
         struct intel_csc_matrix *csc)
{
 struct intel_display *display = to_intel_display(crtc);
 enum pipe pipe = crtc->pipe;
 u32 tmp;

 csc->preoff[0] = intel_de_read_fw(display, PIPE_CSC_PREOFF_HI(pipe));
 csc->preoff[1] = intel_de_read_fw(display, PIPE_CSC_PREOFF_ME(pipe));
 csc->preoff[2] = intel_de_read_fw(display, PIPE_CSC_PREOFF_LO(pipe));

 tmp = intel_de_read_fw(display, PIPE_CSC_COEFF_RY_GY(pipe));
 csc->coeff[0] = tmp >> 16;
 csc->coeff[1] = tmp & 0xffff;
 tmp = intel_de_read_fw(display, PIPE_CSC_COEFF_BY(pipe));
 csc->coeff[2] = tmp >> 16;

 tmp = intel_de_read_fw(display, PIPE_CSC_COEFF_RU_GU(pipe));
 csc->coeff[3] = tmp >> 16;
 csc->coeff[4] = tmp & 0xffff;
 tmp = intel_de_read_fw(display, PIPE_CSC_COEFF_BU(pipe));
 csc->coeff[5] = tmp >> 16;

 tmp = intel_de_read_fw(display, PIPE_CSC_COEFF_RV_GV(pipe));
 csc->coeff[6] = tmp >> 16;
 csc->coeff[7] = tmp & 0xffff;
 tmp = intel_de_read_fw(display, PIPE_CSC_COEFF_BV(pipe));
 csc->coeff[8] = tmp >> 16;

 if (DISPLAY_VER(display) < 7)
  return;

 csc->postoff[0] = intel_de_read_fw(display, PIPE_CSC_POSTOFF_HI(pipe));
 csc->postoff[1] = intel_de_read_fw(display, PIPE_CSC_POSTOFF_ME(pipe));
 csc->postoff[2] = intel_de_read_fw(display, PIPE_CSC_POSTOFF_LO(pipe));
}

static void ilk_read_csc(struct intel_crtc_state *crtc_state)
{
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);

 if (crtc_state->csc_enable)
  ilk_read_pipe_csc(crtc, &crtc_state->csc);
}

static void skl_read_csc(struct intel_crtc_state *crtc_state)
{
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);

 /*
 * Display WA #1184: skl,glk
 * Wa_1406463849: icl
 *
 * Danger! On SKL-ICL *reads* from the CSC coeff/offset registers
 * will disarm an already armed CSC double buffer update.
 * So this must not be called while armed. Fortunately the state checker
 * readout happens only after the update has been already been latched.
 *
 * On earlier and later platforms only writes to said registers will
 * disarm the update. This is considered normal behavior and also
 * happens with various other hardware units.
 */

 if (crtc_state->csc_enable)
  ilk_read_pipe_csc(crtc, &crtc_state->csc);
}

static void icl_update_output_csc(struct intel_dsb *dsb,
      struct intel_crtc *crtc,
      const struct intel_csc_matrix *csc)
{
 struct intel_display *display = to_intel_display(crtc->base.dev);
 enum pipe pipe = crtc->pipe;

 intel_de_write_dsb(display, dsb, PIPE_CSC_OUTPUT_PREOFF_HI(pipe),
      csc->preoff[0]);
 intel_de_write_dsb(display, dsb, PIPE_CSC_OUTPUT_PREOFF_ME(pipe),
      csc->preoff[1]);
 intel_de_write_dsb(display, dsb, PIPE_CSC_OUTPUT_PREOFF_LO(pipe),
      csc->preoff[2]);

 intel_de_write_dsb(display, dsb, PIPE_CSC_OUTPUT_COEFF_RY_GY(pipe),
      csc->coeff[0] << 16 | csc->coeff[1]);
 intel_de_write_dsb(display, dsb, PIPE_CSC_OUTPUT_COEFF_BY(pipe),
      csc->coeff[2] << 16);

 intel_de_write_dsb(display, dsb, PIPE_CSC_OUTPUT_COEFF_RU_GU(pipe),
      csc->coeff[3] << 16 | csc->coeff[4]);
 intel_de_write_dsb(display, dsb, PIPE_CSC_OUTPUT_COEFF_BU(pipe),
      csc->coeff[5] << 16);

 intel_de_write_dsb(display, dsb, PIPE_CSC_OUTPUT_COEFF_RV_GV(pipe),
      csc->coeff[6] << 16 | csc->coeff[7]);
 intel_de_write_dsb(display, dsb, PIPE_CSC_OUTPUT_COEFF_BV(pipe),
      csc->coeff[8] << 16);

 intel_de_write_dsb(display, dsb, PIPE_CSC_OUTPUT_POSTOFF_HI(pipe),
      csc->postoff[0]);
 intel_de_write_dsb(display, dsb, PIPE_CSC_OUTPUT_POSTOFF_ME(pipe),
      csc->postoff[1]);
 intel_de_write_dsb(display, dsb, PIPE_CSC_OUTPUT_POSTOFF_LO(pipe),
      csc->postoff[2]);
}

static void icl_read_output_csc(struct intel_crtc *crtc,
    struct intel_csc_matrix *csc)
{
 struct intel_display *display = to_intel_display(crtc);
 enum pipe pipe = crtc->pipe;
 u32 tmp;

 csc->preoff[0] = intel_de_read_fw(display, PIPE_CSC_OUTPUT_PREOFF_HI(pipe));
 csc->preoff[1] = intel_de_read_fw(display, PIPE_CSC_OUTPUT_PREOFF_ME(pipe));
 csc->preoff[2] = intel_de_read_fw(display, PIPE_CSC_OUTPUT_PREOFF_LO(pipe));

 tmp = intel_de_read_fw(display, PIPE_CSC_OUTPUT_COEFF_RY_GY(pipe));
 csc->coeff[0] = tmp >> 16;
 csc->coeff[1] = tmp & 0xffff;
 tmp = intel_de_read_fw(display, PIPE_CSC_OUTPUT_COEFF_BY(pipe));
 csc->coeff[2] = tmp >> 16;

 tmp = intel_de_read_fw(display, PIPE_CSC_OUTPUT_COEFF_RU_GU(pipe));
 csc->coeff[3] = tmp >> 16;
 csc->coeff[4] = tmp & 0xffff;
 tmp = intel_de_read_fw(display, PIPE_CSC_OUTPUT_COEFF_BU(pipe));
 csc->coeff[5] = tmp >> 16;

 tmp = intel_de_read_fw(display, PIPE_CSC_OUTPUT_COEFF_RV_GV(pipe));
 csc->coeff[6] = tmp >> 16;
 csc->coeff[7] = tmp & 0xffff;
 tmp = intel_de_read_fw(display, PIPE_CSC_OUTPUT_COEFF_BV(pipe));
 csc->coeff[8] = tmp >> 16;

 csc->postoff[0] = intel_de_read_fw(display, PIPE_CSC_OUTPUT_POSTOFF_HI(pipe));
 csc->postoff[1] = intel_de_read_fw(display, PIPE_CSC_OUTPUT_POSTOFF_ME(pipe));
 csc->postoff[2] = intel_de_read_fw(display, PIPE_CSC_OUTPUT_POSTOFF_LO(pipe));
}

static void icl_read_csc(struct intel_crtc_state *crtc_state)
{
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);

 /*
 * Wa_1406463849: icl
 *
 * See skl_read_csc()
 */

 if (crtc_state->csc_mode & ICL_CSC_ENABLE)
  ilk_read_pipe_csc(crtc, &crtc_state->csc);

 if (crtc_state->csc_mode & ICL_OUTPUT_CSC_ENABLE)
  icl_read_output_csc(crtc, &crtc_state->output_csc);
}

static bool ilk_limited_range(const struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(crtc_state);

 /* icl+ have dedicated output CSC */
 if (DISPLAY_VER(display) >= 11)
  return false;

 /* pre-hsw have TRANSCONF_COLOR_RANGE_SELECT */
 if (DISPLAY_VER(display) < 7 || display->platform.ivybridge)
  return false;

 return crtc_state->limited_color_range;
}

static bool ilk_lut_limited_range(const struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(crtc_state);

 if (!ilk_limited_range(crtc_state))
  return false;

 if (crtc_state->c8_planes)
  return false;

 if (DISPLAY_VER(display) == 10)
  return crtc_state->hw.gamma_lut;
 else
  return crtc_state->hw.gamma_lut &&
   (crtc_state->hw.degamma_lut || crtc_state->hw.ctm);
}

static bool ilk_csc_limited_range(const struct intel_crtc_state *crtc_state)
{
 if (!ilk_limited_range(crtc_state))
  return false;

 return !ilk_lut_limited_range(crtc_state);
}

static void ilk_csc_copy(struct intel_display *display,
    struct intel_csc_matrix *dst,
    const struct intel_csc_matrix *src)
{
 *dst = *src;

 if (DISPLAY_VER(display) < 7)
  memset(dst->postoff, 0, sizeof(dst->postoff));
}

static void ilk_csc_convert_ctm(const struct intel_crtc_state *crtc_state,
    struct intel_csc_matrix *csc,
    bool limited_color_range)
{
 struct intel_display *display = to_intel_display(crtc_state);
 const struct drm_color_ctm *ctm = crtc_state->hw.ctm->data;
 const u64 *input;
 u64 temp[9];
 int i;

 /* for preoff/postoff */
 if (limited_color_range)
  ilk_csc_copy(display, csc, &ilk_csc_matrix_limited_range);
 else
  ilk_csc_copy(display, csc, &ilk_csc_matrix_identity);

 if (limited_color_range)
  input = ctm_mult_by_limited(temp, ctm->matrix);
 else
  input = ctm->matrix;

 /*
 * Convert fixed point S31.32 input to format supported by the
 * hardware.
 */

 for (i = 0; i < 9; i++) {
  u64 abs_coeff = ((1ULL << 63) - 1) & input[i];

  /*
 * Clamp input value to min/max supported by
 * hardware.
 */

  abs_coeff = clamp_val(abs_coeff, 0, CTM_COEFF_4_0 - 1);

  csc->coeff[i] = 0;

  /* sign bit */
  if (CTM_COEFF_NEGATIVE(input[i]))
   csc->coeff[i] |= 1 << 15;

  if (abs_coeff < CTM_COEFF_0_125)
   csc->coeff[i] |= (3 << 12) |
    ILK_CSC_COEFF_FP(abs_coeff, 12);
  else if (abs_coeff < CTM_COEFF_0_25)
   csc->coeff[i] |= (2 << 12) |
    ILK_CSC_COEFF_FP(abs_coeff, 11);
  else if (abs_coeff < CTM_COEFF_0_5)
   csc->coeff[i] |= (1 << 12) |
    ILK_CSC_COEFF_FP(abs_coeff, 10);
  else if (abs_coeff < CTM_COEFF_1_0)
   csc->coeff[i] |= ILK_CSC_COEFF_FP(abs_coeff, 9);
  else if (abs_coeff < CTM_COEFF_2_0)
   csc->coeff[i] |= (7 << 12) |
    ILK_CSC_COEFF_FP(abs_coeff, 8);
  else
   csc->coeff[i] |= (6 << 12) |
    ILK_CSC_COEFF_FP(abs_coeff, 7);
 }
}

static void ilk_assign_csc(struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(crtc_state);
 bool limited_color_range = ilk_csc_limited_range(crtc_state);

 if (crtc_state->hw.ctm) {
  drm_WARN_ON(display->drm, !crtc_state->csc_enable);

  ilk_csc_convert_ctm(crtc_state, &crtc_state->csc, limited_color_range);
 } else if (crtc_state->output_format != INTEL_OUTPUT_FORMAT_RGB) {
  drm_WARN_ON(display->drm, !crtc_state->csc_enable);

  ilk_csc_copy(display, &crtc_state->csc, &ilk_csc_matrix_rgb_to_ycbcr);
 } else if (limited_color_range) {
  drm_WARN_ON(display->drm, !crtc_state->csc_enable);

  ilk_csc_copy(display, &crtc_state->csc, &ilk_csc_matrix_limited_range);
 } else if (crtc_state->csc_enable) {
  /*
 * On GLK both pipe CSC and degamma LUT are controlled
 * by csc_enable. Hence for the cases where the degama
 * LUT is needed but CSC is not we need to load an
 * identity matrix.
 */

  drm_WARN_ON(display->drm, !display->platform.geminilake);

  ilk_csc_copy(display, &crtc_state->csc, &ilk_csc_matrix_identity);
 } else {
  intel_csc_clear(&crtc_state->csc);
 }
}

static void ilk_load_csc_matrix(struct intel_dsb *dsb,
    const struct intel_crtc_state *crtc_state)
{
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);

 if (crtc_state->csc_enable)
  ilk_update_pipe_csc(dsb, crtc, &crtc_state->csc);
}

static void icl_assign_csc(struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(crtc_state);

 if (crtc_state->hw.ctm) {
  drm_WARN_ON(display->drm, (crtc_state->csc_mode & ICL_CSC_ENABLE) == 0);

  ilk_csc_convert_ctm(crtc_state, &crtc_state->csc, false);
 } else {
  drm_WARN_ON(display->drm, (crtc_state->csc_mode & ICL_CSC_ENABLE) != 0);

  intel_csc_clear(&crtc_state->csc);
 }

 if (crtc_state->output_format != INTEL_OUTPUT_FORMAT_RGB) {
  drm_WARN_ON(display->drm, (crtc_state->csc_mode & ICL_OUTPUT_CSC_ENABLE) == 0);

  ilk_csc_copy(display, &crtc_state->output_csc, &ilk_csc_matrix_rgb_to_ycbcr);
 } else if (crtc_state->limited_color_range) {
  drm_WARN_ON(display->drm, (crtc_state->csc_mode & ICL_OUTPUT_CSC_ENABLE) == 0);

  ilk_csc_copy(display, &crtc_state->output_csc, &ilk_csc_matrix_limited_range);
 } else {
  drm_WARN_ON(display->drm, (crtc_state->csc_mode & ICL_OUTPUT_CSC_ENABLE) != 0);

  intel_csc_clear(&crtc_state->output_csc);
 }
}

static void icl_load_csc_matrix(struct intel_dsb *dsb,
    const struct intel_crtc_state *crtc_state)
{
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);

 if (crtc_state->csc_mode & ICL_CSC_ENABLE)
  ilk_update_pipe_csc(dsb, crtc, &crtc_state->csc);

 if (crtc_state->csc_mode & ICL_OUTPUT_CSC_ENABLE)
  icl_update_output_csc(dsb, crtc, &crtc_state->output_csc);
}

static u16 ctm_to_twos_complement(u64 coeff, int int_bits, int frac_bits)
{
 s64 c = CTM_COEFF_ABS(coeff);

 /* leave an extra bit for rounding */
 c >>= 32 - frac_bits - 1;

 /* round and drop the extra bit */
 c = (c + 1) >> 1;

 if (CTM_COEFF_NEGATIVE(coeff))
  c = -c;

 c = clamp(c, -(s64)BIT(int_bits + frac_bits - 1),
    (s64)(BIT(int_bits + frac_bits - 1) - 1));

 return c & (BIT(int_bits + frac_bits) - 1);
}

/*
 * VLV/CHV Wide Gamut Color Correction (WGC) CSC
 * |r|   | c0 c1 c2 |   |r|
 * |g| = | c3 c4 c5 | x |g|
 * |b|   | c6 c7 c8 |   |b|
 *
 * Coefficients are two's complement s2.10.
 */

static void vlv_wgc_csc_convert_ctm(const struct intel_crtc_state *crtc_state,
        struct intel_csc_matrix *csc)
{
 const struct drm_color_ctm *ctm = crtc_state->hw.ctm->data;
 int i;

 for (i = 0; i < 9; i++)
  csc->coeff[i] = ctm_to_twos_complement(ctm->matrix[i], 2, 10);
}

static void vlv_load_wgc_csc(struct intel_crtc *crtc,
        const struct intel_csc_matrix *csc)
{
 struct intel_display *display = to_intel_display(crtc);
 enum pipe pipe = crtc->pipe;

 intel_de_write_fw(display, PIPE_WGC_C01_C00(display, pipe),
     csc->coeff[1] << 16 | csc->coeff[0]);
 intel_de_write_fw(display, PIPE_WGC_C02(display, pipe),
     csc->coeff[2]);

 intel_de_write_fw(display, PIPE_WGC_C11_C10(display, pipe),
     csc->coeff[4] << 16 | csc->coeff[3]);
 intel_de_write_fw(display, PIPE_WGC_C12(display, pipe),
     csc->coeff[5]);

 intel_de_write_fw(display, PIPE_WGC_C21_C20(display, pipe),
     csc->coeff[7] << 16 | csc->coeff[6]);
 intel_de_write_fw(display, PIPE_WGC_C22(display, pipe),
     csc->coeff[8]);
}

static void vlv_read_wgc_csc(struct intel_crtc *crtc,
        struct intel_csc_matrix *csc)
{
 struct intel_display *display = to_intel_display(crtc);
 enum pipe pipe = crtc->pipe;
 u32 tmp;

 tmp = intel_de_read_fw(display, PIPE_WGC_C01_C00(display, pipe));
 csc->coeff[0] = tmp & 0xffff;
 csc->coeff[1] = tmp >> 16;

 tmp = intel_de_read_fw(display, PIPE_WGC_C02(display, pipe));
 csc->coeff[2] = tmp & 0xffff;

 tmp = intel_de_read_fw(display, PIPE_WGC_C11_C10(display, pipe));
 csc->coeff[3] = tmp & 0xffff;
 csc->coeff[4] = tmp >> 16;

 tmp = intel_de_read_fw(display, PIPE_WGC_C12(display, pipe));
 csc->coeff[5] = tmp & 0xffff;

 tmp = intel_de_read_fw(display, PIPE_WGC_C21_C20(display, pipe));
 csc->coeff[6] = tmp & 0xffff;
 csc->coeff[7] = tmp >> 16;

 tmp = intel_de_read_fw(display, PIPE_WGC_C22(display, pipe));
 csc->coeff[8] = tmp & 0xffff;
}

static void vlv_read_csc(struct intel_crtc_state *crtc_state)
{
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);

 if (crtc_state->wgc_enable)
  vlv_read_wgc_csc(crtc, &crtc_state->csc);
}

static void vlv_assign_csc(struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(crtc_state);

 if (crtc_state->hw.ctm) {
  drm_WARN_ON(display->drm, !crtc_state->wgc_enable);

  vlv_wgc_csc_convert_ctm(crtc_state, &crtc_state->csc);
 } else {
  drm_WARN_ON(display->drm, crtc_state->wgc_enable);

  intel_csc_clear(&crtc_state->csc);
 }
}

/*
 * CHV Color Gamut Mapping (CGM) CSC
 * |r|   | c0 c1 c2 |   |r|
 * |g| = | c3 c4 c5 | x |g|
 * |b|   | c6 c7 c8 |   |b|
 *
 * Coefficients are two's complement s4.12.
 */

static void chv_cgm_csc_convert_ctm(const struct intel_crtc_state *crtc_state,
        struct intel_csc_matrix *csc)
{
 const struct drm_color_ctm *ctm = crtc_state->hw.ctm->data;
 int i;

 for (i = 0; i < 9; i++)
  csc->coeff[i] = ctm_to_twos_complement(ctm->matrix[i], 4, 12);
}

#define CHV_CGM_CSC_COEFF_1_0 (1 << 12)

static const struct intel_csc_matrix chv_cgm_csc_matrix_identity = {
 .coeff = {
  CHV_CGM_CSC_COEFF_1_0, 0, 0,
  0, CHV_CGM_CSC_COEFF_1_0, 0,
  0, 0, CHV_CGM_CSC_COEFF_1_0,
 },
};

static void chv_load_cgm_csc(struct intel_crtc *crtc,
        const struct intel_csc_matrix *csc)
{
 struct intel_display *display = to_intel_display(crtc);
 enum pipe pipe = crtc->pipe;

 intel_de_write_fw(display, CGM_PIPE_CSC_COEFF01(pipe),
     csc->coeff[1] << 16 | csc->coeff[0]);
 intel_de_write_fw(display, CGM_PIPE_CSC_COEFF23(pipe),
     csc->coeff[3] << 16 | csc->coeff[2]);
 intel_de_write_fw(display, CGM_PIPE_CSC_COEFF45(pipe),
     csc->coeff[5] << 16 | csc->coeff[4]);
 intel_de_write_fw(display, CGM_PIPE_CSC_COEFF67(pipe),
     csc->coeff[7] << 16 | csc->coeff[6]);
 intel_de_write_fw(display, CGM_PIPE_CSC_COEFF8(pipe),
     csc->coeff[8]);
}

static void chv_read_cgm_csc(struct intel_crtc *crtc,
        struct intel_csc_matrix *csc)
{
 struct intel_display *display = to_intel_display(crtc);
 enum pipe pipe = crtc->pipe;
 u32 tmp;

 tmp = intel_de_read_fw(display, CGM_PIPE_CSC_COEFF01(pipe));
 csc->coeff[0] = tmp & 0xffff;
 csc->coeff[1] = tmp >> 16;

 tmp = intel_de_read_fw(display, CGM_PIPE_CSC_COEFF23(pipe));
 csc->coeff[2] = tmp & 0xffff;
 csc->coeff[3] = tmp >> 16;

 tmp = intel_de_read_fw(display, CGM_PIPE_CSC_COEFF45(pipe));
 csc->coeff[4] = tmp & 0xffff;
 csc->coeff[5] = tmp >> 16;

 tmp = intel_de_read_fw(display, CGM_PIPE_CSC_COEFF67(pipe));
 csc->coeff[6] = tmp & 0xffff;
 csc->coeff[7] = tmp >> 16;

 tmp = intel_de_read_fw(display, CGM_PIPE_CSC_COEFF8(pipe));
 csc->coeff[8] = tmp & 0xffff;
}

static void chv_read_csc(struct intel_crtc_state *crtc_state)
{
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);

 if (crtc_state->cgm_mode & CGM_PIPE_MODE_CSC)
  chv_read_cgm_csc(crtc, &crtc_state->csc);
}

static void chv_assign_csc(struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(crtc_state);

 drm_WARN_ON(display->drm, crtc_state->wgc_enable);

 if (crtc_state->hw.ctm) {
  drm_WARN_ON(display->drm, (crtc_state->cgm_mode & CGM_PIPE_MODE_CSC) == 0);

  chv_cgm_csc_convert_ctm(crtc_state, &crtc_state->csc);
 } else {
  drm_WARN_ON(display->drm, (crtc_state->cgm_mode & CGM_PIPE_MODE_CSC) == 0);

  crtc_state->csc = chv_cgm_csc_matrix_identity;
 }
}

/* convert hw value with given bit_precision to lut property val */
static u32 intel_color_lut_pack(u32 val, int bit_precision)
{
 if (bit_precision > 16)
  return DIV_ROUND_CLOSEST_ULL(mul_u32_u32(val, (1 << 16) - 1),
          (1 << bit_precision) - 1);
 else
  return DIV_ROUND_CLOSEST(val * ((1 << 16) - 1),
      (1 << bit_precision) - 1);
}

static u32 i9xx_lut_8(const struct drm_color_lut *color)
{
 return REG_FIELD_PREP(PALETTE_RED_MASK, drm_color_lut_extract(color->red, 8)) |
  REG_FIELD_PREP(PALETTE_GREEN_MASK, drm_color_lut_extract(color->green, 8)) |
  REG_FIELD_PREP(PALETTE_BLUE_MASK, drm_color_lut_extract(color->blue, 8));
}

static void i9xx_lut_8_pack(struct drm_color_lut *entry, u32 val)
{
 entry->red = intel_color_lut_pack(REG_FIELD_GET(PALETTE_RED_MASK, val), 8);
 entry->green = intel_color_lut_pack(REG_FIELD_GET(PALETTE_GREEN_MASK, val), 8);
 entry->blue = intel_color_lut_pack(REG_FIELD_GET(PALETTE_BLUE_MASK, val), 8);
}

/* i8xx/i9xx+ 10bit slope format "even DW" (low 8 bits) */
static u32 _i9xx_lut_10_ldw(u16 a)
{
 return drm_color_lut_extract(a, 10) & 0xff;
}

static u32 i9xx_lut_10_ldw(const struct drm_color_lut *color)
{
 return REG_FIELD_PREP(PALETTE_RED_MASK, _i9xx_lut_10_ldw(color[0].red)) |
  REG_FIELD_PREP(PALETTE_GREEN_MASK, _i9xx_lut_10_ldw(color[0].green)) |
  REG_FIELD_PREP(PALETTE_BLUE_MASK, _i9xx_lut_10_ldw(color[0].blue));
}

/* i8xx/i9xx+ 10bit slope format "odd DW" (high 2 bits + slope) */
static u32 _i9xx_lut_10_udw(u16 a, u16 b)
{
 unsigned int mantissa, exponent;

 a = drm_color_lut_extract(a, 10);
 b = drm_color_lut_extract(b, 10);

 /* b = a + 8 * m * 2 ^ -e */
 mantissa = clamp(b - a, 0, 0x7f);
 exponent = 3;
 while (mantissa > 0xf) {
  mantissa >>= 1;
  exponent--;
 }

 return (exponent << 6) |
  (mantissa << 2) |
  (a >> 8);
}

static u32 i9xx_lut_10_udw(const struct drm_color_lut *color)
{
 return REG_FIELD_PREP(PALETTE_RED_MASK, _i9xx_lut_10_udw(color[0].red, color[1].red)) |
  REG_FIELD_PREP(PALETTE_GREEN_MASK, _i9xx_lut_10_udw(color[0].green, color[1].green)) |
  REG_FIELD_PREP(PALETTE_BLUE_MASK, _i9xx_lut_10_udw(color[0].blue, color[1].blue));
}

static void i9xx_lut_10_pack(struct drm_color_lut *color,
        u32 ldw, u32 udw)
{
 u16 red = REG_FIELD_GET(PALETTE_10BIT_RED_LDW_MASK, ldw) |
  REG_FIELD_GET(PALETTE_10BIT_RED_UDW_MASK, udw) << 8;
 u16 green = REG_FIELD_GET(PALETTE_10BIT_GREEN_LDW_MASK, ldw) |
  REG_FIELD_GET(PALETTE_10BIT_GREEN_UDW_MASK, udw) << 8;
 u16 blue = REG_FIELD_GET(PALETTE_10BIT_BLUE_LDW_MASK, ldw) |
  REG_FIELD_GET(PALETTE_10BIT_BLUE_UDW_MASK, udw) << 8;

 color->red = intel_color_lut_pack(red, 10);
 color->green = intel_color_lut_pack(green, 10);
 color->blue = intel_color_lut_pack(blue, 10);
}

static void i9xx_lut_10_pack_slope(struct drm_color_lut *color,
       u32 ldw, u32 udw)
{
 int r_exp = REG_FIELD_GET(PALETTE_10BIT_RED_EXP_MASK, udw);
 int r_mant = REG_FIELD_GET(PALETTE_10BIT_RED_MANT_MASK, udw);
 int g_exp = REG_FIELD_GET(PALETTE_10BIT_GREEN_EXP_MASK, udw);
 int g_mant = REG_FIELD_GET(PALETTE_10BIT_GREEN_MANT_MASK, udw);
 int b_exp = REG_FIELD_GET(PALETTE_10BIT_BLUE_EXP_MASK, udw);
 int b_mant = REG_FIELD_GET(PALETTE_10BIT_BLUE_MANT_MASK, udw);

 i9xx_lut_10_pack(color, ldw, udw);

 color->red += r_mant << (3 - r_exp);
 color->green += g_mant << (3 - g_exp);
 color->blue += b_mant << (3 - b_exp);
}

/* i965+ "10.6" bit interpolated format "even DW" (low 8 bits) */
static u32 i965_lut_10p6_ldw(const struct drm_color_lut *color)
{
 return REG_FIELD_PREP(PALETTE_RED_MASK, color->red & 0xff) |
  REG_FIELD_PREP(PALETTE_GREEN_MASK, color->green & 0xff) |
  REG_FIELD_PREP(PALETTE_BLUE_MASK, color->blue & 0xff);
}

/* i965+ "10.6" interpolated format "odd DW" (high 8 bits) */
static u32 i965_lut_10p6_udw(const struct drm_color_lut *color)
{
 return REG_FIELD_PREP(PALETTE_RED_MASK, color->red >> 8) |
  REG_FIELD_PREP(PALETTE_GREEN_MASK, color->green >> 8) |
  REG_FIELD_PREP(PALETTE_BLUE_MASK, color->blue >> 8);
}

static void i965_lut_10p6_pack(struct drm_color_lut *entry, u32 ldw, u32 udw)
{
 entry->red = REG_FIELD_GET(PALETTE_RED_MASK, udw) << 8 |
  REG_FIELD_GET(PALETTE_RED_MASK, ldw);
 entry->green = REG_FIELD_GET(PALETTE_GREEN_MASK, udw) << 8 |
  REG_FIELD_GET(PALETTE_GREEN_MASK, ldw);
 entry->blue = REG_FIELD_GET(PALETTE_BLUE_MASK, udw) << 8 |
  REG_FIELD_GET(PALETTE_BLUE_MASK, ldw);
}

static u16 i965_lut_11p6_max_pack(u32 val)
{
 /* PIPEGCMAX is 11.6, clamp to 10.6 */
 return min(val, 0xffffu);
}

static u32 ilk_lut_10(const struct drm_color_lut *color)
{
 return REG_FIELD_PREP(PREC_PALETTE_10_RED_MASK, drm_color_lut_extract(color->red, 10)) |
  REG_FIELD_PREP(PREC_PALETTE_10_GREEN_MASK, drm_color_lut_extract(color->green, 10)) |
  REG_FIELD_PREP(PREC_PALETTE_10_BLUE_MASK, drm_color_lut_extract(color->blue, 10));
}

static void ilk_lut_10_pack(struct drm_color_lut *entry, u32 val)
{
 entry->red = intel_color_lut_pack(REG_FIELD_GET(PREC_PALETTE_10_RED_MASK, val), 10);
 entry->green = intel_color_lut_pack(REG_FIELD_GET(PREC_PALETTE_10_GREEN_MASK, val), 10);
 entry->blue = intel_color_lut_pack(REG_FIELD_GET(PREC_PALETTE_10_BLUE_MASK, val), 10);
}

/* ilk+ "12.4" interpolated format (low 6 bits) */
static u32 ilk_lut_12p4_ldw(const struct drm_color_lut *color)
{
 return REG_FIELD_PREP(PREC_PALETTE_12P4_RED_LDW_MASK, color->red & 0x3f) |
  REG_FIELD_PREP(PREC_PALETTE_12P4_GREEN_LDW_MASK, color->green & 0x3f) |
  REG_FIELD_PREP(PREC_PALETTE_12P4_BLUE_LDW_MASK, color->blue & 0x3f);
}

/* ilk+ "12.4" interpolated format (high 10 bits) */
static u32 ilk_lut_12p4_udw(const struct drm_color_lut *color)
{
 return REG_FIELD_PREP(PREC_PALETTE_12P4_RED_UDW_MASK, color->red >> 6) |
  REG_FIELD_PREP(PREC_PALETTE_12P4_GREEN_UDW_MASK, color->green >> 6) |
  REG_FIELD_PREP(PREC_PALETTE_12P4_BLUE_UDW_MASK, color->blue >> 6);
}

static void ilk_lut_12p4_pack(struct drm_color_lut *entry, u32 ldw, u32 udw)
{
 entry->red = REG_FIELD_GET(PREC_PALETTE_12P4_RED_UDW_MASK, udw) << 6 |
  REG_FIELD_GET(PREC_PALETTE_12P4_RED_LDW_MASK, ldw);
 entry->green = REG_FIELD_GET(PREC_PALETTE_12P4_GREEN_UDW_MASK, udw) << 6 |
  REG_FIELD_GET(PREC_PALETTE_12P4_GREEN_LDW_MASK, ldw);
 entry->blue = REG_FIELD_GET(PREC_PALETTE_12P4_BLUE_UDW_MASK, udw) << 6 |
  REG_FIELD_GET(PREC_PALETTE_12P4_BLUE_LDW_MASK, ldw);
}

static void icl_color_commit_noarm(struct intel_dsb *dsb,
       const struct intel_crtc_state *crtc_state)
{
 /*
 * Despite Wa_1406463849, ICL no longer suffers from the SKL
 * DC5/PSR CSC black screen issue (see skl_color_commit_noarm()).
 * Possibly due to the extra sticky CSC arming
 * (see icl_color_post_update()).
 *
 * On TGL+ all CSC arming issues have been properly fixed.
 */

 icl_load_csc_matrix(dsb, crtc_state);
}

static void skl_color_commit_noarm(struct intel_dsb *dsb,
       const struct intel_crtc_state *crtc_state)
{
 /*
 * Possibly related to display WA #1184, SKL CSC loses the latched
 * CSC coeff/offset register values if the CSC registers are disarmed
 * between DC5 exit and PSR exit. This will cause the plane(s) to
 * output all black (until CSC_MODE is rearmed and properly latched).
 * Once PSR exit (and proper register latching) has occurred the
 * danger is over. Thus when PSR is enabled the CSC coeff/offset
 * register programming will be performed from skl_color_commit_arm()
 * which is called after PSR exit.
 */

 if (!crtc_state->has_psr)
  ilk_load_csc_matrix(dsb, crtc_state);
}

static void ilk_color_commit_noarm(struct intel_dsb *dsb,
       const struct intel_crtc_state *crtc_state)
{
 ilk_load_csc_matrix(dsb, crtc_state);
}

static void i9xx_color_commit_arm(struct intel_dsb *dsb,
      const struct intel_crtc_state *crtc_state)
{
 /* update TRANSCONF GAMMA_MODE */
 i9xx_set_pipeconf(crtc_state);
}

static void ilk_color_commit_arm(struct intel_dsb *dsb,
     const struct intel_crtc_state *crtc_state)
{
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
 struct intel_display *display = to_intel_display(crtc);

 /* update TRANSCONF GAMMA_MODE */
 ilk_set_pipeconf(crtc_state);

 intel_de_write_fw(display, PIPE_CSC_MODE(crtc->pipe),
     crtc_state->csc_mode);
}

static void hsw_color_commit_arm(struct intel_dsb *dsb,
     const struct intel_crtc_state *crtc_state)
{
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
 struct intel_display *display = to_intel_display(crtc);

 intel_de_write(display, GAMMA_MODE(crtc->pipe),
         crtc_state->gamma_mode);

 intel_de_write_fw(display, PIPE_CSC_MODE(crtc->pipe),
     crtc_state->csc_mode);
}

static u32 hsw_read_gamma_mode(struct intel_crtc *crtc)
{
 struct intel_display *display = to_intel_display(crtc);

 return intel_de_read(display, GAMMA_MODE(crtc->pipe));
}

static u32 ilk_read_csc_mode(struct intel_crtc *crtc)
{
 struct intel_display *display = to_intel_display(crtc);

 return intel_de_read(display, PIPE_CSC_MODE(crtc->pipe));
}

static void i9xx_get_config(struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(crtc_state);
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
 struct intel_plane *plane = to_intel_plane(crtc->base.primary);
 enum i9xx_plane_id i9xx_plane = plane->i9xx_plane;
 u32 tmp;

 tmp = intel_de_read(display, DSPCNTR(display, i9xx_plane));

 if (tmp & DISP_PIPE_GAMMA_ENABLE)
  crtc_state->gamma_enable = true;

 if (!HAS_GMCH(display) && tmp & DISP_PIPE_CSC_ENABLE)
  crtc_state->csc_enable = true;
}

static void hsw_get_config(struct intel_crtc_state *crtc_state)
{
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);

 crtc_state->gamma_mode = hsw_read_gamma_mode(crtc);
 crtc_state->csc_mode = ilk_read_csc_mode(crtc);

 i9xx_get_config(crtc_state);
}

static void skl_get_config(struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(crtc_state);
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
 u32 tmp;

 crtc_state->gamma_mode = hsw_read_gamma_mode(crtc);
 crtc_state->csc_mode = ilk_read_csc_mode(crtc);

 tmp = intel_de_read(display, SKL_BOTTOM_COLOR(crtc->pipe));

 if (tmp & SKL_BOTTOM_COLOR_GAMMA_ENABLE)
  crtc_state->gamma_enable = true;

 if (tmp & SKL_BOTTOM_COLOR_CSC_ENABLE)
  crtc_state->csc_enable = true;
}

static void skl_color_commit_arm(struct intel_dsb *dsb,
     const struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(crtc_state);
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
 enum pipe pipe = crtc->pipe;
 u32 val = 0;

 if (crtc_state->has_psr)
  ilk_load_csc_matrix(dsb, crtc_state);

 /*
 * We don't (yet) allow userspace to control the pipe background color,
 * so force it to black, but apply pipe gamma and CSC appropriately
 * so that its handling will match how we program our planes.
 */

 if (crtc_state->gamma_enable)
  val |= SKL_BOTTOM_COLOR_GAMMA_ENABLE;
 if (crtc_state->csc_enable)
  val |= SKL_BOTTOM_COLOR_CSC_ENABLE;
 intel_de_write_dsb(display, dsb, SKL_BOTTOM_COLOR(pipe), val);

 intel_de_write_dsb(display, dsb, GAMMA_MODE(crtc->pipe), crtc_state->gamma_mode);

 intel_de_write_dsb(display, dsb, PIPE_CSC_MODE(crtc->pipe), crtc_state->csc_mode);
}

static void icl_color_commit_arm(struct intel_dsb *dsb,
     const struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(crtc_state);
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
 enum pipe pipe = crtc->pipe;

 /*
 * We don't (yet) allow userspace to control the pipe background color,
 * so force it to black.
 */

 intel_de_write_dsb(display, dsb, SKL_BOTTOM_COLOR(pipe), 0);

 intel_de_write_dsb(display, dsb, GAMMA_MODE(crtc->pipe), crtc_state->gamma_mode);

 intel_de_write_dsb(display, dsb, PIPE_CSC_MODE(crtc->pipe), crtc_state->csc_mode);
}

static void icl_color_post_update(const struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(crtc_state);
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);

 /*
 * Despite Wa_1406463849, ICL CSC is no longer disarmed by
 * coeff/offset register *writes*. Instead, once CSC_MODE
 * is armed it stays armed, even after it has been latched.
 * Afterwards the coeff/offset registers become effectively
 * self-arming. That self-arming must be disabled before the
 * next icl_color_commit_noarm() tries to write the next set
 * of coeff/offset registers. Fortunately register *reads*
 * do still disarm the CSC. Naturally this must not be done
 * until the previously written CSC registers have actually
 * been latched.
 *
 * TGL+ no longer need this workaround.
 */

 intel_de_read_fw(display, PIPE_CSC_PREOFF_HI(crtc->pipe));
}

static struct drm_property_blob *
create_linear_lut(struct intel_display *display, int lut_size)
{
 struct drm_property_blob *blob;
 struct drm_color_lut *lut;
 int i;

 blob = drm_property_create_blob(display->drm,
     sizeof(lut[0]) * lut_size,
     NULL);
 if (IS_ERR(blob))
  return blob;

 lut = blob->data;

 for (i = 0; i < lut_size; i++) {
  u16 val = 0xffff * i / (lut_size - 1);

  lut[i].red = val;
  lut[i].green = val;
  lut[i].blue = val;
 }

 return blob;
}

static u16 lut_limited_range(unsigned int value)
{
 unsigned int min = 16 << 8;
 unsigned int max = 235 << 8;

 return value * (max - min) / 0xffff + min;
}

static struct drm_property_blob *
create_resized_lut(struct intel_display *display,
     const struct drm_property_blob *blob_in, int lut_out_size,
     bool limited_color_range)
{
 int i, lut_in_size = drm_color_lut_size(blob_in);
 struct drm_property_blob *blob_out;
 const struct drm_color_lut *lut_in;
 struct drm_color_lut *lut_out;

 blob_out = drm_property_create_blob(display->drm,
         sizeof(lut_out[0]) * lut_out_size,
         NULL);
 if (IS_ERR(blob_out))
  return blob_out;

 lut_in = blob_in->data;
 lut_out = blob_out->data;

 for (i = 0; i < lut_out_size; i++) {
  const struct drm_color_lut *entry =
   &lut_in[i * (lut_in_size - 1) / (lut_out_size - 1)];

  if (limited_color_range) {
   lut_out[i].red = lut_limited_range(entry->red);
   lut_out[i].green = lut_limited_range(entry->green);
   lut_out[i].blue = lut_limited_range(entry->blue);
  } else {
   lut_out[i] = *entry;
  }
 }

 return blob_out;
}

static void i9xx_load_lut_8(struct intel_crtc *crtc,
       const struct drm_property_blob *blob)
{
 struct intel_display *display = to_intel_display(crtc);
 const struct drm_color_lut *lut;
 enum pipe pipe = crtc->pipe;
 int i;

 if (!blob)
  return;

 lut = blob->data;

 for (i = 0; i < 256; i++)
  intel_de_write_fw(display, PALETTE(display, pipe, i),
      i9xx_lut_8(&lut[i]));
}

static void i9xx_load_lut_10(struct intel_crtc *crtc,
        const struct drm_property_blob *blob)
{
 struct intel_display *display = to_intel_display(crtc);
 const struct drm_color_lut *lut = blob->data;
 int i, lut_size = drm_color_lut_size(blob);
 enum pipe pipe = crtc->pipe;

 for (i = 0; i < lut_size - 1; i++) {
  intel_de_write_fw(display,
      PALETTE(display, pipe, 2 * i + 0),
      i9xx_lut_10_ldw(&lut[i]));
  intel_de_write_fw(display,
      PALETTE(display, pipe, 2 * i + 1),
      i9xx_lut_10_udw(&lut[i]));
 }
}

static void i9xx_load_luts(const struct intel_crtc_state *crtc_state)
{
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
 const struct drm_property_blob *post_csc_lut = crtc_state->post_csc_lut;

 switch (crtc_state->gamma_mode) {
 case GAMMA_MODE_MODE_8BIT:
  i9xx_load_lut_8(crtc, post_csc_lut);
  break;
 case GAMMA_MODE_MODE_10BIT:
  i9xx_load_lut_10(crtc, post_csc_lut);
  break;
 default:
  MISSING_CASE(crtc_state->gamma_mode);
  break;
 }
}

static void i965_load_lut_10p6(struct intel_crtc *crtc,
          const struct drm_property_blob *blob)
{
 struct intel_display *display = to_intel_display(crtc);
 const struct drm_color_lut *lut = blob->data;
 int i, lut_size = drm_color_lut_size(blob);
 enum pipe pipe = crtc->pipe;

 for (i = 0; i < lut_size - 1; i++) {
  intel_de_write_fw(display,
      PALETTE(display, pipe, 2 * i + 0),
      i965_lut_10p6_ldw(&lut[i]));
  intel_de_write_fw(display,
      PALETTE(display, pipe, 2 * i + 1),
      i965_lut_10p6_udw(&lut[i]));
 }

 intel_de_write_fw(display, PIPEGCMAX(display, pipe, 0), lut[i].red);
 intel_de_write_fw(display, PIPEGCMAX(display, pipe, 1), lut[i].green);
 intel_de_write_fw(display, PIPEGCMAX(display, pipe, 2), lut[i].blue);
}

static void i965_load_luts(const struct intel_crtc_state *crtc_state)
{
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
 const struct drm_property_blob *post_csc_lut = crtc_state->post_csc_lut;

 switch (crtc_state->gamma_mode) {
 case GAMMA_MODE_MODE_8BIT:
  i9xx_load_lut_8(crtc, post_csc_lut);
  break;
 case GAMMA_MODE_MODE_10BIT:
  i965_load_lut_10p6(crtc, post_csc_lut);
  break;
 default:
  MISSING_CASE(crtc_state->gamma_mode);
  break;
 }
}

static void ilk_lut_write(const struct intel_crtc_state *crtc_state,
     i915_reg_t reg, u32 val)
{
 struct intel_display *display = to_intel_display(crtc_state);

 if (crtc_state->dsb_color)
  intel_dsb_reg_write(crtc_state->dsb_color, reg, val);
 else
  intel_de_write_fw(display, reg, val);
}

static void ilk_lut_write_indexed(const struct intel_crtc_state *crtc_state,
      i915_reg_t reg, u32 val)
{
 struct intel_display *display = to_intel_display(crtc_state);

 if (crtc_state->dsb_color)
  intel_dsb_reg_write_indexed(crtc_state->dsb_color, reg, val);
 else
  intel_de_write_fw(display, reg, val);
}

static void ilk_load_lut_8(const struct intel_crtc_state *crtc_state,
      const struct drm_property_blob *blob)
{
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
 const struct drm_color_lut *lut;
 enum pipe pipe = crtc->pipe;
 int i;

 if (!blob)
  return;

 lut = blob->data;

 /*
 * DSB fails to correctly load the legacy LUT unless
 * we either write each entry twice when using posted
 * writes, or we use non-posted writes.
 *
 * If palette anti-collision is active during LUT
 * register writes:
 * - posted writes simply get dropped and thus the LUT
 *   contents may not be correctly updated
 * - non-posted writes are blocked and thus the LUT
 *   contents are always correct, but simultaneous CPU
 *   MMIO access will start to fail
 *
 * Choose the lesser of two evils and use posted writes.
 * Using posted writes is also faster, even when having
 * to write each register twice.
 */

 for (i = 0; i < 256; i++) {
  ilk_lut_write(crtc_state, LGC_PALETTE(pipe, i),
         i9xx_lut_8(&lut[i]));
  if (crtc_state->dsb_color)
   ilk_lut_write(crtc_state, LGC_PALETTE(pipe, i),
          i9xx_lut_8(&lut[i]));
 }
}

static void ilk_load_lut_10(const struct intel_crtc_state *crtc_state,
       const struct drm_property_blob *blob)
{
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
 const struct drm_color_lut *lut = blob->data;
 int i, lut_size = drm_color_lut_size(blob);
 enum pipe pipe = crtc->pipe;

 for (i = 0; i < lut_size; i++)
  ilk_lut_write(crtc_state, PREC_PALETTE(pipe, i),
         ilk_lut_10(&lut[i]));
}

static void ilk_load_luts(const struct intel_crtc_state *crtc_state)
{
 const struct drm_property_blob *post_csc_lut = crtc_state->post_csc_lut;
 const struct drm_property_blob *pre_csc_lut = crtc_state->pre_csc_lut;
 const struct drm_property_blob *blob = post_csc_lut ?: pre_csc_lut;

 switch (crtc_state->gamma_mode) {
 case GAMMA_MODE_MODE_8BIT:
  ilk_load_lut_8(crtc_state, blob);
  break;
 case GAMMA_MODE_MODE_10BIT:
  ilk_load_lut_10(crtc_state, blob);
  break;
 default:
  MISSING_CASE(crtc_state->gamma_mode);
  break;
 }
}

static int ivb_lut_10_size(u32 prec_index)
{
 if (prec_index & PAL_PREC_SPLIT_MODE)
  return 512;
 else
  return 1024;
}

/*
 * IVB/HSW Bspec / PAL_PREC_INDEX:
 * "Restriction : Index auto increment mode is not
 *  supported and must not be enabled."
 */

static void ivb_load_lut_10(const struct intel_crtc_state *crtc_state,
       const struct drm_property_blob *blob,
       u32 prec_index)
{
 const struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
 const struct drm_color_lut *lut = blob->data;
 int i, lut_size = drm_color_lut_size(blob);
 enum pipe pipe = crtc->pipe;

 for (i = 0; i < lut_size; i++) {
  ilk_lut_write(crtc_state, PREC_PAL_INDEX(pipe),
         prec_index + i);
  ilk_lut_write(crtc_state, PREC_PAL_DATA(pipe),
         ilk_lut_10(&lut[i]));
 }

 /*
 * Reset the index, otherwise it prevents the legacy palette to be
 * written properly.
 */

 ilk_lut_write(crtc_state, PREC_PAL_INDEX(pipe),
        PAL_PREC_INDEX_VALUE(0));
}

/* On BDW+ the index auto increment mode actually works */
static void bdw_load_lut_10(const struct intel_crtc_state *crtc_state,
       const struct drm_property_blob *blob,
       u32 prec_index)
{
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
 const struct drm_color_lut *lut = blob->data;
 int i, lut_size = drm_color_lut_size(blob);
 enum pipe pipe = crtc->pipe;

 ilk_lut_write(crtc_state, PREC_PAL_INDEX(pipe),
        prec_index);
 ilk_lut_write(crtc_state, PREC_PAL_INDEX(pipe),
        PAL_PREC_AUTO_INCREMENT |
        prec_index);

 for (i = 0; i < lut_size; i++)
  ilk_lut_write_indexed(crtc_state, PREC_PAL_DATA(pipe),
          ilk_lut_10(&lut[i]));

 /*
 * Reset the index, otherwise it prevents the legacy palette to be
 * written properly.
 */

 ilk_lut_write(crtc_state, PREC_PAL_INDEX(pipe),
        PAL_PREC_INDEX_VALUE(0));
}

static void ivb_load_lut_ext_max(const struct intel_crtc_state *crtc_state)
{
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
 enum pipe pipe = crtc->pipe;

 /* Program the max register to clamp values > 1.0. */
 ilk_lut_write(crtc_state, PREC_PAL_EXT_GC_MAX(pipe, 0), 1 << 16);
 ilk_lut_write(crtc_state, PREC_PAL_EXT_GC_MAX(pipe, 1), 1 << 16);
 ilk_lut_write(crtc_state, PREC_PAL_EXT_GC_MAX(pipe, 2), 1 << 16);
}

static void glk_load_lut_ext2_max(const struct intel_crtc_state *crtc_state)
{
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
 enum pipe pipe = crtc->pipe;

 /* Program the max register to clamp values > 1.0. */
 ilk_lut_write(crtc_state, PREC_PAL_EXT2_GC_MAX(pipe, 0), 1 << 16);
 ilk_lut_write(crtc_state, PREC_PAL_EXT2_GC_MAX(pipe, 1), 1 << 16);
 ilk_lut_write(crtc_state, PREC_PAL_EXT2_GC_MAX(pipe, 2), 1 << 16);
}

static void ivb_load_luts(const struct intel_crtc_state *crtc_state)
{
 const struct drm_property_blob *post_csc_lut = crtc_state->post_csc_lut;
 const struct drm_property_blob *pre_csc_lut = crtc_state->pre_csc_lut;
 const struct drm_property_blob *blob = post_csc_lut ?: pre_csc_lut;

 switch (crtc_state->gamma_mode) {
 case GAMMA_MODE_MODE_8BIT:
  ilk_load_lut_8(crtc_state, blob);
  break;
 case GAMMA_MODE_MODE_SPLIT:
  ivb_load_lut_10(crtc_state, pre_csc_lut, PAL_PREC_SPLIT_MODE |
    PAL_PREC_INDEX_VALUE(0));
  ivb_load_lut_ext_max(crtc_state);
  ivb_load_lut_10(crtc_state, post_csc_lut, PAL_PREC_SPLIT_MODE |
    PAL_PREC_INDEX_VALUE(512));
  break;
 case GAMMA_MODE_MODE_10BIT:
  ivb_load_lut_10(crtc_state, blob,
    PAL_PREC_INDEX_VALUE(0));
  ivb_load_lut_ext_max(crtc_state);
  break;
 default:
  MISSING_CASE(crtc_state->gamma_mode);
  break;
 }
}

static void bdw_load_luts(const struct intel_crtc_state *crtc_state)
{
 const struct drm_property_blob *post_csc_lut = crtc_state->post_csc_lut;
 const struct drm_property_blob *pre_csc_lut = crtc_state->pre_csc_lut;
 const struct drm_property_blob *blob = post_csc_lut ?: pre_csc_lut;

 switch (crtc_state->gamma_mode) {
 case GAMMA_MODE_MODE_8BIT:
  ilk_load_lut_8(crtc_state, blob);
  break;
 case GAMMA_MODE_MODE_SPLIT:
  bdw_load_lut_10(crtc_state, pre_csc_lut, PAL_PREC_SPLIT_MODE |
    PAL_PREC_INDEX_VALUE(0));
  ivb_load_lut_ext_max(crtc_state);
  bdw_load_lut_10(crtc_state, post_csc_lut, PAL_PREC_SPLIT_MODE |
    PAL_PREC_INDEX_VALUE(512));
  break;
 case GAMMA_MODE_MODE_10BIT:
  bdw_load_lut_10(crtc_state, blob,
    PAL_PREC_INDEX_VALUE(0));
  ivb_load_lut_ext_max(crtc_state);
  break;
 default:
  MISSING_CASE(crtc_state->gamma_mode);
  break;
 }
}

static int glk_degamma_lut_size(struct intel_display *display)
{
 if (DISPLAY_VER(display) >= 13)
  return 131;
 else
  return 35;
}

static u32 glk_degamma_lut(const struct drm_color_lut *color)
{
 return color->green;
}

static void glk_degamma_lut_pack(struct drm_color_lut *entry, u32 val)
{
 /* PRE_CSC_GAMC_DATA is 3.16, clamp to 0.16 */
 entry->red = entry->green = entry->blue = min(val, 0xffffu);
}

static u32 mtl_degamma_lut(const struct drm_color_lut *color)
{
 return drm_color_lut_extract(color->green, 24);
}

static void mtl_degamma_lut_pack(struct drm_color_lut *entry, u32 val)
{
 /* PRE_CSC_GAMC_DATA is 3.24, clamp to 0.16 */
 entry->red = entry->green = entry->blue =
  intel_color_lut_pack(min(val, 0xffffffu), 24);
}

static void glk_load_degamma_lut(const struct intel_crtc_state *crtc_state,
     const struct drm_property_blob *blob)
{
 struct intel_display *display = to_intel_display(crtc_state);
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
 const struct drm_color_lut *lut = blob->data;
 int i, lut_size = drm_color_lut_size(blob);
 enum pipe pipe = crtc->pipe;

 /*
 * When setting the auto-increment bit, the hardware seems to
 * ignore the index bits, so we need to reset it to index 0
 * separately.
 */

 ilk_lut_write(crtc_state, PRE_CSC_GAMC_INDEX(pipe),
        PRE_CSC_GAMC_INDEX_VALUE(0));
 ilk_lut_write(crtc_state, PRE_CSC_GAMC_INDEX(pipe),
        PRE_CSC_GAMC_AUTO_INCREMENT |
        PRE_CSC_GAMC_INDEX_VALUE(0));

 for (i = 0; i < lut_size; i++) {
  /*
 * First lut_size entries represent range from 0 to 1.0
 * 3 additional lut entries will represent extended range
 * inputs 3.0 and 7.0 respectively, currently clamped
 * at 1.0. Since the precision is 16bit, the user
 * value can be directly filled to register.
 * The pipe degamma table in GLK+ onwards doesn't
 * support different values per channel, so this just
 * programs green value which will be equal to Red and
 * Blue into the lut registers.
 * ToDo: Extend to max 7.0. Enable 32 bit input value
 * as compared to just 16 to achieve this.
 */

  ilk_lut_write_indexed(crtc_state, PRE_CSC_GAMC_DATA(pipe),
          DISPLAY_VER(display) >= 14 ?
          mtl_degamma_lut(&lut[i]) : glk_degamma_lut(&lut[i]));
 }

 /* Clamp values > 1.0. */
 while (i++ < glk_degamma_lut_size(display))
  ilk_lut_write_indexed(crtc_state, PRE_CSC_GAMC_DATA(pipe),
          DISPLAY_VER(display) >= 14 ?
          1 << 24 : 1 << 16);

 ilk_lut_write(crtc_state, PRE_CSC_GAMC_INDEX(pipe), 0);
}

static void glk_load_luts(const struct intel_crtc_state *crtc_state)
{
 const struct drm_property_blob *pre_csc_lut = crtc_state->pre_csc_lut;
 const struct drm_property_blob *post_csc_lut = crtc_state->post_csc_lut;

 if (pre_csc_lut)
  glk_load_degamma_lut(crtc_state, pre_csc_lut);

 switch (crtc_state->gamma_mode) {
 case GAMMA_MODE_MODE_8BIT:
  ilk_load_lut_8(crtc_state, post_csc_lut);
  break;
 case GAMMA_MODE_MODE_10BIT:
  bdw_load_lut_10(crtc_state, post_csc_lut, PAL_PREC_INDEX_VALUE(0));
  ivb_load_lut_ext_max(crtc_state);
  glk_load_lut_ext2_max(crtc_state);
  break;
 default:
  MISSING_CASE(crtc_state->gamma_mode);
  break;
 }
}

static void
ivb_load_lut_max(const struct intel_crtc_state *crtc_state,
   const struct drm_color_lut *color)
{
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
 enum pipe pipe = crtc->pipe;

 /* FIXME LUT entries are 16 bit only, so we can prog 0xFFFF max */
 ilk_lut_write(crtc_state, PREC_PAL_GC_MAX(pipe, 0), color->red);
 ilk_lut_write(crtc_state, PREC_PAL_GC_MAX(pipe, 1), color->green);
 ilk_lut_write(crtc_state, PREC_PAL_GC_MAX(pipe, 2), color->blue);
}

static void
icl_program_gamma_superfine_segment(const struct intel_crtc_state *crtc_state)
{
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
 const struct drm_property_blob *blob = crtc_state->post_csc_lut;
 const struct drm_color_lut *lut = blob->data;
 enum pipe pipe = crtc->pipe;
 int i;

 /*
 * Program Super Fine segment (let's call it seg1)...
 *
 * Super Fine segment's step is 1/(8 * 128 * 256) and it has
 * 9 entries, corresponding to values 0, 1/(8 * 128 * 256),
 * 2/(8 * 128 * 256) ... 8/(8 * 128 * 256).
 */

 ilk_lut_write(crtc_state, PREC_PAL_MULTI_SEG_INDEX(pipe),
        PAL_PREC_MULTI_SEG_INDEX_VALUE(0));
 ilk_lut_write(crtc_state, PREC_PAL_MULTI_SEG_INDEX(pipe),
        PAL_PREC_AUTO_INCREMENT |
        PAL_PREC_MULTI_SEG_INDEX_VALUE(0));

 for (i = 0; i < 9; i++) {
  const struct drm_color_lut *entry = &lut[i];

  ilk_lut_write_indexed(crtc_state, PREC_PAL_MULTI_SEG_DATA(pipe),
          ilk_lut_12p4_ldw(entry));
  ilk_lut_write_indexed(crtc_state, PREC_PAL_MULTI_SEG_DATA(pipe),
          ilk_lut_12p4_udw(entry));
 }

 ilk_lut_write(crtc_state, PREC_PAL_MULTI_SEG_INDEX(pipe),
        PAL_PREC_MULTI_SEG_INDEX_VALUE(0));
}

static void
icl_program_gamma_multi_segment(const struct intel_crtc_state *crtc_state)
{
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
 const struct drm_property_blob *blob = crtc_state->post_csc_lut;
 const struct drm_color_lut *lut = blob->data;
 const struct drm_color_lut *entry;
 enum pipe pipe = crtc->pipe;
 int i;

 /*
 * Program Fine segment (let's call it seg2)...
 *
 * Fine segment's step is 1/(128 * 256) i.e. 1/(128 * 256), 2/(128 * 256)
 * ... 256/(128 * 256). So in order to program fine segment of LUT we
 * need to pick every 8th entry in the LUT, and program 256 indexes.
 *
 * PAL_PREC_INDEX[0] and PAL_PREC_INDEX[1] map to seg2[1],
 * seg2[0] being unused by the hardware.
 */

 ilk_lut_write(crtc_state, PREC_PAL_INDEX(pipe),
        PAL_PREC_INDEX_VALUE(0));
 ilk_lut_write(crtc_state, PREC_PAL_INDEX(pipe),
        PAL_PREC_AUTO_INCREMENT |
        PAL_PREC_INDEX_VALUE(0));

 for (i = 1; i < 257; i++) {
  entry = &lut[i * 8];

  ilk_lut_write_indexed(crtc_state, PREC_PAL_DATA(pipe),
          ilk_lut_12p4_ldw(entry));
  ilk_lut_write_indexed(crtc_state, PREC_PAL_DATA(pipe),
          ilk_lut_12p4_udw(entry));
 }

 /*
 * Program Coarse segment (let's call it seg3)...
 *
 * Coarse segment starts from index 0 and it's step is 1/256 ie 0,
 * 1/256, 2/256 ... 256/256. As per the description of each entry in LUT
 * above, we need to pick every (8 * 128)th entry in LUT, and
 * program 256 of those.
 *
 * Spec is not very clear about if entries seg3[0] and seg3[1] are
 * being used or not, but we still need to program these to advance
 * the index.
 */

 for (i = 0; i < 256; i++) {
  entry = &lut[i * 8 * 128];

  ilk_lut_write_indexed(crtc_state, PREC_PAL_DATA(pipe),
          ilk_lut_12p4_ldw(entry));
  ilk_lut_write_indexed(crtc_state, PREC_PAL_DATA(pipe),
          ilk_lut_12p4_udw(entry));
 }

 ilk_lut_write(crtc_state, PREC_PAL_INDEX(pipe),
        PAL_PREC_INDEX_VALUE(0));

 /* The last entry in the LUT is to be programmed in GCMAX */
 entry = &lut[256 * 8 * 128];
 ivb_load_lut_max(crtc_state, entry);
}

static void icl_load_luts(const struct intel_crtc_state *crtc_state)
{
 const struct drm_property_blob *pre_csc_lut = crtc_state->pre_csc_lut;
 const struct drm_property_blob *post_csc_lut = crtc_state->post_csc_lut;

 if (pre_csc_lut)
  glk_load_degamma_lut(crtc_state, pre_csc_lut);

 switch (crtc_state->gamma_mode & GAMMA_MODE_MODE_MASK) {
 case GAMMA_MODE_MODE_8BIT:
  ilk_load_lut_8(crtc_state, post_csc_lut);
  break;
 case GAMMA_MODE_MODE_12BIT_MULTI_SEG:
  icl_program_gamma_superfine_segment(crtc_state);
  icl_program_gamma_multi_segment(crtc_state);
  ivb_load_lut_ext_max(crtc_state);
  glk_load_lut_ext2_max(crtc_state);
  break;
 case GAMMA_MODE_MODE_10BIT:
  bdw_load_lut_10(crtc_state, post_csc_lut, PAL_PREC_INDEX_VALUE(0));
  ivb_load_lut_ext_max(crtc_state);
  glk_load_lut_ext2_max(crtc_state);
  break;
 default:
  MISSING_CASE(crtc_state->gamma_mode);
  break;
 }
}

static void vlv_load_luts(const struct intel_crtc_state *crtc_state)
{
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);

 if (crtc_state->wgc_enable)
  vlv_load_wgc_csc(crtc, &crtc_state->csc);

 i965_load_luts(crtc_state);
}

static u32 chv_cgm_degamma_ldw(const struct drm_color_lut *color)
{
 return REG_FIELD_PREP(CGM_PIPE_DEGAMMA_GREEN_LDW_MASK, drm_color_lut_extract(color->green, 14)) |
  REG_FIELD_PREP(CGM_PIPE_DEGAMMA_BLUE_LDW_MASK, drm_color_lut_extract(color->blue, 14));
}

static u32 chv_cgm_degamma_udw(const struct drm_color_lut *color)
{
 return REG_FIELD_PREP(CGM_PIPE_DEGAMMA_RED_UDW_MASK, drm_color_lut_extract(color->red, 14));
}

static void chv_cgm_degamma_pack(struct drm_color_lut *entry, u32 ldw, u32 udw)
{
 entry->green = intel_color_lut_pack(REG_FIELD_GET(CGM_PIPE_DEGAMMA_GREEN_LDW_MASK, ldw), 14);
 entry->blue = intel_color_lut_pack(REG_FIELD_GET(CGM_PIPE_DEGAMMA_BLUE_LDW_MASK, ldw), 14);
 entry->red = intel_color_lut_pack(REG_FIELD_GET(CGM_PIPE_DEGAMMA_RED_UDW_MASK, udw), 14);
}

static void chv_load_cgm_degamma(struct intel_crtc *crtc,
     const struct drm_property_blob *blob)
{
 struct intel_display *display = to_intel_display(crtc);
 const struct drm_color_lut *lut = blob->data;
 int i, lut_size = drm_color_lut_size(blob);
 enum pipe pipe = crtc->pipe;

 for (i = 0; i < lut_size; i++) {
  intel_de_write_fw(display, CGM_PIPE_DEGAMMA(pipe, i, 0),
      chv_cgm_degamma_ldw(&lut[i]));
  intel_de_write_fw(display, CGM_PIPE_DEGAMMA(pipe, i, 1),
      chv_cgm_degamma_udw(&lut[i]));
 }
}

static u32 chv_cgm_gamma_ldw(const struct drm_color_lut *color)
{
 return REG_FIELD_PREP(CGM_PIPE_GAMMA_GREEN_LDW_MASK, drm_color_lut_extract(color->green, 10)) |
  REG_FIELD_PREP(CGM_PIPE_GAMMA_BLUE_LDW_MASK, drm_color_lut_extract(color->blue, 10));
}

static u32 chv_cgm_gamma_udw(const struct drm_color_lut *color)
{
 return REG_FIELD_PREP(CGM_PIPE_GAMMA_RED_UDW_MASK, drm_color_lut_extract(color->red, 10));
}

static void chv_cgm_gamma_pack(struct drm_color_lut *entry, u32 ldw, u32 udw)
{
 entry->green = intel_color_lut_pack(REG_FIELD_GET(CGM_PIPE_GAMMA_GREEN_LDW_MASK, ldw), 10);
 entry->blue = intel_color_lut_pack(REG_FIELD_GET(CGM_PIPE_GAMMA_BLUE_LDW_MASK, ldw), 10);
 entry->red = intel_color_lut_pack(REG_FIELD_GET(CGM_PIPE_GAMMA_RED_UDW_MASK, udw), 10);
}

static void chv_load_cgm_gamma(struct intel_crtc *crtc,
          const struct drm_property_blob *blob)
{
 struct intel_display *display = to_intel_display(crtc);
 const struct drm_color_lut *lut = blob->data;
 int i, lut_size = drm_color_lut_size(blob);
 enum pipe pipe = crtc->pipe;

 for (i = 0; i < lut_size; i++) {
  intel_de_write_fw(display, CGM_PIPE_GAMMA(pipe, i, 0),
      chv_cgm_gamma_ldw(&lut[i]));
  intel_de_write_fw(display, CGM_PIPE_GAMMA(pipe, i, 1),
      chv_cgm_gamma_udw(&lut[i]));
 }
}

static void chv_load_luts(const struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(crtc_state);
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
 const struct drm_property_blob *pre_csc_lut = crtc_state->pre_csc_lut;
 const struct drm_property_blob *post_csc_lut = crtc_state->post_csc_lut;

 if (crtc_state->cgm_mode & CGM_PIPE_MODE_CSC)
  chv_load_cgm_csc(crtc, &crtc_state->csc);

 if (crtc_state->cgm_mode & CGM_PIPE_MODE_DEGAMMA)
  chv_load_cgm_degamma(crtc, pre_csc_lut);

 if (crtc_state->cgm_mode & CGM_PIPE_MODE_GAMMA)
  chv_load_cgm_gamma(crtc, post_csc_lut);
 else
  i965_load_luts(crtc_state);

 intel_de_write_fw(display, CGM_PIPE_MODE(crtc->pipe),
     crtc_state->cgm_mode);
}

void intel_color_load_luts(const struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(crtc_state);

 if (crtc_state->dsb_color)
  return;

 display->funcs.color->load_luts(crtc_state);
}

void intel_color_commit_noarm(struct intel_dsb *dsb,
         const struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(crtc_state);

 if (display->funcs.color->color_commit_noarm)
  display->funcs.color->color_commit_noarm(dsb, crtc_state);
}

void intel_color_commit_arm(struct intel_dsb *dsb,
       const struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(crtc_state);

 display->funcs.color->color_commit_arm(dsb, crtc_state);
}

void intel_color_post_update(const struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(crtc_state);

 if (display->funcs.color->color_post_update)
  display->funcs.color->color_post_update(crtc_state);
}

void intel_color_modeset(const struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(crtc_state);

 intel_color_load_luts(crtc_state);
 intel_color_commit_noarm(NULL, crtc_state);
 intel_color_commit_arm(NULL, crtc_state);

 if (DISPLAY_VER(display) < 9) {
  struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
  struct intel_plane *plane = to_intel_plane(crtc->base.primary);

  /* update DSPCNTR to configure gamma/csc for pipe bottom color */
  plane->disable_arm(NULL, plane, crtc_state);
 }
}

bool intel_color_uses_dsb(const struct intel_crtc_state *crtc_state)
{
 return crtc_state->dsb_color;
}

bool intel_color_uses_chained_dsb(const struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(crtc_state);

 return crtc_state->dsb_color && !HAS_DOUBLE_BUFFERED_LUT(display);
}

bool intel_color_uses_gosub_dsb(const struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(crtc_state);

 return crtc_state->dsb_color && HAS_DOUBLE_BUFFERED_LUT(display);
}

void intel_color_prepare_commit(struct intel_atomic_state *state,
    struct intel_crtc *crtc)
{
 struct intel_display *display = to_intel_display(state);
 struct intel_crtc_state *crtc_state =
  intel_atomic_get_new_crtc_state(state, crtc);

 if (!crtc_state->hw.active ||
     intel_crtc_needs_modeset(crtc_state))
  return;

 if (!intel_crtc_needs_color_update(crtc_state))
  return;

 if (!crtc_state->pre_csc_lut && !crtc_state->post_csc_lut)
  return;

 if (HAS_DOUBLE_BUFFERED_LUT(display))
  crtc_state->dsb_color = intel_dsb_prepare(state, crtc, INTEL_DSB_0, 1024);
 else
  crtc_state->dsb_color = intel_dsb_prepare(state, crtc, INTEL_DSB_1, 1024);

 if (!intel_color_uses_dsb(crtc_state))
  return;

 display->funcs.color->load_luts(crtc_state);

 if (crtc_state->use_dsb && intel_color_uses_chained_dsb(crtc_state)) {
  intel_vrr_send_push(crtc_state->dsb_color, crtc_state);
  intel_dsb_wait_vblank_delay(state, crtc_state->dsb_color);
  intel_vrr_check_push_sent(crtc_state->dsb_color, crtc_state);
  intel_dsb_interrupt(crtc_state->dsb_color);
 }

 if (intel_color_uses_gosub_dsb(crtc_state))
  intel_dsb_gosub_finish(crtc_state->dsb_color);
 else
  intel_dsb_finish(crtc_state->dsb_color);
}

void intel_color_cleanup_commit(struct intel_crtc_state *crtc_state)
{
 if (crtc_state->dsb_color) {
  intel_dsb_cleanup(crtc_state->dsb_color);
  crtc_state->dsb_color = NULL;
 }
}

void intel_color_wait_commit(const struct intel_crtc_state *crtc_state)
{
 if (crtc_state->dsb_color)
  intel_dsb_wait(crtc_state->dsb_color);
}

static bool intel_can_preload_luts(struct intel_atomic_state *state,
       struct intel_crtc *crtc)
{
 struct intel_display *display = to_intel_display(state);
 const struct intel_crtc_state *old_crtc_state =
  intel_atomic_get_old_crtc_state(state, crtc);

 if (HAS_DOUBLE_BUFFERED_LUT(display))
  return false;

 return !old_crtc_state->post_csc_lut &&
  !old_crtc_state->pre_csc_lut;
}

static bool vlv_can_preload_luts(struct intel_atomic_state *state,
     struct intel_crtc *crtc)
{
 const struct intel_crtc_state *old_crtc_state =
  intel_atomic_get_old_crtc_state(state, crtc);

 return !old_crtc_state->wgc_enable &&
  !old_crtc_state->post_csc_lut;
}

static bool chv_can_preload_luts(struct intel_atomic_state *state,
     struct intel_crtc *crtc)
{
 const struct intel_crtc_state *old_crtc_state =
  intel_atomic_get_old_crtc_state(state, crtc);
 const struct intel_crtc_state *new_crtc_state =
  intel_atomic_get_new_crtc_state(state, crtc);

 /*
 * CGM_PIPE_MODE is itself single buffered. We'd have to
 * somehow split it out from chv_load_luts() if we wanted
 * the ability to preload the CGM LUTs/CSC without tearing.
 */

 if (old_crtc_state->cgm_mode || new_crtc_state->cgm_mode)
  return false;

 return vlv_can_preload_luts(state, crtc);
}

int intel_color_check(struct intel_atomic_state *state,
        struct intel_crtc *crtc)
{
 struct intel_display *display = to_intel_display(state);
 const struct intel_crtc_state *old_crtc_state =
  intel_atomic_get_old_crtc_state(state, crtc);
 struct intel_crtc_state *new_crtc_state =
  intel_atomic_get_new_crtc_state(state, crtc);

 /*
 * May need to update pipe gamma enable bits
 * when C8 planes are getting enabled/disabled.
 */

 if (!old_crtc_state->c8_planes != !new_crtc_state->c8_planes)
  new_crtc_state->uapi.color_mgmt_changed = true;

 if (!intel_crtc_needs_color_update(new_crtc_state))
  return 0;

 return display->funcs.color->color_check(state, crtc);
}

void intel_color_get_config(struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(crtc_state);

 display->funcs.color->get_config(crtc_state);

 display->funcs.color->read_luts(crtc_state);

 if (display->funcs.color->read_csc)
  display->funcs.color->read_csc(crtc_state);
}

bool intel_color_lut_equal(const struct intel_crtc_state *crtc_state,
      const struct drm_property_blob *blob1,
      const struct drm_property_blob *blob2,
      bool is_pre_csc_lut)
{
 struct intel_display *display = to_intel_display(crtc_state);

 /*
 * FIXME c8_planes readout missing thus
 * .read_luts() doesn't read out post_csc_lut.
 */

 if (!is_pre_csc_lut && crtc_state->c8_planes)
  return true;

 return display->funcs.color->lut_equal(crtc_state, blob1, blob2,
            is_pre_csc_lut);
}

static bool need_plane_update(struct intel_plane *plane,
         const struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(plane);

 /*
 * On pre-SKL the pipe gamma enable and pipe csc enable for
 * the pipe bottom color are configured via the primary plane.
 * We have to reconfigure that even if the plane is inactive.
 */

 return crtc_state->active_planes & BIT(plane->id) ||
  (DISPLAY_VER(display) < 9 && plane->id == PLANE_PRIMARY);
}

static int
intel_color_add_affected_planes(struct intel_atomic_state *state,
    struct intel_crtc *crtc)
{
 struct intel_display *display = to_intel_display(state);
 const struct intel_crtc_state *old_crtc_state =
  intel_atomic_get_old_crtc_state(state, crtc);
 struct intel_crtc_state *new_crtc_state =
  intel_atomic_get_new_crtc_state(state, crtc);
 struct intel_plane *plane;

 if (!new_crtc_state->hw.active ||
     intel_crtc_needs_modeset(new_crtc_state))
  return 0;

 if (new_crtc_state->gamma_enable == old_crtc_state->gamma_enable &&
     new_crtc_state->csc_enable == old_crtc_state->csc_enable)
  return 0;

 for_each_intel_plane_on_crtc(display->drm, crtc, plane) {
  struct intel_plane_state *plane_state;

  if (!need_plane_update(plane, new_crtc_state))
   continue;

  plane_state = intel_atomic_get_plane_state(state, plane);
  if (IS_ERR(plane_state))
   return PTR_ERR(plane_state);

  new_crtc_state->update_planes |= BIT(plane->id);
  new_crtc_state->async_flip_planes = 0;
  new_crtc_state->do_async_flip = false;

  /* plane control register changes blocked by CxSR */
  if (HAS_GMCH(display))
   new_crtc_state->disable_cxsr = true;
 }

 return 0;
}

static u32 intel_gamma_lut_tests(const struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(crtc_state);
 const struct drm_property_blob *gamma_lut = crtc_state->hw.gamma_lut;

 if (lut_is_legacy(gamma_lut))
  return 0;

 return DISPLAY_INFO(display)->color.gamma_lut_tests;
}

static u32 intel_degamma_lut_tests(const struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(crtc_state);

 return DISPLAY_INFO(display)->color.degamma_lut_tests;
}

static int intel_gamma_lut_size(const struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(crtc_state);
 const struct drm_property_blob *gamma_lut = crtc_state->hw.gamma_lut;

 if (lut_is_legacy(gamma_lut))
  return LEGACY_LUT_LENGTH;

 return DISPLAY_INFO(display)->color.gamma_lut_size;
}

static u32 intel_degamma_lut_size(const struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(crtc_state);

 return DISPLAY_INFO(display)->color.degamma_lut_size;
}

static int check_lut_size(struct intel_crtc *crtc, const char *lut_name,
     const struct drm_property_blob *lut, int expected)
{
 struct intel_display *display = to_intel_display(crtc);
 int len;

 if (!lut)
  return 0;

 len = drm_color_lut_size(lut);
 if (len != expected) {
  drm_dbg_kms(display->drm,
       "[CRTC:%d:%s] Invalid %s LUT size; got %d, expected %d\n",
       crtc->base.base.id, crtc->base.name, lut_name, len, expected);
  return -EINVAL;
 }

 return 0;
}

static int _check_luts(const struct intel_crtc_state *crtc_state,
         u32 degamma_tests, u32 gamma_tests)
{
 struct intel_display *display = to_intel_display(crtc_state);
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
 const struct drm_property_blob *gamma_lut = crtc_state->hw.gamma_lut;
 const struct drm_property_blob *degamma_lut = crtc_state->hw.degamma_lut;
 int gamma_length, degamma_length;

 /* C8 relies on its palette being stored in the legacy LUT */
 if (crtc_state->c8_planes && !lut_is_legacy(crtc_state->hw.gamma_lut)) {
  drm_dbg_kms(display->drm,
       "[CRTC:%d:%s] C8 pixelformat requires the legacy LUT\n",
       crtc->base.base.id, crtc->base.name);
  return -EINVAL;
 }

 degamma_length = intel_degamma_lut_size(crtc_state);
 gamma_length = intel_gamma_lut_size(crtc_state);

 if (check_lut_size(crtc, "degamma", degamma_lut, degamma_length) ||
     check_lut_size(crtc, "gamma", gamma_lut, gamma_length))
  return -EINVAL;

 if (drm_color_lut_check(degamma_lut, degamma_tests) ||
     drm_color_lut_check(gamma_lut, gamma_tests))
  return -EINVAL;

 return 0;
}

static int check_luts(const struct intel_crtc_state *crtc_state)
{
 return _check_luts(crtc_state,
      intel_degamma_lut_tests(crtc_state),
      intel_gamma_lut_tests(crtc_state));
}

static u32 i9xx_gamma_mode(struct intel_crtc_state *crtc_state)
{
 if (!crtc_state->gamma_enable ||
     lut_is_legacy(crtc_state->hw.gamma_lut))
  return GAMMA_MODE_MODE_8BIT;
 else
  return GAMMA_MODE_MODE_10BIT;
}

static int i9xx_lut_10_diff(u16 a, u16 b)
{
 return drm_color_lut_extract(a, 10) -
  drm_color_lut_extract(b, 10);
}

static int i9xx_check_lut_10(struct intel_crtc *crtc,
        const struct drm_property_blob *blob)
{
 struct intel_display *display = to_intel_display(crtc);
 const struct drm_color_lut *lut = blob->data;
 int lut_size = drm_color_lut_size(blob);
 const struct drm_color_lut *a = &lut[lut_size - 2];
 const struct drm_color_lut *b = &lut[lut_size - 1];

 if (i9xx_lut_10_diff(b->red, a->red) > 0x7f ||
     i9xx_lut_10_diff(b->green, a->green) > 0x7f ||
     i9xx_lut_10_diff(b->blue, a->blue) > 0x7f) {
  drm_dbg_kms(display->drm,
       "[CRTC:%d:%s] Last gamma LUT entry exceeds max slope\n",
       crtc->base.base.id, crtc->base.name);
  return -EINVAL;
 }

 return 0;
}

void intel_color_assert_luts(const struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(crtc_state);

 /* make sure {pre,post}_csc_lut were correctly assigned */
 if (DISPLAY_VER(display) >= 11 || HAS_GMCH(display)) {
  drm_WARN_ON(display->drm,
       crtc_state->pre_csc_lut != crtc_state->hw.degamma_lut);
  drm_WARN_ON(display->drm,
       crtc_state->post_csc_lut != crtc_state->hw.gamma_lut);
 } else if (DISPLAY_VER(display) == 10) {
  drm_WARN_ON(display->drm,
       crtc_state->post_csc_lut == crtc_state->hw.gamma_lut &&
       crtc_state->pre_csc_lut != crtc_state->hw.degamma_lut &&
       crtc_state->pre_csc_lut != display->color.glk_linear_degamma_lut);
  drm_WARN_ON(display->drm,
       !ilk_lut_limited_range(crtc_state) &&
       crtc_state->post_csc_lut != NULL &&
       crtc_state->post_csc_lut != crtc_state->hw.gamma_lut);
 } else if (crtc_state->gamma_mode != GAMMA_MODE_MODE_SPLIT) {
  drm_WARN_ON(display->drm,
       crtc_state->pre_csc_lut != crtc_state->hw.degamma_lut &&
       crtc_state->pre_csc_lut != crtc_state->hw.gamma_lut);
  drm_WARN_ON(display->drm,
       !ilk_lut_limited_range(crtc_state) &&
       crtc_state->post_csc_lut != crtc_state->hw.degamma_lut &&
       crtc_state->post_csc_lut != crtc_state->hw.gamma_lut);
 }
}

static void intel_assign_luts(struct intel_crtc_state *crtc_state)
{
 drm_property_replace_blob(&crtc_state->pre_csc_lut,
      crtc_state->hw.degamma_lut);
 drm_property_replace_blob(&crtc_state->post_csc_lut,
      crtc_state->hw.gamma_lut);
}

static int i9xx_color_check(struct intel_atomic_state *state,
       struct intel_crtc *crtc)
{
 struct intel_display *display = to_intel_display(state);
 struct intel_crtc_state *crtc_state =
  intel_atomic_get_new_crtc_state(state, crtc);
 int ret;

 ret = check_luts(crtc_state);
 if (ret)
  return ret;

 crtc_state->gamma_enable =
  crtc_state->hw.gamma_lut &&
  !crtc_state->c8_planes;

 crtc_state->gamma_mode = i9xx_gamma_mode(crtc_state);

 if (DISPLAY_VER(display) < 4 &&
     crtc_state->gamma_mode == GAMMA_MODE_MODE_10BIT) {
  ret = i9xx_check_lut_10(crtc, crtc_state->hw.gamma_lut);
  if (ret)
   return ret;
 }

 ret = intel_color_add_affected_planes(state, crtc);
 if (ret)
  return ret;

 intel_assign_luts(crtc_state);

 crtc_state->preload_luts = intel_can_preload_luts(state, crtc);

 return 0;
}

/*
 * VLV color pipeline:
 * u0.10 -> WGC csc -> u0.10 -> pipe gamma -> u0.10
 */

static int vlv_color_check(struct intel_atomic_state *state,
      struct intel_crtc *crtc)
{
 struct intel_crtc_state *crtc_state =
  intel_atomic_get_new_crtc_state(state, crtc);
 int ret;

 ret = check_luts(crtc_state);
 if (ret)
  return ret;

 crtc_state->gamma_enable =
  crtc_state->hw.gamma_lut &&
  !crtc_state->c8_planes;

 crtc_state->gamma_mode = i9xx_gamma_mode(crtc_state);

 crtc_state->wgc_enable = crtc_state->hw.ctm;

 ret = intel_color_add_affected_planes(state, crtc);
 if (ret)
  return ret;

 intel_assign_luts(crtc_state);

 vlv_assign_csc(crtc_state);

 crtc_state->preload_luts = vlv_can_preload_luts(state, crtc);

 return 0;
}

static u32 chv_cgm_mode(const struct intel_crtc_state *crtc_state)
{
 u32 cgm_mode = 0;

 if (crtc_state->hw.degamma_lut)
  cgm_mode |= CGM_PIPE_MODE_DEGAMMA;
 if (crtc_state->hw.ctm)
  cgm_mode |= CGM_PIPE_MODE_CSC;
 if (crtc_state->hw.gamma_lut &&
     !lut_is_legacy(crtc_state->hw.gamma_lut))
  cgm_mode |= CGM_PIPE_MODE_GAMMA;

 /*
 * Toggling the CGM CSC on/off outside of the tiny window
 * between start of vblank and frame start causes underruns.
 * Always enable the CGM CSC as a workaround.
 */

 cgm_mode |= CGM_PIPE_MODE_CSC;

 return cgm_mode;
}

/*
 * CHV color pipeline:
 * u0.10 -> CGM degamma -> u0.14 -> CGM csc -> u0.14 -> CGM gamma ->
 * u0.10 -> WGC csc -> u0.10 -> pipe gamma -> u0.10
 *
 * We always bypass the WGC csc and use the CGM csc
 * instead since it has degamma and better precision.
 */

static int chv_color_check(struct intel_atomic_state *state,
      struct intel_crtc *crtc)
{
 struct intel_crtc_state *crtc_state =
  intel_atomic_get_new_crtc_state(state, crtc);
 int ret;

 ret = check_luts(crtc_state);
 if (ret)
  return ret;

 /*
 * Pipe gamma will be used only for the legacy LUT.
 * Otherwise we bypass it and use the CGM gamma instead.
 */

 crtc_state->gamma_enable =
  lut_is_legacy(crtc_state->hw.gamma_lut) &&
  !crtc_state->c8_planes;

 crtc_state->gamma_mode = GAMMA_MODE_MODE_8BIT;

 crtc_state->cgm_mode = chv_cgm_mode(crtc_state);

 /*
 * We always bypass the WGC CSC and use the CGM CSC
 * instead since it has degamma and better precision.
 */

 crtc_state->wgc_enable = false;

 ret = intel_color_add_affected_planes(state, crtc);
 if (ret)
  return ret;

 intel_assign_luts(crtc_state);

 chv_assign_csc(crtc_state);

 crtc_state->preload_luts = chv_can_preload_luts(state, crtc);

 return 0;
}

static bool ilk_gamma_enable(const struct intel_crtc_state *crtc_state)
{
 return (crtc_state->hw.gamma_lut ||
  crtc_state->hw.degamma_lut) &&
  !crtc_state->c8_planes;
}

static bool ilk_csc_enable(const struct intel_crtc_state *crtc_state)
{
 return crtc_state->output_format != INTEL_OUTPUT_FORMAT_RGB ||
  ilk_csc_limited_range(crtc_state) ||
  crtc_state->hw.ctm;
}

static u32 ilk_gamma_mode(const struct intel_crtc_state *crtc_state)
{
 if (!crtc_state->gamma_enable ||
     lut_is_legacy(crtc_state->hw.gamma_lut))
  return GAMMA_MODE_MODE_8BIT;
 else
  return GAMMA_MODE_MODE_10BIT;
}

static u32 ilk_csc_mode(const struct intel_crtc_state *crtc_state)
{
 /*
 * CSC comes after the LUT in RGB->YCbCr mode.
 * RGB->YCbCr needs the limited range offsets added to
 * the output. RGB limited range output is handled by
 * the hw automagically elsewhere.
 */

 if (crtc_state->output_format != INTEL_OUTPUT_FORMAT_RGB)
  return CSC_BLACK_SCREEN_OFFSET;

 if (crtc_state->hw.degamma_lut)
  return CSC_MODE_YUV_TO_RGB;

 return CSC_MODE_YUV_TO_RGB |
  CSC_POSITION_BEFORE_GAMMA;
}

static int ilk_assign_luts(struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(crtc_state);

 if (ilk_lut_limited_range(crtc_state)) {
  struct drm_property_blob *gamma_lut;

  gamma_lut = create_resized_lut(display, crtc_state->hw.gamma_lut,
            drm_color_lut_size(crtc_state->hw.gamma_lut),
            true);
  if (IS_ERR(gamma_lut))
   return PTR_ERR(gamma_lut);

  drm_property_replace_blob(&crtc_state->post_csc_lut, gamma_lut);

  drm_property_blob_put(gamma_lut);

  drm_property_replace_blob(&crtc_state->pre_csc_lut, crtc_state->hw.degamma_lut);

  return 0;
 }

 if (crtc_state->hw.degamma_lut ||
     crtc_state->csc_mode & CSC_POSITION_BEFORE_GAMMA) {
  drm_property_replace_blob(&crtc_state->pre_csc_lut,
       crtc_state->hw.degamma_lut);
  drm_property_replace_blob(&crtc_state->post_csc_lut,
       crtc_state->hw.gamma_lut);
 } else {
  drm_property_replace_blob(&crtc_state->pre_csc_lut,
       crtc_state->hw.gamma_lut);
  drm_property_replace_blob(&crtc_state->post_csc_lut,
       NULL);
 }

 return 0;
}

static int ilk_color_check(struct intel_atomic_state *state,
      struct intel_crtc *crtc)
{
 struct intel_display *display = to_intel_display(state);
 struct intel_crtc_state *crtc_state =
  intel_atomic_get_new_crtc_state(state, crtc);
 int ret;

 ret = check_luts(crtc_state);
 if (ret)
  return ret;

 if (crtc_state->hw.degamma_lut && crtc_state->hw.gamma_lut) {
  drm_dbg_kms(display->drm,
       "[CRTC:%d:%s] Degamma and gamma together are not possible\n",
       crtc->base.base.id, crtc->base.name);
  return -EINVAL;
 }

 if (crtc_state->output_format != INTEL_OUTPUT_FORMAT_RGB &&
     crtc_state->hw.ctm) {
  drm_dbg_kms(display->drm,
       "[CRTC:%d:%s] YCbCr and CTM together are not possible\n",
       crtc->base.base.id, crtc->base.name);
  return -EINVAL;
 }

 crtc_state->gamma_enable = ilk_gamma_enable(crtc_state);

 crtc_state->csc_enable = ilk_csc_enable(crtc_state);

 crtc_state->gamma_mode = ilk_gamma_mode(crtc_state);

 crtc_state->csc_mode = ilk_csc_mode(crtc_state);

 ret = intel_color_add_affected_planes(state, crtc);
 if (ret)
  return ret;

 ret = ilk_assign_luts(crtc_state);
 if (ret)
  return ret;

 ilk_assign_csc(crtc_state);

 crtc_state->preload_luts = intel_can_preload_luts(state, crtc);

 return 0;
}

static u32 ivb_gamma_mode(const struct intel_crtc_state *crtc_state)
{
 if (crtc_state->hw.degamma_lut && crtc_state->hw.gamma_lut)
  return GAMMA_MODE_MODE_SPLIT;

 return ilk_gamma_mode(crtc_state);
}

static u32 ivb_csc_mode(const struct intel_crtc_state *crtc_state)
{
 bool limited_color_range = ilk_csc_limited_range(crtc_state);

 /*
 * CSC comes after the LUT in degamma, RGB->YCbCr,
 * and RGB full->limited range mode.
 */

 if (crtc_state->hw.degamma_lut ||
     crtc_state->output_format != INTEL_OUTPUT_FORMAT_RGB ||
     limited_color_range)
  return 0;

 return CSC_POSITION_BEFORE_GAMMA;
}

static int ivb_assign_luts(struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(crtc_state);
 struct drm_property_blob *degamma_lut, *gamma_lut;

 if (crtc_state->gamma_mode != GAMMA_MODE_MODE_SPLIT)
  return ilk_assign_luts(crtc_state);

 drm_WARN_ON(display->drm, drm_color_lut_size(crtc_state->hw.degamma_lut) != 1024);
 drm_WARN_ON(display->drm, drm_color_lut_size(crtc_state->hw.gamma_lut) != 1024);

 degamma_lut = create_resized_lut(display, crtc_state->hw.degamma_lut, 512,
      false);
 if (IS_ERR(degamma_lut))
  return PTR_ERR(degamma_lut);

 gamma_lut = create_resized_lut(display, crtc_state->hw.gamma_lut, 512,
           ilk_lut_limited_range(crtc_state));
 if (IS_ERR(gamma_lut)) {
  drm_property_blob_put(degamma_lut);
  return PTR_ERR(gamma_lut);
 }

 drm_property_replace_blob(&crtc_state->pre_csc_lut, degamma_lut);
 drm_property_replace_blob(&crtc_state->post_csc_lut, gamma_lut);

 drm_property_blob_put(degamma_lut);
 drm_property_blob_put(gamma_lut);

 return 0;
}

static int ivb_color_check(struct intel_atomic_state *state,
      struct intel_crtc *crtc)
{
 struct intel_display *display = to_intel_display(state);
 struct intel_crtc_state *crtc_state =
  intel_atomic_get_new_crtc_state(state, crtc);
 int ret;

 ret = check_luts(crtc_state);
 if (ret)
  return ret;

 if (crtc_state->c8_planes && crtc_state->hw.degamma_lut) {
  drm_dbg_kms(display->drm,
       "[CRTC:%d:%s] C8 pixelformat and degamma together are not possible\n",
       crtc->base.base.id, crtc->base.name);
  return -EINVAL;
 }

 if (crtc_state->output_format != INTEL_OUTPUT_FORMAT_RGB &&
     crtc_state->hw.ctm) {
  drm_dbg_kms(display->drm,
       "[CRTC:%d:%s] YCbCr and CTM together are not possible\n",
       crtc->base.base.id, crtc->base.name);
  return -EINVAL;
 }

 if (crtc_state->output_format != INTEL_OUTPUT_FORMAT_RGB &&
     crtc_state->hw.degamma_lut && crtc_state->hw.gamma_lut) {
  drm_dbg_kms(display->drm,
       "[CRTC:%d:%s] YCbCr and degamma+gamma together are not possible\n",
       crtc->base.base.id, crtc->base.name);
  return -EINVAL;
 }

 crtc_state->gamma_enable = ilk_gamma_enable(crtc_state);

 crtc_state->csc_enable = ilk_csc_enable(crtc_state);

 crtc_state->gamma_mode = ivb_gamma_mode(crtc_state);

 crtc_state->csc_mode = ivb_csc_mode(crtc_state);

 ret = intel_color_add_affected_planes(state, crtc);
 if (ret)
  return ret;

 ret = ivb_assign_luts(crtc_state);
 if (ret)
  return ret;

 ilk_assign_csc(crtc_state);

 crtc_state->preload_luts = intel_can_preload_luts(state, crtc);

 return 0;
}

static u32 glk_gamma_mode(const struct intel_crtc_state *crtc_state)
{
 if (!crtc_state->gamma_enable ||
     lut_is_legacy(crtc_state->hw.gamma_lut))
  return GAMMA_MODE_MODE_8BIT;
 else
  return GAMMA_MODE_MODE_10BIT;
}

static bool glk_use_pre_csc_lut_for_gamma(const struct intel_crtc_state *crtc_state)
{
 return crtc_state->hw.gamma_lut &&
  !crtc_state->c8_planes &&
  crtc_state->output_format != INTEL_OUTPUT_FORMAT_RGB;
}

static int glk_assign_luts(struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(crtc_state);

 if (glk_use_pre_csc_lut_for_gamma(crtc_state)) {
  struct drm_property_blob *gamma_lut;

  gamma_lut = create_resized_lut(display, crtc_state->hw.gamma_lut,
            DISPLAY_INFO(display)->color.degamma_lut_size,
            false);
  if (IS_ERR(gamma_lut))
   return PTR_ERR(gamma_lut);

  drm_property_replace_blob(&crtc_state->pre_csc_lut, gamma_lut);
  drm_property_replace_blob(&crtc_state->post_csc_lut, NULL);

  drm_property_blob_put(gamma_lut);

  return 0;
 }

 if (ilk_lut_limited_range(crtc_state)) {
  struct drm_property_blob *gamma_lut;

  gamma_lut = create_resized_lut(display, crtc_state->hw.gamma_lut,
            drm_color_lut_size(crtc_state->hw.gamma_lut),
            true);
  if (IS_ERR(gamma_lut))
   return PTR_ERR(gamma_lut);

  drm_property_replace_blob(&crtc_state->post_csc_lut, gamma_lut);

  drm_property_blob_put(gamma_lut);
 } else {
  drm_property_replace_blob(&crtc_state->post_csc_lut, crtc_state->hw.gamma_lut);
 }

 drm_property_replace_blob(&crtc_state->pre_csc_lut, crtc_state->hw.degamma_lut);

 /*
 * On GLK+ both pipe CSC and degamma LUT are controlled
 * by csc_enable. Hence for the cases where the CSC is
 * needed but degamma LUT is not we need to load a
 * linear degamma LUT.
 */

 if (crtc_state->csc_enable && !crtc_state->pre_csc_lut)
  drm_property_replace_blob(&crtc_state->pre_csc_lut,
       display->color.glk_linear_degamma_lut);

 return 0;
}

static int glk_check_luts(const struct intel_crtc_state *crtc_state)
{
 u32 degamma_tests = intel_degamma_lut_tests(crtc_state);
 u32 gamma_tests = intel_gamma_lut_tests(crtc_state);

 if (glk_use_pre_csc_lut_for_gamma(crtc_state))
  gamma_tests |= degamma_tests;

 return _check_luts(crtc_state, degamma_tests, gamma_tests);
}

static int glk_color_check(struct intel_atomic_state *state,
      struct intel_crtc *crtc)
{
 struct intel_display *display = to_intel_display(state);
 struct intel_crtc_state *crtc_state =
  intel_atomic_get_new_crtc_state(state, crtc);
 int ret;

 ret = glk_check_luts(crtc_state);
 if (ret)
  return ret;

 if (crtc_state->output_format != INTEL_OUTPUT_FORMAT_RGB &&
     crtc_state->hw.ctm) {
  drm_dbg_kms(display->drm,
       "[CRTC:%d:%s] YCbCr and CTM together are not possible\n",
       crtc->base.base.id, crtc->base.name);
  return -EINVAL;
 }

 if (crtc_state->output_format != INTEL_OUTPUT_FORMAT_RGB &&
     crtc_state->hw.degamma_lut && crtc_state->hw.gamma_lut) {
  drm_dbg_kms(display->drm,
       "[CRTC:%d:%s] YCbCr and degamma+gamma together are not possible\n",
       crtc->base.base.id, crtc->base.name);
  return -EINVAL;
 }

 crtc_state->gamma_enable =
  !glk_use_pre_csc_lut_for_gamma(crtc_state) &&
  crtc_state->hw.gamma_lut &&
  !crtc_state->c8_planes;

 /* On GLK+ degamma LUT is controlled by csc_enable */
 crtc_state->csc_enable =
  glk_use_pre_csc_lut_for_gamma(crtc_state) ||
  crtc_state->hw.degamma_lut ||
  crtc_state->output_format != INTEL_OUTPUT_FORMAT_RGB ||
  crtc_state->hw.ctm || ilk_csc_limited_range(crtc_state);

 crtc_state->gamma_mode = glk_gamma_mode(crtc_state);

 crtc_state->csc_mode = 0;

 ret = intel_color_add_affected_planes(state, crtc);
 if (ret)
  return ret;

 ret = glk_assign_luts(crtc_state);
 if (ret)
  return ret;

 ilk_assign_csc(crtc_state);

 crtc_state->preload_luts = intel_can_preload_luts(state, crtc);

 return 0;
}

static u32 icl_gamma_mode(const struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(crtc_state);
 u32 gamma_mode = 0;

 if (crtc_state->hw.degamma_lut)
  gamma_mode |= PRE_CSC_GAMMA_ENABLE;

 if (crtc_state->hw.gamma_lut &&
     !crtc_state->c8_planes)
  gamma_mode |= POST_CSC_GAMMA_ENABLE;

 if (!crtc_state->hw.gamma_lut ||
     lut_is_legacy(crtc_state->hw.gamma_lut))
  gamma_mode |= GAMMA_MODE_MODE_8BIT;
 /*
 * Enable 10bit gamma for D13
 * ToDo: Extend to Logarithmic Gamma once the new UAPI
 * is accepted and implemented by a userspace consumer
 */

 else if (DISPLAY_VER(display) >= 13)
  gamma_mode |= GAMMA_MODE_MODE_10BIT;
 else
  gamma_mode |= GAMMA_MODE_MODE_12BIT_MULTI_SEG;

 return gamma_mode;
}

static u32 icl_csc_mode(const struct intel_crtc_state *crtc_state)
{
 u32 csc_mode = 0;

 if (crtc_state->hw.ctm)
  csc_mode |= ICL_CSC_ENABLE;

 if (crtc_state->output_format != INTEL_OUTPUT_FORMAT_RGB ||
     crtc_state->limited_color_range)
  csc_mode |= ICL_OUTPUT_CSC_ENABLE;

 return csc_mode;
}

static int icl_color_check(struct intel_atomic_state *state,
      struct intel_crtc *crtc)
{
 struct intel_crtc_state *crtc_state =
  intel_atomic_get_new_crtc_state(state, crtc);
 int ret;

 ret = check_luts(crtc_state);
 if (ret)
  return ret;

 crtc_state->gamma_mode = icl_gamma_mode(crtc_state);

 crtc_state->csc_mode = icl_csc_mode(crtc_state);

 intel_assign_luts(crtc_state);

 icl_assign_csc(crtc_state);

 crtc_state->preload_luts = intel_can_preload_luts(state, crtc);

 return 0;
}

static int i9xx_post_csc_lut_precision(const struct intel_crtc_state *crtc_state)
{
 if (!crtc_state->gamma_enable && !crtc_state->c8_planes)
  return 0;

 switch (crtc_state->gamma_mode) {
 case GAMMA_MODE_MODE_8BIT:
  return 8;
 case GAMMA_MODE_MODE_10BIT:
  return 10;
 default:
  MISSING_CASE(crtc_state->gamma_mode);
  return 0;
 }
}

static int i9xx_pre_csc_lut_precision(const struct intel_crtc_state *crtc_state)
{
 return 0;
}

static int i965_post_csc_lut_precision(const struct intel_crtc_state *crtc_state)
{
 if (!crtc_state->gamma_enable && !crtc_state->c8_planes)
  return 0;

 switch (crtc_state->gamma_mode) {
 case GAMMA_MODE_MODE_8BIT:
  return 8;
 case GAMMA_MODE_MODE_10BIT:
  return 16;
 default:
  MISSING_CASE(crtc_state->gamma_mode);
  return 0;
 }
}

static int ilk_gamma_mode_precision(u32 gamma_mode)
{
 switch (gamma_mode) {
 case GAMMA_MODE_MODE_8BIT:
  return 8;
 case GAMMA_MODE_MODE_10BIT:
  return 10;
 default:
  MISSING_CASE(gamma_mode);
  return 0;
 }
}

static bool ilk_has_post_csc_lut(const struct intel_crtc_state *crtc_state)
{
 if (crtc_state->c8_planes)
  return true;

 return crtc_state->gamma_enable &&
  (crtc_state->csc_mode & CSC_POSITION_BEFORE_GAMMA) != 0;
}

static bool ilk_has_pre_csc_lut(const struct intel_crtc_state *crtc_state)
{
 return crtc_state->gamma_enable &&
  (crtc_state->csc_mode & CSC_POSITION_BEFORE_GAMMA) == 0;
}

static int ilk_post_csc_lut_precision(const struct intel_crtc_state *crtc_state)
{
 if (!ilk_has_post_csc_lut(crtc_state))
  return 0;

 return ilk_gamma_mode_precision(crtc_state->gamma_mode);
}

static int ilk_pre_csc_lut_precision(const struct intel_crtc_state *crtc_state)
{
 if (!ilk_has_pre_csc_lut(crtc_state))
  return 0;

 return ilk_gamma_mode_precision(crtc_state->gamma_mode);
}

static int ivb_post_csc_lut_precision(const struct intel_crtc_state *crtc_state)
{
 if (crtc_state->gamma_enable &&
     crtc_state->gamma_mode == GAMMA_MODE_MODE_SPLIT)
  return 10;

 return ilk_post_csc_lut_precision(crtc_state);
}

static int ivb_pre_csc_lut_precision(const struct intel_crtc_state *crtc_state)
{
 if (crtc_state->gamma_enable &&
     crtc_state->gamma_mode == GAMMA_MODE_MODE_SPLIT)
  return 10;

 return ilk_pre_csc_lut_precision(crtc_state);
}

static int chv_post_csc_lut_precision(const struct intel_crtc_state *crtc_state)
{
 if (crtc_state->cgm_mode & CGM_PIPE_MODE_GAMMA)
  return 10;

 return i965_post_csc_lut_precision(crtc_state);
}

static int chv_pre_csc_lut_precision(const struct intel_crtc_state *crtc_state)
{
 if (crtc_state->cgm_mode & CGM_PIPE_MODE_DEGAMMA)
  return 14;

 return 0;
}

static int glk_post_csc_lut_precision(const struct intel_crtc_state *crtc_state)
{
 if (!crtc_state->gamma_enable && !crtc_state->c8_planes)
  return 0;

 return ilk_gamma_mode_precision(crtc_state->gamma_mode);
}

static int glk_pre_csc_lut_precision(const struct intel_crtc_state *crtc_state)
{
 if (!crtc_state->csc_enable)
  return 0;

 return 16;
}

static bool icl_has_post_csc_lut(const struct intel_crtc_state *crtc_state)
{
 if (crtc_state->c8_planes)
  return true;

 return crtc_state->gamma_mode & POST_CSC_GAMMA_ENABLE;
}

static bool icl_has_pre_csc_lut(const struct intel_crtc_state *crtc_state)
{
 return crtc_state->gamma_mode & PRE_CSC_GAMMA_ENABLE;
}

static int icl_post_csc_lut_precision(const struct intel_crtc_state *crtc_state)
{
 if (!icl_has_post_csc_lut(crtc_state))
  return 0;

 switch (crtc_state->gamma_mode & GAMMA_MODE_MODE_MASK) {
 case GAMMA_MODE_MODE_8BIT:
  return 8;
 case GAMMA_MODE_MODE_10BIT:
  return 10;
 case GAMMA_MODE_MODE_12BIT_MULTI_SEG:
  return 16;
 default:
  MISSING_CASE(crtc_state->gamma_mode);
  return 0;
 }
}

static int icl_pre_csc_lut_precision(const struct intel_crtc_state *crtc_state)
{
 if (!icl_has_pre_csc_lut(crtc_state))
  return 0;

 return 16;
}

static bool err_check(const struct drm_color_lut *lut1,
        const struct drm_color_lut *lut2, u32 err)
{
 return ((abs((long)lut2->red - lut1->red)) <= err) &&
  ((abs((long)lut2->blue - lut1->blue)) <= err) &&
  ((abs((long)lut2->green - lut1->green)) <= err);
}

static bool intel_lut_entries_equal(const struct drm_color_lut *lut1,
        const struct drm_color_lut *lut2,
        int lut_size, u32 err)
{
 int i;

 for (i = 0; i < lut_size; i++) {
  if (!err_check(&lut1[i], &lut2[i], err))
   return false;
 }

 return true;
}

static bool intel_lut_equal(const struct drm_property_blob *blob1,
       const struct drm_property_blob *blob2,
       int check_size, int precision)
{
 const struct drm_color_lut *lut1, *lut2;
 int lut_size1, lut_size2;
 u32 err;

 if (!blob1 != !blob2)
  return false;

 if (!blob1 != !precision)
  return false;

 if (!blob1)
  return true;

 lut_size1 = drm_color_lut_size(blob1);
 lut_size2 = drm_color_lut_size(blob2);

 if (lut_size1 != lut_size2)
  return false;

 if (check_size > lut_size1)
  return false;

 lut1 = blob1->data;
 lut2 = blob2->data;

 err = 0xffff >> precision;

 if (!check_size)
  check_size = lut_size1;

 return intel_lut_entries_equal(lut1, lut2, check_size, err);
}

static bool i9xx_lut_equal(const struct intel_crtc_state *crtc_state,
      const struct drm_property_blob *blob1,
      const struct drm_property_blob *blob2,
      bool is_pre_csc_lut)
{
 int check_size = 0;

 if (is_pre_csc_lut)
  return intel_lut_equal(blob1, blob2, 0,
           i9xx_pre_csc_lut_precision(crtc_state));

 /* 10bit mode last entry is implicit, just skip it */
 if (crtc_state->gamma_mode == GAMMA_MODE_MODE_10BIT)
  check_size = 128;

 return intel_lut_equal(blob1, blob2, check_size,
          i9xx_post_csc_lut_precision(crtc_state));
}

static bool i965_lut_equal(const struct intel_crtc_state *crtc_state,
      const struct drm_property_blob *blob1,
      const struct drm_property_blob *blob2,
      bool is_pre_csc_lut)
{
 if (is_pre_csc_lut)
  return intel_lut_equal(blob1, blob2, 0,
           i9xx_pre_csc_lut_precision(crtc_state));
 else
  return intel_lut_equal(blob1, blob2, 0,
           i965_post_csc_lut_precision(crtc_state));
}

static bool chv_lut_equal(const struct intel_crtc_state *crtc_state,
     const struct drm_property_blob *blob1,
     const struct drm_property_blob *blob2,
     bool is_pre_csc_lut)
{
 if (is_pre_csc_lut)
  return intel_lut_equal(blob1, blob2, 0,
           chv_pre_csc_lut_precision(crtc_state));
 else
  return intel_lut_equal(blob1, blob2, 0,
           chv_post_csc_lut_precision(crtc_state));
}

static bool ilk_lut_equal(const struct intel_crtc_state *crtc_state,
     const struct drm_property_blob *blob1,
     const struct drm_property_blob *blob2,
     bool is_pre_csc_lut)
{
 if (is_pre_csc_lut)
  return intel_lut_equal(blob1, blob2, 0,
           ilk_pre_csc_lut_precision(crtc_state));
 else
  return intel_lut_equal(blob1, blob2, 0,
           ilk_post_csc_lut_precision(crtc_state));
}

static bool ivb_lut_equal(const struct intel_crtc_state *crtc_state,
     const struct drm_property_blob *blob1,
     const struct drm_property_blob *blob2,
     bool is_pre_csc_lut)
{
 if (is_pre_csc_lut)
  return intel_lut_equal(blob1, blob2, 0,
           ivb_pre_csc_lut_precision(crtc_state));
 else
  return intel_lut_equal(blob1, blob2, 0,
           ivb_post_csc_lut_precision(crtc_state));
}

static bool glk_lut_equal(const struct intel_crtc_state *crtc_state,
     const struct drm_property_blob *blob1,
     const struct drm_property_blob *blob2,
     bool is_pre_csc_lut)
{
 if (is_pre_csc_lut)
  return intel_lut_equal(blob1, blob2, 0,
           glk_pre_csc_lut_precision(crtc_state));
 else
  return intel_lut_equal(blob1, blob2, 0,
           glk_post_csc_lut_precision(crtc_state));
}

static bool icl_lut_equal(const struct intel_crtc_state *crtc_state,
     const struct drm_property_blob *blob1,
     const struct drm_property_blob *blob2,
     bool is_pre_csc_lut)
{
 int check_size = 0;

 if (is_pre_csc_lut)
  return intel_lut_equal(blob1, blob2, 0,
           icl_pre_csc_lut_precision(crtc_state));

 /* hw readout broken except for the super fine segment :( */
 if ((crtc_state->gamma_mode & GAMMA_MODE_MODE_MASK) ==
     GAMMA_MODE_MODE_12BIT_MULTI_SEG)
  check_size = 9;

 return intel_lut_equal(blob1, blob2, check_size,
          icl_post_csc_lut_precision(crtc_state));
}

static struct drm_property_blob *i9xx_read_lut_8(struct intel_crtc *crtc)
{
 struct intel_display *display = to_intel_display(crtc);
 enum pipe pipe = crtc->pipe;
 struct drm_property_blob *blob;
 struct drm_color_lut *lut;
 int i;

 blob = drm_property_create_blob(display->drm,
     sizeof(lut[0]) * LEGACY_LUT_LENGTH,
     NULL);
 if (IS_ERR(blob))
  return NULL;

 lut = blob->data;

 for (i = 0; i < LEGACY_LUT_LENGTH; i++) {
  u32 val = intel_de_read_fw(display,
        PALETTE(display, pipe, i));

  i9xx_lut_8_pack(&lut[i], val);
 }

 return blob;
}

static struct drm_property_blob *i9xx_read_lut_10(struct intel_crtc *crtc)
{
 struct intel_display *display = to_intel_display(crtc);
 u32 lut_size = DISPLAY_INFO(display)->color.gamma_lut_size;
 enum pipe pipe = crtc->pipe;
 struct drm_property_blob *blob;
 struct drm_color_lut *lut;
 u32 ldw, udw;
 int i;

 blob = drm_property_create_blob(display->drm,
     lut_size * sizeof(lut[0]), NULL);
 if (IS_ERR(blob))
  return NULL;

 lut = blob->data;

 for (i = 0; i < lut_size - 1; i++) {
  ldw = intel_de_read_fw(display,
           PALETTE(display, pipe, 2 * i + 0));
  udw = intel_de_read_fw(display,
           PALETTE(display, pipe, 2 * i + 1));

  i9xx_lut_10_pack(&lut[i], ldw, udw);
 }

 i9xx_lut_10_pack_slope(&lut[i], ldw, udw);

 return blob;
}

static void i9xx_read_luts(struct intel_crtc_state *crtc_state)
{
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);

 if (!crtc_state->gamma_enable && !crtc_state->c8_planes)
  return;

 switch (crtc_state->gamma_mode) {
 case GAMMA_MODE_MODE_8BIT:
  crtc_state->post_csc_lut = i9xx_read_lut_8(crtc);
  break;
 case GAMMA_MODE_MODE_10BIT:
  crtc_state->post_csc_lut = i9xx_read_lut_10(crtc);
  break;
 default:
  MISSING_CASE(crtc_state->gamma_mode);
  break;
 }
}

static struct drm_property_blob *i965_read_lut_10p6(struct intel_crtc *crtc)
{
 struct intel_display *display = to_intel_display(crtc);
 int i, lut_size = DISPLAY_INFO(display)->color.gamma_lut_size;
 enum pipe pipe = crtc->pipe;
 struct drm_property_blob *blob;
 struct drm_color_lut *lut;

 blob = drm_property_create_blob(display->drm,
     sizeof(lut[0]) * lut_size,
     NULL);
 if (IS_ERR(blob))
  return NULL;

 lut = blob->data;

 for (i = 0; i < lut_size - 1; i++) {
  u32 ldw = intel_de_read_fw(display,
        PALETTE(display, pipe, 2 * i + 0));
  u32 udw = intel_de_read_fw(display,
        PALETTE(display, pipe, 2 * i + 1));

  i965_lut_10p6_pack(&lut[i], ldw, udw);
 }

 lut[i].red = i965_lut_11p6_max_pack(intel_de_read_fw(display, PIPEGCMAX(display, pipe, 0)));
 lut[i].green = i965_lut_11p6_max_pack(intel_de_read_fw(display, PIPEGCMAX(display, pipe, 1)));
 lut[i].blue = i965_lut_11p6_max_pack(intel_de_read_fw(display, PIPEGCMAX(display, pipe, 2)));

 return blob;
}

static void i965_read_luts(struct intel_crtc_state *crtc_state)
{
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);

 if (!crtc_state->gamma_enable && !crtc_state->c8_planes)
  return;

 switch (crtc_state->gamma_mode) {
 case GAMMA_MODE_MODE_8BIT:
  crtc_state->post_csc_lut = i9xx_read_lut_8(crtc);
  break;
 case GAMMA_MODE_MODE_10BIT:
  crtc_state->post_csc_lut = i965_read_lut_10p6(crtc);
  break;
 default:
  MISSING_CASE(crtc_state->gamma_mode);
  break;
 }
}

static struct drm_property_blob *chv_read_cgm_degamma(struct intel_crtc *crtc)
{
 struct intel_display *display = to_intel_display(crtc);
 int i, lut_size = DISPLAY_INFO(display)->color.degamma_lut_size;
 enum pipe pipe = crtc->pipe;
 struct drm_property_blob *blob;
 struct drm_color_lut *lut;

 blob = drm_property_create_blob(display->drm,
     sizeof(lut[0]) * lut_size,
     NULL);
 if (IS_ERR(blob))
  return NULL;

 lut = blob->data;

 for (i = 0; i < lut_size; i++) {
  u32 ldw = intel_de_read_fw(display, CGM_PIPE_DEGAMMA(pipe, i, 0));
  u32 udw = intel_de_read_fw(display, CGM_PIPE_DEGAMMA(pipe, i, 1));

  chv_cgm_degamma_pack(&lut[i], ldw, udw);
 }

 return blob;
}

static struct drm_property_blob *chv_read_cgm_gamma(struct intel_crtc *crtc)
{
 struct intel_display *display = to_intel_display(crtc);
 int i, lut_size = DISPLAY_INFO(display)->color.gamma_lut_size;
 enum pipe pipe = crtc->pipe;
 struct drm_property_blob *blob;
 struct drm_color_lut *lut;

 blob = drm_property_create_blob(display->drm,
     sizeof(lut[0]) * lut_size,
     NULL);
 if (IS_ERR(blob))
  return NULL;

 lut = blob->data;

 for (i = 0; i < lut_size; i++) {
  u32 ldw = intel_de_read_fw(display, CGM_PIPE_GAMMA(pipe, i, 0));
  u32 udw = intel_de_read_fw(display, CGM_PIPE_GAMMA(pipe, i, 1));

  chv_cgm_gamma_pack(&lut[i], ldw, udw);
 }

 return blob;
}

static void chv_get_config(struct intel_crtc_state *crtc_state)
{
 struct intel_display *display = to_intel_display(crtc_state);
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);

 crtc_state->cgm_mode = intel_de_read(display, CGM_PIPE_MODE(crtc->pipe));

 i9xx_get_config(crtc_state);
}

static void chv_read_luts(struct intel_crtc_state *crtc_state)
{
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);

 if (crtc_state->cgm_mode & CGM_PIPE_MODE_DEGAMMA)
  crtc_state->pre_csc_lut = chv_read_cgm_degamma(crtc);

 if (crtc_state->cgm_mode & CGM_PIPE_MODE_GAMMA)
  crtc_state->post_csc_lut = chv_read_cgm_gamma(crtc);
 else
  i965_read_luts(crtc_state);
}

static struct drm_property_blob *ilk_read_lut_8(struct intel_crtc *crtc)
{
 struct intel_display *display = to_intel_display(crtc);
 enum pipe pipe = crtc->pipe;
 struct drm_property_blob *blob;
 struct drm_color_lut *lut;
 int i;

 blob = drm_property_create_blob(display->drm,
     sizeof(lut[0]) * LEGACY_LUT_LENGTH,
     NULL);
 if (IS_ERR(blob))
  return NULL;

 lut = blob->data;

 for (i = 0; i < LEGACY_LUT_LENGTH; i++) {
  u32 val = intel_de_read_fw(display, LGC_PALETTE(pipe, i));

  i9xx_lut_8_pack(&lut[i], val);
 }

 return blob;
}

static struct drm_property_blob *ilk_read_lut_10(struct intel_crtc *crtc)
{
 struct intel_display *display = to_intel_display(crtc);
 int i, lut_size = DISPLAY_INFO(display)->color.gamma_lut_size;
 enum pipe pipe = crtc->pipe;
 struct drm_property_blob *blob;
 struct drm_color_lut *lut;

 blob = drm_property_create_blob(display->drm,
     sizeof(lut[0]) * lut_size,
     NULL);
 if (IS_ERR(blob))
  return NULL;

 lut = blob->data;

 for (i = 0; i < lut_size; i++) {
  u32 val = intel_de_read_fw(display, PREC_PALETTE(pipe, i));

  ilk_lut_10_pack(&lut[i], val);
 }

 return blob;
}

static void ilk_get_config(struct intel_crtc_state *crtc_state)
{
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);

 crtc_state->csc_mode = ilk_read_csc_mode(crtc);

 i9xx_get_config(crtc_state);
}

static void ilk_read_luts(struct intel_crtc_state *crtc_state)
{
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
 struct drm_property_blob **blob =
  ilk_has_post_csc_lut(crtc_state) ?
  &crtc_state->post_csc_lut : &crtc_state->pre_csc_lut;

 if (!crtc_state->gamma_enable && !crtc_state->c8_planes)
  return;

 switch (crtc_state->gamma_mode) {
 case GAMMA_MODE_MODE_8BIT:
  *blob = ilk_read_lut_8(crtc);
  break;
 case GAMMA_MODE_MODE_10BIT:
  *blob = ilk_read_lut_10(crtc);
  break;
 default:
  MISSING_CASE(crtc_state->gamma_mode);
  break;
 }
}

/*
 * IVB/HSW Bspec / PAL_PREC_INDEX:
 * "Restriction : Index auto increment mode is not
 *  supported and must not be enabled."
 */

static struct drm_property_blob *ivb_read_lut_10(struct intel_crtc *crtc,
       u32 prec_index)
{
 struct intel_display *display = to_intel_display(crtc);
 int i, lut_size = ivb_lut_10_size(prec_index);
 enum pipe pipe = crtc->pipe;
 struct drm_property_blob *blob;
 struct drm_color_lut *lut;

 blob = drm_property_create_blob(display->drm,
     sizeof(lut[0]) * lut_size,
     NULL);
 if (IS_ERR(blob))
  return NULL;

 lut = blob->data;

 for (i = 0; i < lut_size; i++) {
  u32 val;

  intel_de_write_fw(display, PREC_PAL_INDEX(pipe),
      prec_index + i);
  val = intel_de_read_fw(display, PREC_PAL_DATA(pipe));

  ilk_lut_10_pack(&lut[i], val);
 }

 intel_de_write_fw(display, PREC_PAL_INDEX(pipe),
     PAL_PREC_INDEX_VALUE(0));

 return blob;
}

static void ivb_read_luts(struct intel_crtc_state *crtc_state)
{
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
 struct drm_property_blob **blob =
  ilk_has_post_csc_lut(crtc_state) ?
  &crtc_state->post_csc_lut : &crtc_state->pre_csc_lut;

 if (!crtc_state->gamma_enable && !crtc_state->c8_planes)
  return;

 switch (crtc_state->gamma_mode) {
 case GAMMA_MODE_MODE_8BIT:
  *blob = ilk_read_lut_8(crtc);
  break;
 case GAMMA_MODE_MODE_SPLIT:
  crtc_state->pre_csc_lut =
   ivb_read_lut_10(crtc, PAL_PREC_SPLIT_MODE |
     PAL_PREC_INDEX_VALUE(0));
  crtc_state->post_csc_lut =
   ivb_read_lut_10(crtc, PAL_PREC_SPLIT_MODE |
     PAL_PREC_INDEX_VALUE(512));
  break;
 case GAMMA_MODE_MODE_10BIT:
  *blob = ivb_read_lut_10(crtc, PAL_PREC_INDEX_VALUE(0));
  break;
 default:
  MISSING_CASE(crtc_state->gamma_mode);
  break;
 }
}

/* On BDW+ the index auto increment mode actually works */
static struct drm_property_blob *bdw_read_lut_10(struct intel_crtc *crtc,
       u32 prec_index)
{
 struct intel_display *display = to_intel_display(crtc);
 int i, lut_size = ivb_lut_10_size(prec_index);
 enum pipe pipe = crtc->pipe;
 struct drm_property_blob *blob;
 struct drm_color_lut *lut;

 blob = drm_property_create_blob(display->drm,
     sizeof(lut[0]) * lut_size,
     NULL);
 if (IS_ERR(blob))
  return NULL;

 lut = blob->data;

 intel_de_write_fw(display, PREC_PAL_INDEX(pipe),
     prec_index);
 intel_de_write_fw(display, PREC_PAL_INDEX(pipe),
     PAL_PREC_AUTO_INCREMENT |
     prec_index);

 for (i = 0; i < lut_size; i++) {
  u32 val = intel_de_read_fw(display, PREC_PAL_DATA(pipe));

  ilk_lut_10_pack(&lut[i], val);
 }

 intel_de_write_fw(display, PREC_PAL_INDEX(pipe),
     PAL_PREC_INDEX_VALUE(0));

 return blob;
}

static void bdw_read_luts(struct intel_crtc_state *crtc_state)
{
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
 struct drm_property_blob **blob =
  ilk_has_post_csc_lut(crtc_state) ?
  &crtc_state->post_csc_lut : &crtc_state->pre_csc_lut;

 if (!crtc_state->gamma_enable && !crtc_state->c8_planes)
  return;

 switch (crtc_state->gamma_mode) {
 case GAMMA_MODE_MODE_8BIT:
  *blob = ilk_read_lut_8(crtc);
  break;
 case GAMMA_MODE_MODE_SPLIT:
  crtc_state->pre_csc_lut =
   bdw_read_lut_10(crtc, PAL_PREC_SPLIT_MODE |
     PAL_PREC_INDEX_VALUE(0));
  crtc_state->post_csc_lut =
   bdw_read_lut_10(crtc, PAL_PREC_SPLIT_MODE |
     PAL_PREC_INDEX_VALUE(512));
  break;
 case GAMMA_MODE_MODE_10BIT:
  *blob = bdw_read_lut_10(crtc, PAL_PREC_INDEX_VALUE(0));
  break;
 default:
  MISSING_CASE(crtc_state->gamma_mode);
  break;
 }
}

static struct drm_property_blob *glk_read_degamma_lut(struct intel_crtc *crtc)
{
 struct intel_display *display = to_intel_display(crtc);
 int i, lut_size = DISPLAY_INFO(display)->color.degamma_lut_size;
 enum pipe pipe = crtc->pipe;
 struct drm_property_blob *blob;
 struct drm_color_lut *lut;

 blob = drm_property_create_blob(display->drm,
     sizeof(lut[0]) * lut_size,
     NULL);
 if (IS_ERR(blob))
  return NULL;

 lut = blob->data;

 /*
 * When setting the auto-increment bit, the hardware seems to
 * ignore the index bits, so we need to reset it to index 0
 * separately.
 */

 intel_de_write_fw(display, PRE_CSC_GAMC_INDEX(pipe),
     PRE_CSC_GAMC_INDEX_VALUE(0));
 intel_de_write_fw(display, PRE_CSC_GAMC_INDEX(pipe),
     PRE_CSC_GAMC_AUTO_INCREMENT |
     PRE_CSC_GAMC_INDEX_VALUE(0));

 for (i = 0; i < lut_size; i++) {
  u32 val = intel_de_read_fw(display, PRE_CSC_GAMC_DATA(pipe));

  if (DISPLAY_VER(display) >= 14)
   mtl_degamma_lut_pack(&lut[i], val);
  else
   glk_degamma_lut_pack(&lut[i], val);
 }

 intel_de_write_fw(display, PRE_CSC_GAMC_INDEX(pipe),
     PRE_CSC_GAMC_INDEX_VALUE(0));

 return blob;
}

static void glk_read_luts(struct intel_crtc_state *crtc_state)
{
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);

 if (crtc_state->csc_enable)
  crtc_state->pre_csc_lut = glk_read_degamma_lut(crtc);

 if (!crtc_state->gamma_enable && !crtc_state->c8_planes)
  return;

 switch (crtc_state->gamma_mode) {
 case GAMMA_MODE_MODE_8BIT:
  crtc_state->post_csc_lut = ilk_read_lut_8(crtc);
  break;
 case GAMMA_MODE_MODE_10BIT:
  crtc_state->post_csc_lut = bdw_read_lut_10(crtc, PAL_PREC_INDEX_VALUE(0));
  break;
 default:
  MISSING_CASE(crtc_state->gamma_mode);
  break;
 }
}

static struct drm_property_blob *
icl_read_lut_multi_segment(struct intel_crtc *crtc)
{
 struct intel_display *display = to_intel_display(crtc);
 int i, lut_size = DISPLAY_INFO(display)->color.gamma_lut_size;
 enum pipe pipe = crtc->pipe;
 struct drm_property_blob *blob;
 struct drm_color_lut *lut;

 blob = drm_property_create_blob(display->drm,
     sizeof(lut[0]) * lut_size,
     NULL);
 if (IS_ERR(blob))
  return NULL;

 lut = blob->data;

 intel_de_write_fw(display, PREC_PAL_MULTI_SEG_INDEX(pipe),
     PAL_PREC_MULTI_SEG_INDEX_VALUE(0));
 intel_de_write_fw(display, PREC_PAL_MULTI_SEG_INDEX(pipe),
     PAL_PREC_MULTI_SEG_AUTO_INCREMENT |
     PAL_PREC_MULTI_SEG_INDEX_VALUE(0));

 for (i = 0; i < 9; i++) {
  u32 ldw = intel_de_read_fw(display, PREC_PAL_MULTI_SEG_DATA(pipe));
  u32 udw = intel_de_read_fw(display, PREC_PAL_MULTI_SEG_DATA(pipe));

  ilk_lut_12p4_pack(&lut[i], ldw, udw);
 }

 intel_de_write_fw(display, PREC_PAL_MULTI_SEG_INDEX(pipe),
     PAL_PREC_MULTI_SEG_INDEX_VALUE(0));

 /*
 * FIXME readouts from PAL_PREC_DATA register aren't giving
 * correct values in the case of fine and coarse segments.
 * Restricting readouts only for super fine segment as of now.
 */


 return blob;
}

static void icl_read_luts(struct intel_crtc_state *crtc_state)
{
 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);

 if (icl_has_pre_csc_lut(crtc_state))
  crtc_state->pre_csc_lut = glk_read_degamma_lut(crtc);

 if (!icl_has_post_csc_lut(crtc_state))
  return;

 switch (crtc_state->gamma_mode & GAMMA_MODE_MODE_MASK) {
 case GAMMA_MODE_MODE_8BIT:
  crtc_state->post_csc_lut = ilk_read_lut_8(crtc);
  break;
 case GAMMA_MODE_MODE_10BIT:
  crtc_state->post_csc_lut = bdw_read_lut_10(crtc, PAL_PREC_INDEX_VALUE(0));
  break;
 case GAMMA_MODE_MODE_12BIT_MULTI_SEG:
  crtc_state->post_csc_lut = icl_read_lut_multi_segment(crtc);
  break;
 default:
  MISSING_CASE(crtc_state->gamma_mode);
  break;
 }
}

static const struct intel_color_funcs chv_color_funcs = {
 .color_check = chv_color_check,
 .color_commit_arm = i9xx_color_commit_arm,
 .load_luts = chv_load_luts,
 .read_luts = chv_read_luts,
 .lut_equal = chv_lut_equal,
 .read_csc = chv_read_csc,
 .get_config = chv_get_config,
};

static const struct intel_color_funcs vlv_color_funcs = {
 .color_check = vlv_color_check,
 .color_commit_arm = i9xx_color_commit_arm,
 .load_luts = vlv_load_luts,
 .read_luts = i965_read_luts,
 .lut_equal = i965_lut_equal,
 .read_csc = vlv_read_csc,
 .get_config = i9xx_get_config,
};

static const struct intel_color_funcs i965_color_funcs = {
 .color_check = i9xx_color_check,
 .color_commit_arm = i9xx_color_commit_arm,
 .load_luts = i965_load_luts,
 .read_luts = i965_read_luts,
 .lut_equal = i965_lut_equal,
 .get_config = i9xx_get_config,
};

static const struct intel_color_funcs i9xx_color_funcs = {
 .color_check = i9xx_color_check,
 .color_commit_arm = i9xx_color_commit_arm,
 .load_luts = i9xx_load_luts,
 .read_luts = i9xx_read_luts,
 .lut_equal = i9xx_lut_equal,
 .get_config = i9xx_get_config,
};

static const struct intel_color_funcs tgl_color_funcs = {
 .color_check = icl_color_check,
 .color_commit_noarm = icl_color_commit_noarm,
 .color_commit_arm = icl_color_commit_arm,
 .load_luts = icl_load_luts,
 .read_luts = icl_read_luts,
 .lut_equal = icl_lut_equal,
 .read_csc = icl_read_csc,
 .get_config = skl_get_config,
};

static const struct intel_color_funcs icl_color_funcs = {
 .color_check = icl_color_check,
 .color_commit_noarm = icl_color_commit_noarm,
 .color_commit_arm = icl_color_commit_arm,
 .color_post_update = icl_color_post_update,
 .load_luts = icl_load_luts,
 .read_luts = icl_read_luts,
 .lut_equal = icl_lut_equal,
 .read_csc = icl_read_csc,
 .get_config = skl_get_config,
};

static const struct intel_color_funcs glk_color_funcs = {
 .color_check = glk_color_check,
 .color_commit_noarm = skl_color_commit_noarm,
 .color_commit_arm = skl_color_commit_arm,
 .load_luts = glk_load_luts,
 .read_luts = glk_read_luts,
 .lut_equal = glk_lut_equal,
 .read_csc = skl_read_csc,
 .get_config = skl_get_config,
};

static const struct intel_color_funcs skl_color_funcs = {
 .color_check = ivb_color_check,
 .color_commit_noarm = skl_color_commit_noarm,
 .color_commit_arm = skl_color_commit_arm,
 .load_luts = bdw_load_luts,
 .read_luts = bdw_read_luts,
 .lut_equal = ivb_lut_equal,
 .read_csc = skl_read_csc,
 .get_config = skl_get_config,
};

static const struct intel_color_funcs bdw_color_funcs = {
 .color_check = ivb_color_check,
 .color_commit_noarm = ilk_color_commit_noarm,
 .color_commit_arm = hsw_color_commit_arm,
 .load_luts = bdw_load_luts,
 .read_luts = bdw_read_luts,
 .lut_equal = ivb_lut_equal,
 .read_csc = ilk_read_csc,
 .get_config = hsw_get_config,
};

static const struct intel_color_funcs hsw_color_funcs = {
 .color_check = ivb_color_check,
 .color_commit_noarm = ilk_color_commit_noarm,
 .color_commit_arm = hsw_color_commit_arm,
 .load_luts = ivb_load_luts,
 .read_luts = ivb_read_luts,
 .lut_equal = ivb_lut_equal,
 .read_csc = ilk_read_csc,
 .get_config = hsw_get_config,
};

static const struct intel_color_funcs ivb_color_funcs = {
 .color_check = ivb_color_check,
 .color_commit_noarm = ilk_color_commit_noarm,
 .color_commit_arm = ilk_color_commit_arm,
 .load_luts = ivb_load_luts,
 .read_luts = ivb_read_luts,
 .lut_equal = ivb_lut_equal,
 .read_csc = ilk_read_csc,
 .get_config = ilk_get_config,
};

static const struct intel_color_funcs ilk_color_funcs = {
 .color_check = ilk_color_check,
 .color_commit_noarm = ilk_color_commit_noarm,
 .color_commit_arm = ilk_color_commit_arm,
 .load_luts = ilk_load_luts,
 .read_luts = ilk_read_luts,
 .lut_equal = ilk_lut_equal,
 .read_csc = ilk_read_csc,
 .get_config = ilk_get_config,
};

void intel_color_crtc_init(struct intel_crtc *crtc)
{
 struct intel_display *display = to_intel_display(crtc);
 int degamma_lut_size, gamma_lut_size;
 bool has_ctm;

 drm_mode_crtc_set_gamma_size(&crtc->base, 256);

 gamma_lut_size = DISPLAY_INFO(display)->color.gamma_lut_size;
 degamma_lut_size = DISPLAY_INFO(display)->color.degamma_lut_size;
 has_ctm = DISPLAY_VER(display) >= 5;

 /*
 * "DPALETTE_A: NOTE: The 8-bit (non-10-bit) mode is the
 *  only mode supported by Alviso and Grantsdale."
 *
 * Actually looks like this affects all of gen3.
 * Confirmed on alv,cst,pnv. Mobile gen2 parts (alm,mgm)
 * are confirmed not to suffer from this restriction.
 */

 if (DISPLAY_VER(display) == 3 && crtc->pipe == PIPE_A)
  gamma_lut_size = 256;

 drm_crtc_enable_color_mgmt(&crtc->base, degamma_lut_size,
       has_ctm, gamma_lut_size);
}

int intel_color_init(struct intel_display *display)
{
 struct drm_property_blob *blob;

 if (DISPLAY_VER(display) != 10)
  return 0;

 blob = create_linear_lut(display,
     DISPLAY_INFO(display)->color.degamma_lut_size);
 if (IS_ERR(blob))
  return PTR_ERR(blob);

 display->color.glk_linear_degamma_lut = blob;

 return 0;
}

void intel_color_init_hooks(struct intel_display *display)
{
 if (HAS_GMCH(display)) {
  if (display->platform.cherryview)
   display->funcs.color = &chv_color_funcs;
  else if (display->platform.valleyview)
   display->funcs.color = &vlv_color_funcs;
  else if (DISPLAY_VER(display) >= 4)
   display->funcs.color = &i965_color_funcs;
  else
   display->funcs.color = &i9xx_color_funcs;
 } else {
  if (DISPLAY_VER(display) >= 12)
   display->funcs.color = &tgl_color_funcs;
  else if (DISPLAY_VER(display) == 11)
   display->funcs.color = &icl_color_funcs;
  else if (DISPLAY_VER(display) == 10)
   display->funcs.color = &glk_color_funcs;
  else if (DISPLAY_VER(display) == 9)
   display->funcs.color = &skl_color_funcs;
  else if (DISPLAY_VER(display) == 8)
   display->funcs.color = &bdw_color_funcs;
  else if (display->platform.haswell)
   display->funcs.color = &hsw_color_funcs;
  else if (DISPLAY_VER(display) == 7)
   display->funcs.color = &ivb_color_funcs;
  else
   display->funcs.color = &ilk_color_funcs;
 }
}

Messung V0.5 in Prozent
C=95 H=93 G=93

¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.71Angebot  (Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-04-26) ¤

*Eine klare Vorstellung vom Zielzustand






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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge