> -----Original Message----- > From: Intel-gfx <intel-gfx-bounces@xxxxxxxxxxxxxxxxxxxxx> On Behalf Of Ville > Syrjala > Sent: Saturday, July 18, 2020 2:44 AM > To: intel-gfx@xxxxxxxxxxxxxxxxxxxxx > Subject: [PATCH 20/20] drm/i915: Add 10bit gamma mode for gen2/3 > > From: Ville Syrjälä <ville.syrjala@xxxxxxxxxxxxxxx> > > Some gen2/gen3 parts have a 10bit gamma mode, on some pipes. > Expose it. > > The format is different to the later i965+ style in that we store a 10bit value and > a 6 bit floating point slope for each entry. Ie. the hardware extrapolates the > intermediate steps from the current LUT entry, instead of interpolating between > the current and next LUT entries. This also means we don't store the last LUT > entry in any register as it is defined by the previous LUT entry's value+slope. > > The slope has limited precision though (2 bit exponent + 4 bit mantissa), so we > have to allow for more error in the state checker for the last entry, and we have > to make sure userspace doesn't pass in something where the slope is simply to > steep. In theory we should perhaps check the slope for every interval, but we > don't do that for any other interpolated gamma mode and I suspect they may > also have some internal limit on the slope. I haven't confirmed that theory > though. Anyways, only the last entry provides a slight challenge for the state > checker since all the other entries do have an explicit value stored in a register. Couldn't verify the details from spec. But with the description, was not able to spot any issue with the implementation. Acked-by: Uma Shankar <uma.shankar@xxxxxxxxx> > Signed-off-by: Ville Syrjälä <ville.syrjala@xxxxxxxxxxxxxxx> > --- > drivers/gpu/drm/i915/display/intel_color.c | 248 ++++++++++++++++++++- > drivers/gpu/drm/i915/i915_pci.c | 10 +- > drivers/gpu/drm/i915/i915_reg.h | 38 +++- > 3 files changed, 279 insertions(+), 17 deletions(-) > > diff --git a/drivers/gpu/drm/i915/display/intel_color.c > b/drivers/gpu/drm/i915/display/intel_color.c > index ca6c6679368c..055a49ed4e3a 100644 > --- a/drivers/gpu/drm/i915/display/intel_color.c > +++ b/drivers/gpu/drm/i915/display/intel_color.c > @@ -414,6 +414,79 @@ static void i9xx_lut_8_pack(struct drm_color_lut *entry, > u32 val) > entry->blue = > intel_color_lut_pack(REG_FIELD_GET(LGC_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 _i9xx_lut_10_ldw(color[0].red) << 16 | > + _i9xx_lut_10_ldw(color[0].green) << 8 | > + _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 _i9xx_lut_10_udw(color[0].red, color[1].red) << 16 | > + _i9xx_lut_10_udw(color[0].green, color[1].green) << 8 | > + _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) { @@ -554,6 +627,22 @@ > static void i9xx_load_lut_8(struct intel_crtc *crtc, > i9xx_lut_8(&lut[i])); > } > > +static void i9xx_load_lut_10(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; > + > + for (i = 0; i < lut_size - 1; i++) { > + intel_de_write(dev_priv, PALETTE(pipe, 2 * i + 0), > + i9xx_lut_10_ldw(&lut[i])); > + intel_de_write(dev_priv, PALETTE(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); > @@ -562,7 +651,10 @@ static void i9xx_load_luts(const struct intel_crtc_state > *crtc_state) > > assert_pll_enabled(dev_priv, crtc->pipe); > > - i9xx_load_lut_8(crtc, gamma_lut); > + if (crtc_state->gamma_mode == GAMMA_MODE_MODE_8BIT) > + i9xx_load_lut_8(crtc, gamma_lut); > + else > + i9xx_load_lut_10(crtc, gamma_lut); > } > > static void i965_load_lut_10p6(struct intel_crtc *crtc, @@ -1326,11 +1418,36 > @@ static u32 i9xx_gamma_mode(struct intel_crtc_state *crtc_state) > crtc_state_is_legacy_gamma(crtc_state)) > return GAMMA_MODE_MODE_8BIT; > else > - return GAMMA_MODE_MODE_10BIT; /* i965+ only */ > + 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 drm_i915_private *dev_priv, > + const struct drm_property_blob *blob) { > + 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(&dev_priv->drm, "Last gamma LUT entry exceeds > max slope\n"); > + return -EINVAL; > + } > + > + return 0; > } > > static int i9xx_color_check(struct intel_crtc_state *crtc_state) { > + struct drm_i915_private *dev_priv = > +to_i915(crtc_state->uapi.crtc->dev); > int ret; > > ret = check_luts(crtc_state); > @@ -1343,6 +1460,13 @@ static int i9xx_color_check(struct intel_crtc_state > *crtc_state) > > crtc_state->gamma_mode = i9xx_gamma_mode(crtc_state); > > + if (INTEL_GEN(dev_priv) < 4 && > + crtc_state->gamma_mode == GAMMA_MODE_MODE_10BIT) { > + ret = i9xx_check_lut_10(dev_priv, crtc_state->hw.gamma_lut); > + if (ret) > + return ret; > + } > + > ret = intel_color_add_affected_planes(crtc_state); > if (ret) > return ret; > @@ -1613,6 +1737,22 @@ static int icl_color_check(struct intel_crtc_state > *crtc_state) } > > static int i9xx_gamma_precision(const struct intel_crtc_state *crtc_state) > +{ > + if (!crtc_state->gamma_enable) > + 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 i965_gamma_precision(const struct intel_crtc_state > +*crtc_state) > { > if (!crtc_state->gamma_enable) > return 0; > @@ -1700,7 +1840,7 @@ static int chv_gamma_precision(const struct > intel_crtc_state *crtc_state) > if (crtc_state->cgm_mode & CGM_PIPE_MODE_GAMMA) > return 10; > else > - return i9xx_gamma_precision(crtc_state); > + return i965_gamma_precision(crtc_state); > } > > static int glk_degamma_precision(const struct intel_crtc_state *crtc_state) @@ - > 1803,6 +1943,37 @@ static bool intel_lut_equal(const struct drm_property_blob > *blob1, > return true; > } > > +static bool i9xx_lut_10_equal(const struct drm_property_blob *blob1, > + const struct drm_property_blob *blob2, > + int bit_precision) > +{ > + const struct drm_color_lut *lut1, *lut2; > + int i, lut_size; > + > + if (!intel_color_lut_sizes_equal(blob1, blob2)) > + return false; > + > + if (!blob1) > + return true; > + > + if (!bit_precision) > + return false; > + > + lut_size = drm_color_lut_size(blob1); > + lut1 = blob1->data; > + lut2 = blob2->data; > + > + for (i = 0; i < lut_size - 1; i++) { > + if (!lut_entry_equal(&lut1[i], &lut2[i], > + 0xffff >> bit_precision)) > + return false; > + } > + > + /* limited precision for the last entry due to floating point slope */ > + return lut_entry_equal(&lut1[i], &lut2[i], > + 0xffff >> (bit_precision - 3)); } > + > static bool i9xx_degamma_equal(const struct intel_crtc_state *crtc_state1, > const struct intel_crtc_state *crtc_state2, > bool fastset) > @@ -1821,8 +1992,23 @@ static bool i9xx_gamma_equal(const struct > intel_crtc_state *crtc_state1, > const struct drm_property_blob *gamma_lut1 = crtc_state1- > >hw.gamma_lut; > const struct drm_property_blob *gamma_lut2 = crtc_state2- > >hw.gamma_lut; > > + if (crtc_state1->gamma_mode == GAMMA_MODE_MODE_10BIT) > + return i9xx_lut_10_equal(gamma_lut1, gamma_lut2, > + i9xx_gamma_precision(crtc_state1)); > + else > + return intel_lut_equal(gamma_lut1, gamma_lut2, > + i9xx_gamma_precision(crtc_state1)); > +} > + > +static bool i965_gamma_equal(const struct intel_crtc_state *crtc_state1, > + const struct intel_crtc_state *crtc_state2, > + bool fastset) > +{ > + const struct drm_property_blob *gamma_lut1 = crtc_state1- > >hw.gamma_lut; > + const struct drm_property_blob *gamma_lut2 = > +crtc_state2->hw.gamma_lut; > + > return intel_lut_equal(gamma_lut1, gamma_lut2, > - i9xx_gamma_precision(crtc_state1)); > + i965_gamma_precision(crtc_state1)); > } > > static bool chv_degamma_equal(const struct intel_crtc_state *crtc_state1, @@ - > 2084,6 +2270,36 @@ static struct drm_property_blob *i9xx_read_lut_8(struct > intel_crtc *crtc) > return blob; > } > > +static struct drm_property_blob * > +i9xx_read_lut_10(struct intel_crtc *crtc) { > + struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); > + u32 lut_size = INTEL_INFO(dev_priv)->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(&dev_priv->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(dev_priv, PALETTE(pipe, 2 * i + 0)); > + udw = intel_de_read(dev_priv, PALETTE(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); > @@ -2091,7 +2307,10 @@ static void i9xx_read_luts(struct intel_crtc_state > *crtc_state) > if (!crtc_state->gamma_enable) > return; > > - crtc_state->hw.gamma_lut = i9xx_read_lut_8(crtc); > + if (crtc_state->gamma_mode == GAMMA_MODE_MODE_8BIT) > + crtc_state->hw.gamma_lut = i9xx_read_lut_8(crtc); > + else > + crtc_state->hw.gamma_lut = i9xx_read_lut_10(crtc); > } > > static struct drm_property_blob *i965_read_lut_10p6(struct intel_crtc *crtc) > @@ -2546,6 +2765,7 @@ void intel_color_init(struct intel_crtc *crtc) { > struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); > bool has_ctm = INTEL_INFO(dev_priv)->color.degamma_lut_size != 0; > + int gamma_lut_size; > > drm_mode_crtc_set_gamma_size(&crtc->base, 256); > > @@ -2562,7 +2782,7 @@ void intel_color_init(struct intel_crtc *crtc) > dev_priv->display.color_commit = i9xx_color_commit; > dev_priv->display.load_luts = i965_load_luts; > dev_priv->display.read_luts = i965_read_luts; > - dev_priv->display.gamma_equal = i9xx_gamma_equal; > + dev_priv->display.gamma_equal = i965_gamma_equal; > dev_priv->display.degamma_equal = > i9xx_degamma_equal; > } else { > dev_priv->display.color_check = i9xx_color_check; @@ - > 2618,8 +2838,20 @@ void intel_color_init(struct intel_crtc *crtc) > > } > > + /* > + * "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 (IS_GEN(dev_priv, 3) && crtc->pipe == PIPE_A) > + gamma_lut_size = 256; > + else > + gamma_lut_size = INTEL_INFO(dev_priv)->color.gamma_lut_size; > + > drm_crtc_enable_color_mgmt(&crtc->base, > INTEL_INFO(dev_priv)- > >color.degamma_lut_size, > - has_ctm, > - INTEL_INFO(dev_priv)->color.gamma_lut_size); > + has_ctm, gamma_lut_size); > } > diff --git a/drivers/gpu/drm/i915/i915_pci.c b/drivers/gpu/drm/i915/i915_pci.c > index 2338f92ce490..baa24e4691c0 100644 > --- a/drivers/gpu/drm/i915/i915_pci.c > +++ b/drivers/gpu/drm/i915/i915_pci.c > @@ -127,9 +127,9 @@ > [PIPE_D] = TGL_CURSOR_D_OFFSET, \ > } > > -#define I9XX_COLORS \ > +#define I845_COLORS \ > .color = { .gamma_lut_size = 256 } > -#define I965_COLORS \ > +#define I9XX_COLORS \ > .color = { .gamma_lut_size = 129, \ > .gamma_lut_tests = DRM_COLOR_LUT_NON_DECREASING, \ > } > @@ -194,7 +194,7 @@ > .dma_mask_size = 32, \ > I845_PIPE_OFFSETS, \ > I845_CURSOR_OFFSETS, \ > - I9XX_COLORS, \ > + I845_COLORS, \ > GEN_DEFAULT_PAGE_SIZES, \ > GEN_DEFAULT_REGIONS > > @@ -323,7 +323,7 @@ static const struct intel_device_info pnv_m_info = { > .dma_mask_size = 36, \ > I9XX_PIPE_OFFSETS, \ > I9XX_CURSOR_OFFSETS, \ > - I965_COLORS, \ > + I9XX_COLORS, \ > GEN_DEFAULT_PAGE_SIZES, \ > GEN_DEFAULT_REGIONS > > @@ -524,7 +524,7 @@ static const struct intel_device_info vlv_info = { > .display_mmio_offset = VLV_DISPLAY_BASE, > I9XX_PIPE_OFFSETS, > I9XX_CURSOR_OFFSETS, > - I965_COLORS, > + I9XX_COLORS, > GEN_DEFAULT_PAGE_SIZES, > GEN_DEFAULT_REGIONS, > }; > diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h > index 428ef06b8084..0c34d62f22f4 100644 > --- a/drivers/gpu/drm/i915/i915_reg.h > +++ b/drivers/gpu/drm/i915/i915_reg.h > @@ -3708,10 +3708,40 @@ static inline bool i915_mmio_reg_valid(i915_reg_t > reg) > #define _PALETTE_A 0xa000 > #define _PALETTE_B 0xa800 > #define _CHV_PALETTE_C 0xc000 > -#define PALETTE_RED_MASK REG_GENMASK(23, 16) > -#define PALETTE_GREEN_MASK REG_GENMASK(15, 8) > -#define PALETTE_BLUE_MASK REG_GENMASK(7, 0) > -#define PALETTE(pipe, i) _MMIO(DISPLAY_MMIO_BASE(dev_priv) + \ > +/* 8bit palette mode */ > +#define PALETTE_RED_MASK REG_GENMASK(23, 16) > +#define PALETTE_RED(x) > REG_FIELD_PREP(PALETTE_RED_MASK, (x)) > +#define PALETTE_GREEN_MASK REG_GENMASK(15, 8) > +#define PALETTE_GREEN(x) REG_FIELD_PREP(PALETTE_GREEN_MASK, > (x)) > +#define PALETTE_BLUE_MASK REG_GENMASK(7, 0) > +#define PALETTE_BLUE(x) REG_FIELD_PREP(PALETTE_BLUE_MASK, > (x)) > +/* i9xx 10bit interpolated ldw */ > +#define PALETTE_10BIT_RED_LDW_MASK REG_GENMASK(23, 16) > +#define PALETTE_10BIT_RED_LDW(x) > REG_FIELD_PREP(PALETTE_10BIT_RED_LDW_MASK, (x)) > +#define PALETTE_10BIT_GREEN_LDW_MASK REG_GENMASK(15, 8) > +#define PALETTE_10BIT_GREEN_LDW(x) > REG_FIELD_PREP(PALETTE_10BIT_GREEN_LDW_MASK, (x)) > +#define PALETTE_10BIT_BLUE_LDW_MASK REG_GENMASK(7, 0) > +#define PALETTE_10BIT_BLUE_LDW(x) > REG_FIELD_PREP(PALETTE_10BIT_BLUE_LDW_MASK, (x)) > +/* i9xx 10bit interpolated udw */ > +#define PALETTE_10BIT_RED_EXP_MASK REG_GENMASK(23, 22) > +#define PALETTE_10BIT_RED_EXP(x) > REG_FIELD_PREP(PALETTE_10BIT_RED_EXP_MASK, (x)) > +#define PALETTE_10BIT_RED_MANT_MASK REG_GENMASK(21, 18) > +#define PALETTE_10BIT_RED_MANT(x) > REG_FIELD_PREP(PALETTE_10BIT_RED_MANT_MASK, (x)) > +#define PALETTE_10BIT_RED_UDW_MASK REG_GENMASK(17, 16) > +#define PALETTE_10BIT_RED_UDW(x) > REG_FIELD_PREP(PALETTE_10BIT_RED_UDW_MASK, (X)) > +#define PALETTE_10BIT_GREEN_EXP_MASK REG_GENMASK(15, 14) > +#define PALETTE_10BIT_GREEN_EXP(x) > REG_FIELD_PREP(PALETTE_10BIT_GREEN_EXP_MASK, (x)) > +#define PALETTE_10BIT_GREEN_MANT_MASK REG_GENMASK(13, 10) > +#define PALETTE_10BIT_GREEN_MANT(x) > REG_FIELD_PREP(PALETTE_10BIT_GREEN_MANT_MASK, (x)) > +#define PALETTE_10BIT_GREEN_UDW_MASK REG_GENMASK(9, 8) > +#define PALETTE_10BIT_GREEN_UDW(x) > REG_FIELD_PREP(PALETTE_10BIT_GREEN_UDW_MASK, (x)) > +#define PALETTE_10BIT_BLUE_EXP_MASK REG_GENMASK(7, 6) > +#define PALETTE_10BIT_BLUE_EXP(x) > REG_FIELD_PREP(PALETTE_10BIT_BLUE_EXP_MASK, (x)) > +#define PALETTE_10BIT_BLUE_MANT_MASK REG_GENMASK(5, 2) > +#define PALETTE_10BIT_BLUE_MANT(x) > REG_FIELD_PREP(PALETTE_10BIT_BLUE_MANT_MASK, (x)) > +#define PALETTE_10BIT_BLUE_UDW_MASK REG_GENMASK(1, 0) > +#define PALETTE_10BIT_BLUE_UDW(x) > REG_FIELD_PREP(PALETTE_10BIT_BLUE_UDW_MASK, (x)) > +#define PALETTE(pipe, i) _MMIO(DISPLAY_MMIO_BASE(dev_priv) + \ > _PICK((pipe), _PALETTE_A, \ > _PALETTE_B, _CHV_PALETTE_C) + \ > (i) * 4) > -- > 2.26.2 > > _______________________________________________ > Intel-gfx mailing list > Intel-gfx@xxxxxxxxxxxxxxxxxxxxx > https://lists.freedesktop.org/mailman/listinfo/intel-gfx _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/intel-gfx