[Intel-gfx] [PATCH 20/20] drm/i915: Add 10bit gamma mode for gen2/3

Ville Syrjala ville.syrjala at linux.intel.com
Fri Jul 17 21:13:45 UTC 2020


From: Ville Syrjälä <ville.syrjala at linux.intel.com>

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.

Signed-off-by: Ville Syrjälä <ville.syrjala at linux.intel.com>
---
 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



More information about the Intel-gfx mailing list