]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/gpu/drm/i915/intel_color.c
drm/i915/icl: Add Multi-segmented gamma support
[linux.git] / drivers / gpu / drm / i915 / intel_color.c
index 9093daabc290d25324823d8ffec5fa00dab2bdef..23a84dd7989fb1986219dcf70202db16b2322ac2 100644 (file)
@@ -41,6 +41,7 @@
 #define CTM_COEFF_ABS(coeff)           ((coeff) & (CTM_COEFF_SIGN - 1))
 
 #define LEGACY_LUT_LENGTH              256
+
 /*
  * Extract the CSC coefficient from a CTM coefficient (in U32.32 fixed point
  * format). This macro takes the coefficient we want transformed and the
@@ -607,7 +608,7 @@ static void bdw_load_lut_10(struct intel_crtc *crtc,
        I915_WRITE(PREC_PAL_INDEX(pipe), 0);
 }
 
-static void ivb_load_lut_10_max(struct intel_crtc *crtc)
+static void ivb_load_lut_ext_max(struct intel_crtc *crtc)
 {
        struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
        enum pipe pipe = crtc->pipe;
@@ -640,7 +641,7 @@ static void ivb_load_luts(const struct intel_crtc_state *crtc_state)
        } else if (crtc_state->gamma_mode == GAMMA_MODE_MODE_SPLIT) {
                ivb_load_lut_10(crtc, degamma_lut, PAL_PREC_SPLIT_MODE |
                                PAL_PREC_INDEX_VALUE(0));
-               ivb_load_lut_10_max(crtc);
+               ivb_load_lut_ext_max(crtc);
                ivb_load_lut_10(crtc, gamma_lut, PAL_PREC_SPLIT_MODE |
                                PAL_PREC_INDEX_VALUE(512));
        } else {
@@ -648,7 +649,7 @@ static void ivb_load_luts(const struct intel_crtc_state *crtc_state)
 
                ivb_load_lut_10(crtc, blob,
                                PAL_PREC_INDEX_VALUE(0));
-               ivb_load_lut_10_max(crtc);
+               ivb_load_lut_ext_max(crtc);
        }
 }
 
@@ -663,7 +664,7 @@ static void bdw_load_luts(const struct intel_crtc_state *crtc_state)
        } else if (crtc_state->gamma_mode == GAMMA_MODE_MODE_SPLIT) {
                bdw_load_lut_10(crtc, degamma_lut, PAL_PREC_SPLIT_MODE |
                                PAL_PREC_INDEX_VALUE(0));
-               ivb_load_lut_10_max(crtc);
+               ivb_load_lut_ext_max(crtc);
                bdw_load_lut_10(crtc, gamma_lut, PAL_PREC_SPLIT_MODE |
                                PAL_PREC_INDEX_VALUE(512));
        } else {
@@ -671,7 +672,7 @@ static void bdw_load_luts(const struct intel_crtc_state *crtc_state)
 
                bdw_load_lut_10(crtc, blob,
                                PAL_PREC_INDEX_VALUE(0));
-               ivb_load_lut_10_max(crtc);
+               ivb_load_lut_ext_max(crtc);
        }
 }
 
@@ -763,10 +764,120 @@ static void glk_load_luts(const struct intel_crtc_state *crtc_state)
                i9xx_load_luts(crtc_state);
        } else {
                bdw_load_lut_10(crtc, gamma_lut, PAL_PREC_INDEX_VALUE(0));
-               ivb_load_lut_10_max(crtc);
+               ivb_load_lut_ext_max(crtc);
+       }
+}
+
+/* ilk+ "12.4" interpolated format (high 10 bits) */
+static u32 ilk_lut_12p4_udw(const struct drm_color_lut *color)
+{
+       return (color->red >> 6) << 20 | (color->green >> 6) << 10 |
+               (color->blue >> 6);
+}
+
+/* ilk+ "12.4" interpolated format (low 6 bits) */
+static u32 ilk_lut_12p4_ldw(const struct drm_color_lut *color)
+{
+       return (color->red & 0x3f) << 24 | (color->green & 0x3f) << 14 |
+               (color->blue & 0x3f) << 4;
+}
+
+static void
+icl_load_gcmax(const struct intel_crtc_state *crtc_state,
+              const struct drm_color_lut *color)
+{
+       struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
+       struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+       enum pipe pipe = crtc->pipe;
+
+       /* Fixme: LUT entries are 16 bit only, so we can prog 0xFFFF max */
+       I915_WRITE(PREC_PAL_GC_MAX(pipe, 0), color->red);
+       I915_WRITE(PREC_PAL_GC_MAX(pipe, 1), color->green);
+       I915_WRITE(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->base.crtc);
+       struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+       const struct drm_property_blob *blob = crtc_state->base.gamma_lut;
+       const struct drm_color_lut *lut = blob->data;
+       enum pipe pipe = crtc->pipe;
+       u32 i;
+
+       /*
+        * Every entry in the multi-segment LUT is corresponding to a superfine
+        * segment step which is 1/(8 * 128 * 256).
+        *
+        * Superfine segment has 9 entries, corresponding to values
+        * 0, 1/(8 * 128 * 256), 2/(8 * 128 * 256) .... 8/(8 * 128 * 256).
+        */
+       I915_WRITE(PREC_PAL_MULTI_SEG_INDEX(pipe), PAL_PREC_AUTO_INCREMENT);
+
+       for (i = 0; i < 9; i++) {
+               const struct drm_color_lut *entry = &lut[i];
+
+               I915_WRITE(PREC_PAL_MULTI_SEG_DATA(pipe),
+                          ilk_lut_12p4_ldw(entry));
+               I915_WRITE(PREC_PAL_MULTI_SEG_DATA(pipe),
+                          ilk_lut_12p4_udw(entry));
        }
 }
 
+static void
+icl_program_gamma_multi_segment(const struct intel_crtc_state *crtc_state)
+{
+       struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
+       struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+       const struct drm_property_blob *blob = crtc_state->base.gamma_lut;
+       const struct drm_color_lut *lut = blob->data;
+       const struct drm_color_lut *entry;
+       enum pipe pipe = crtc->pipe;
+       u32 i;
+
+       /*
+        *
+        * Program Fine segment (let's call it seg2)...
+        *
+        * Fine segment's step is 1/(128 * 256) ie 1/(128 * 256),  2/(128*256)
+        * ... 256/(128*256). So in order to program fine segment of LUT we
+        * need to pick every 8'th entry in LUT, and program 256 indexes.
+        *
+        * PAL_PREC_INDEX[0] and PAL_PREC_INDEX[1] map to seg2[1],
+        * with seg2[0] being unused by the hardware.
+        */
+       I915_WRITE(PREC_PAL_INDEX(pipe), PAL_PREC_AUTO_INCREMENT);
+       for (i = 1; i < 257; i++) {
+               entry = &lut[i * 8];
+               I915_WRITE(PREC_PAL_DATA(pipe), ilk_lut_12p4_ldw(entry));
+               I915_WRITE(PREC_PAL_DATA(pipe), ilk_lut_12p4_udw(entry));
+       }
+
+       /*
+        * Program Coarse segment (let's call it seg3)...
+        *
+        * Coarse segment's 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];
+               I915_WRITE(PREC_PAL_DATA(pipe), ilk_lut_12p4_ldw(entry));
+               I915_WRITE(PREC_PAL_DATA(pipe), ilk_lut_12p4_udw(entry));
+       }
+
+       /* The last entry in the LUT is to be programmed in GCMAX */
+       entry = &lut[256 * 8 * 128];
+       icl_load_gcmax(crtc_state, entry);
+       ivb_load_lut_ext_max(crtc);
+}
+
 static void icl_load_luts(const struct intel_crtc_state *crtc_state)
 {
        const struct drm_property_blob *gamma_lut = crtc_state->base.gamma_lut;
@@ -775,65 +886,94 @@ static void icl_load_luts(const struct intel_crtc_state *crtc_state)
        if (crtc_state->base.degamma_lut)
                glk_load_degamma_lut(crtc_state);
 
-       if ((crtc_state->gamma_mode & GAMMA_MODE_MODE_MASK) ==
-           GAMMA_MODE_MODE_8BIT) {
+       switch (crtc_state->gamma_mode & GAMMA_MODE_MODE_MASK) {
+       case GAMMA_MODE_MODE_8BIT:
                i9xx_load_luts(crtc_state);
-       } else {
+               break;
+
+       case GAMMA_MODE_MODE_12BIT_MULTI_SEGMENTED:
+               icl_program_gamma_superfine_segment(crtc_state);
+               icl_program_gamma_multi_segment(crtc_state);
+               break;
+
+       default:
                bdw_load_lut_10(crtc, gamma_lut, PAL_PREC_INDEX_VALUE(0));
-               ivb_load_lut_10_max(crtc);
+               ivb_load_lut_ext_max(crtc);
        }
 }
 
-static void cherryview_load_luts(const struct intel_crtc_state *crtc_state)
+static u32 chv_cgm_degamma_ldw(const struct drm_color_lut *color)
+{
+       return drm_color_lut_extract(color->green, 14) << 16 |
+               drm_color_lut_extract(color->blue, 14);
+}
+
+static u32 chv_cgm_degamma_udw(const struct drm_color_lut *color)
+{
+       return drm_color_lut_extract(color->red, 14);
+}
+
+static void chv_load_cgm_degamma(struct intel_crtc *crtc,
+                                const struct drm_property_blob *blob)
 {
-       struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
        struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
-       const struct drm_property_blob *gamma_lut = crtc_state->base.gamma_lut;
-       const struct drm_property_blob *degamma_lut = crtc_state->base.degamma_lut;
+       const struct drm_color_lut *lut = blob->data;
+       int i, lut_size = drm_color_lut_size(blob);
        enum pipe pipe = crtc->pipe;
 
-       cherryview_load_csc_matrix(crtc_state);
-
-       if (crtc_state_is_legacy_gamma(crtc_state)) {
-               i9xx_load_luts(crtc_state);
-               return;
+       for (i = 0; i < lut_size; i++) {
+               I915_WRITE(CGM_PIPE_DEGAMMA(pipe, i, 0),
+                          chv_cgm_degamma_ldw(&lut[i]));
+               I915_WRITE(CGM_PIPE_DEGAMMA(pipe, i, 1),
+                          chv_cgm_degamma_udw(&lut[i]));
        }
+}
 
-       if (degamma_lut) {
-               const struct drm_color_lut *lut = degamma_lut->data;
-               int i, lut_size = INTEL_INFO(dev_priv)->color.degamma_lut_size;
+static u32 chv_cgm_gamma_ldw(const struct drm_color_lut *color)
+{
+       return drm_color_lut_extract(color->green, 10) << 16 |
+               drm_color_lut_extract(color->blue, 10);
+}
 
-               for (i = 0; i < lut_size; i++) {
-                       u32 word0, word1;
+static u32 chv_cgm_gamma_udw(const struct drm_color_lut *color)
+{
+       return drm_color_lut_extract(color->red, 10);
+}
 
-                       /* Write LUT in U0.14 format. */
-                       word0 =
-                       (drm_color_lut_extract(lut[i].green, 14) << 16) |
-                       drm_color_lut_extract(lut[i].blue, 14);
-                       word1 = drm_color_lut_extract(lut[i].red, 14);
+static void chv_load_cgm_gamma(struct intel_crtc *crtc,
+                              const struct drm_property_blob *blob)
+{
+       struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+       const struct drm_color_lut *lut = blob->data;
+       int i, lut_size = drm_color_lut_size(blob);
+       enum pipe pipe = crtc->pipe;
 
-                       I915_WRITE(CGM_PIPE_DEGAMMA(pipe, i, 0), word0);
-                       I915_WRITE(CGM_PIPE_DEGAMMA(pipe, i, 1), word1);
-               }
+       for (i = 0; i < lut_size; i++) {
+               I915_WRITE(CGM_PIPE_GAMMA(pipe, i, 0),
+                          chv_cgm_gamma_ldw(&lut[i]));
+               I915_WRITE(CGM_PIPE_GAMMA(pipe, i, 1),
+                          chv_cgm_gamma_udw(&lut[i]));
        }
+}
 
-       if (gamma_lut) {
-               const struct drm_color_lut *lut = gamma_lut->data;
-               int i, lut_size = INTEL_INFO(dev_priv)->color.gamma_lut_size;
-
-               for (i = 0; i < lut_size; i++) {
-                       u32 word0, word1;
+static void chv_load_luts(const struct intel_crtc_state *crtc_state)
+{
+       struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
+       const struct drm_property_blob *gamma_lut = crtc_state->base.gamma_lut;
+       const struct drm_property_blob *degamma_lut = crtc_state->base.degamma_lut;
 
-                       /* Write LUT in U0.10 format. */
-                       word0 =
-                       (drm_color_lut_extract(lut[i].green, 10) << 16) |
-                       drm_color_lut_extract(lut[i].blue, 10);
-                       word1 = drm_color_lut_extract(lut[i].red, 10);
+       cherryview_load_csc_matrix(crtc_state);
 
-                       I915_WRITE(CGM_PIPE_GAMMA(pipe, i, 0), word0);
-                       I915_WRITE(CGM_PIPE_GAMMA(pipe, i, 1), word1);
-               }
+       if (crtc_state_is_legacy_gamma(crtc_state)) {
+               i9xx_load_luts(crtc_state);
+               return;
        }
+
+       if (degamma_lut)
+               chv_load_cgm_degamma(crtc, degamma_lut);
+
+       if (gamma_lut)
+               chv_load_cgm_gamma(crtc, gamma_lut);
 }
 
 void intel_color_load_luts(const struct intel_crtc_state *crtc_state)
@@ -857,6 +997,14 @@ int intel_color_check(struct intel_crtc_state *crtc_state)
        return dev_priv->display.color_check(crtc_state);
 }
 
+void intel_color_get_config(struct intel_crtc_state *crtc_state)
+{
+       struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
+
+       if (dev_priv->display.read_luts)
+               dev_priv->display.read_luts(crtc_state);
+}
+
 static bool need_plane_update(struct intel_plane *plane,
                              const struct intel_crtc_state *crtc_state)
 {
@@ -937,8 +1085,10 @@ static int check_luts(const struct intel_crtc_state *crtc_state)
                return 0;
 
        /* C8 relies on its palette being stored in the legacy LUT */
-       if (crtc_state->c8_planes)
+       if (crtc_state->c8_planes) {
+               DRM_DEBUG_KMS("C8 pixelformat requires the legacy LUT\n");
                return -EINVAL;
+       }
 
        degamma_length = INTEL_INFO(dev_priv)->color.degamma_lut_size;
        gamma_length = INTEL_INFO(dev_priv)->color.gamma_lut_size;
@@ -1187,7 +1337,7 @@ static u32 icl_gamma_mode(const struct intel_crtc_state *crtc_state)
            crtc_state_is_legacy_gamma(crtc_state))
                gamma_mode |= GAMMA_MODE_MODE_8BIT;
        else
-               gamma_mode |= GAMMA_MODE_MODE_10BIT;
+               gamma_mode |= GAMMA_MODE_MODE_12BIT_MULTI_SEGMENTED;
 
        return gamma_mode;
 }
@@ -1232,7 +1382,7 @@ void intel_color_init(struct intel_crtc *crtc)
                if (IS_CHERRYVIEW(dev_priv)) {
                        dev_priv->display.color_check = chv_color_check;
                        dev_priv->display.color_commit = i9xx_color_commit;
-                       dev_priv->display.load_luts = cherryview_load_luts;
+                       dev_priv->display.load_luts = chv_load_luts;
                } else if (INTEL_GEN(dev_priv) >= 4) {
                        dev_priv->display.color_check = i9xx_color_check;
                        dev_priv->display.color_commit = i9xx_color_commit;