xf86-video-amdgpu: Branch 'master' - 7 commits

Michel Dänzer daenzer at kemper.freedesktop.org
Tue Jun 26 15:45:52 UTC 2018


 src/drmmode_display.c |  693 +++++++++++++++++++++++++++++++++++++++++++++++++-
 src/drmmode_display.h |   25 +
 2 files changed, 715 insertions(+), 3 deletions(-)

New commits:
commit 606075b852d8e1d40ed0a56b5a928abdd7012f95
Author: Leo Li (Sunpeng) <sunpeng.li at amd.com>
Date:   Fri Jun 15 17:05:52 2018 -0400

    Also compose LUT when setting legacy gamma
    
    We compose the two LUTs when pushing non-legacy gamma changes, and the
    same needs to be done when setting legacy gamma.
    
    To do so, we just call push_cm_prop() on the gamma LUT. It will compose
    the LUTs for us, and fall back to using legacy LUT (upscaled to non-
    legacy size) if non-legacy is unavailable.
    
    It's also possible that the Kernel has no support support for non-
    legacy color. In which case, we fall back to legacy gamma.
    
    v2: Remove per-CRTC check for color management support.
    
    Signed-off-by: Leo (Sunpeng) Li <sunpeng.li at amd.com>
    Signed-off-by: Michel Dänzer <michel.daenzer at amd.com>

diff --git a/src/drmmode_display.c b/src/drmmode_display.c
index 2623273..fa0c05b 100644
--- a/src/drmmode_display.c
+++ b/src/drmmode_display.c
@@ -1245,9 +1245,21 @@ drmmode_crtc_gamma_do_set(xf86CrtcPtr crtc, uint16_t *red, uint16_t *green,
 {
 	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
 	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(crtc->scrn);
+	int ret;
+
+	/* Use legacy if no support for non-legacy gamma */
+	if (!drmmode_cm_enabled(drmmode_crtc->drmmode)) {
+		drmModeCrtcSetGamma(pAMDGPUEnt->fd,
+				    drmmode_crtc->mode_crtc->crtc_id,
+				    size, red, green, blue);
+		return;
+	}
 
-	drmModeCrtcSetGamma(pAMDGPUEnt->fd, drmmode_crtc->mode_crtc->crtc_id,
-			    size, red, green, blue);
+	ret = drmmode_crtc_push_cm_prop(crtc, CM_GAMMA_LUT);
+	if (ret)
+		xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
+			   "Setting Gamma LUT failed with errno %d\n",
+			   ret);
 }
 
 Bool
commit e0a4c0e2155a5fcfad747ea5bddcf5b4b551f151
Author: Leo Li (Sunpeng) <sunpeng.li at amd.com>
Date:   Fri Jun 15 17:12:48 2018 -0400

    Compose non-legacy with legacy regamma LUT
    
    Frequently, a user may have non-legacy gamma enabled for monitor
    correction, while using legacy gamma for things like
    redshift/nightlight.
    
    To do so, we compose the two LUTs. Legacy gamma will be applied first,
    then non-legacy. i.e. non-legacy_LUT(legacy_LUT(in_color)).
    
    Note that the staged gamma LUT within the driver-private CRTC will
    always contain the non-legacy LUT. This is to ensure that we have a
    cached copy for future compositions.
    
    v2: Don't compose LUTs if legacy gamma is disabled (which is the case
        for deep 30bpp color). The legacy LUT won't be computed here,
        causing composition to spit out something invalid.
    
    v3: Use LUT sizes that are now cached in drmmode.
    
    Signed-off-by: Leo (Sunpeng) Li <sunpeng.li at amd.com>
    
    [ Michel Dänzer: Replace "crtc->funcs->gamma_set == NULL" with
      !crtc->funcs->gamma_set ]
    
    Signed-off-by: Michel Dänzer <michel.daenzer at amd.com>

diff --git a/src/drmmode_display.c b/src/drmmode_display.c
index 15a2c94..2623273 100644
--- a/src/drmmode_display.c
+++ b/src/drmmode_display.c
@@ -777,6 +777,150 @@ static Bool drmmode_cm_enabled(drmmode_ptr drmmode)
 }
 
 /**
+ * If legacy LUT is a, and non-legacy LUT is b, then the result of b(a(x)) is
+ * returned in out_lut. out_lut's length is expected to be the same as the
+ * non-legacy LUT b.
+ *
+ * @a_(red|green|blue): The red, green, and blue components of the legacy LUT.
+ * @b_lut: The non-legacy LUT, in DRM's color LUT format.
+ * @out_lut: The composed LUT, in DRM's color LUT format.
+ * @len_a: Length of legacy lut.
+ * @len_b: Length of non-legacy lut.
+ */
+static void drmmode_lut_compose(uint16_t *a_red,
+				uint16_t *a_green,
+				uint16_t *a_blue,
+				struct drm_color_lut *b_lut,
+				struct drm_color_lut *out_lut,
+				uint32_t len_a, uint32_t len_b)
+{
+	uint32_t i_l, i_r, i;
+	uint32_t i_amax, i_bmax;
+	uint32_t coeff_ibmax;
+	uint32_t j;
+	uint64_t a_out_ibmax;
+	int color;
+	size_t struct_size = sizeof(struct drm_color_lut);
+
+	uint32_t max_lut = (1 << 16) - 1;
+
+	i_amax = len_a - 1;
+	i_bmax = len_b - 1;
+
+	/* A linear interpolation is done on the legacy LUT before it is
+	 * composed, to bring it up-to-size with the non-legacy LUT. The
+	 * interpolation uses integers by keeping things multiplied until the
+	 * last moment.
+	 */
+	for (color = 0; color < 3; color++) {
+		uint16_t *a, *b, *out;
+
+		/* Set the initial pointers to the right color components. The
+		 * inner for-loop will then maintain the correct offset from
+		 * the initial element.
+		 */
+		if (color == 0) {
+			a = a_red;
+			b = &b_lut[0].red;
+			out = &out_lut[0].red;
+		} else if (color == 1) {
+			a = a_green;
+			b = &b_lut[0].green;
+			out = &out_lut[0].green;
+		} else {
+			a = a_blue;
+			b = &b_lut[0].blue;
+			out = &out_lut[0].blue;
+		}
+
+		for (i = 0; i < len_b; i++) {
+			/* i_l and i_r tracks the left and right elements in
+			 * a_lut, to the sample point i. Also handle last
+			 * element edge case, when i_l = i_amax.
+			 */
+			i_l = i * i_amax / i_bmax;
+			i_r = i_l + !!(i_amax - i_l);
+
+			/* coeff is intended to be in [0, 1), depending on
+			 * where sample i is between i_l and i_r. We keep it
+			 * multiplied with i_bmax throughout to maintain
+			 * precision */
+			coeff_ibmax = (i * i_amax) - (i_l * i_bmax);
+			a_out_ibmax = i_bmax * a[i_l] +
+				      coeff_ibmax * (a[i_r] - a[i_l]);
+
+			/* j = floor((a_out/max_lut)*i_bmax).
+			 * i.e. the element in LUT b that a_out maps to. We
+			 * have to divide by max_lut to normalize a_out, since
+			 * values in the LUTs are [0, 1<<16)
+			 */
+			j = a_out_ibmax / max_lut;
+			*(uint16_t*)((void*)out + (i*struct_size)) =
+				*(uint16_t*)((void*)b + (j*struct_size));
+		}
+	}
+
+	for (i = 0; i < len_b; i++)
+		out_lut[i].reserved = 0;
+}
+
+/**
+ * Resize a LUT, using linear interpolation.
+ *
+ * @in_(red|green|blue): Legacy LUT components
+ * @out_lut: The resized LUT is returned here, in DRM color LUT format.
+ * @len_in: Length of legacy LUT.
+ * @len_out: Length of out_lut, i.e. the target size.
+ */
+static void drmmode_lut_interpolate(uint16_t *in_red,
+				    uint16_t *in_green,
+				    uint16_t *in_blue,
+				    struct drm_color_lut *out_lut,
+				    uint32_t len_in, uint32_t len_out)
+{
+	uint32_t i_l, i_r, i;
+	uint32_t i_amax, i_bmax;
+	uint32_t coeff_ibmax;
+	uint64_t out_ibmax;
+	int color;
+	size_t struct_size = sizeof(struct drm_color_lut);
+
+	i_amax = len_in - 1;
+	i_bmax = len_out - 1;
+
+	/* See @drmmode_lut_compose for details */
+	for (color = 0; color < 3; color++) {
+		uint16_t *in, *out;
+
+		if (color == 0) {
+			in = in_red;
+			out = &out_lut[0].red;
+		} else if (color == 1) {
+			in = in_green;
+			out = &out_lut[0].green;
+		} else {
+			in = in_blue;
+			out = &out_lut[0].blue;
+		}
+
+		for (i = 0; i < len_out; i++) {
+			i_l = i * i_amax / i_bmax;
+			i_r = i_l + !!(i_amax - i_l);
+
+			coeff_ibmax = (i * i_amax) - (i_l * i_bmax);
+			out_ibmax = i_bmax * in[i_l] +
+				      coeff_ibmax * (in[i_r] - in[i_l]);
+
+			*(uint16_t*)((void*)out + (i*struct_size)) =
+				out_ibmax / i_bmax;
+		}
+	}
+
+	for (i = 0; i < len_out; i++)
+		out_lut[i].reserved = 0;
+}
+
+/**
  * Configure and change a color property on a CRTC, through RandR. Only the
  * specified output will be affected, even if the CRTC is attached to multiple
  * outputs. Note that changes will be non-pending: the changes won't be pushed
@@ -993,6 +1137,7 @@ static int drmmode_crtc_push_cm_prop(xf86CrtcPtr crtc,
 	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
 	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(crtc->scrn);
 	drmmode_ptr drmmode = drmmode_crtc->drmmode;
+	Bool free_blob_data = FALSE;
 	uint32_t created_blob_id = 0;
 	uint32_t drm_prop_id;
 	size_t expected_bytes = 0;
@@ -1004,7 +1149,39 @@ static int drmmode_crtc_push_cm_prop(xf86CrtcPtr crtc,
 		/* Calculate the expected size of value in bytes */
 		expected_bytes = sizeof(struct drm_color_lut) *
 					drmmode->gamma_lut_size;
-		blob_data = drmmode_crtc->gamma_lut;
+
+		/* Legacy gamma LUT is disabled on deep 30bpp color. In which
+		 * case, directly use non-legacy LUT.
+		 */
+		if (!crtc->funcs->gamma_set) {
+			blob_data = drmmode_crtc->gamma_lut;
+			goto do_push;
+		}
+
+		blob_data = malloc(expected_bytes);
+		if (!blob_data)
+			return BadAlloc;
+
+		free_blob_data = TRUE;
+		/*
+		 * Compose legacy and non-legacy LUT if non-legacy was set.
+		 * Otherwise, interpolate legacy LUT to non-legacy size.
+		 */
+		if (drmmode_crtc->gamma_lut) {
+			drmmode_lut_compose(crtc->gamma_red,
+					    crtc->gamma_green,
+					    crtc->gamma_blue,
+					    drmmode_crtc->gamma_lut,
+					    blob_data, crtc->gamma_size,
+					    drmmode->gamma_lut_size);
+		} else {
+			drmmode_lut_interpolate(crtc->gamma_red,
+						crtc->gamma_green,
+						crtc->gamma_blue,
+						blob_data,
+						crtc->gamma_size,
+						drmmode->gamma_lut_size);
+		}
 		break;
 	case CM_DEGAMMA_LUT:
 		expected_bytes = sizeof(struct drm_color_lut) *
@@ -1019,6 +1196,7 @@ static int drmmode_crtc_push_cm_prop(xf86CrtcPtr crtc,
 		return BadName;
 	}
 
+do_push:
 	if (blob_data) {
 		ret = drmModeCreatePropertyBlob(pAMDGPUEnt->fd,
 						blob_data, expected_bytes,
@@ -1027,6 +1205,8 @@ static int drmmode_crtc_push_cm_prop(xf86CrtcPtr crtc,
 			xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
 				   "Creating DRM blob failed with errno %d\n",
 				   ret);
+			if (free_blob_data)
+				free(blob_data);
 			return BadRequest;
 		}
 	}
@@ -1048,9 +1228,14 @@ static int drmmode_crtc_push_cm_prop(xf86CrtcPtr crtc,
 		xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
 			   "Setting DRM property blob failed with errno %d\n",
 			   ret);
+		if (free_blob_data)
+			free(blob_data);
 		return BadRequest;
 	}
 
+	if (free_blob_data)
+		free(blob_data);
+
 	return Success;
 }
 
commit e1fe46013c281f4644ca49915ae0ff081582a5b9
Author: Leo Li (Sunpeng) <sunpeng.li at amd.com>
Date:   Fri Jun 15 17:05:39 2018 -0400

    Enable setting of color properties via RandR
    
    Setting a color property involves:
    1. Staging the property onto the driver-private CRTC object
    2. Pushing the staged property into kernel DRM, for HW programming
    
    Add a function to do the staging, and execute the above steps in
    output_property_set.
    
    v2:
    - Remove per-CRTC check for color management support in stage_cm_prop.
    - Use switch statement instead of if statements.
    
    Signed-off-by: Leo (Sunpeng) Li <sunpeng.li at amd.com>
    Signed-off-by: Michel Dänzer <michel.daenzer at amd.com>

diff --git a/src/drmmode_display.c b/src/drmmode_display.c
index baffa88..15a2c94 100644
--- a/src/drmmode_display.c
+++ b/src/drmmode_display.c
@@ -898,6 +898,88 @@ static int rr_configure_and_change_cm_property(xf86OutputPtr output,
 }
 
 /**
+* Stage a color management property. This parses the property value, according
+* to the cm property type, then stores it within the driver-private CRTC
+* object.
+*
+* @crtc: The CRTC to stage the new color management properties in
+* @cm_prop_index: The color property to stage
+* @value: The RandR property value to stage
+*
+* Return 0 on success, X-defined error code on failure.
+*/
+static int drmmode_crtc_stage_cm_prop(xf86CrtcPtr crtc,
+				      enum drmmode_cm_prop cm_prop_index,
+				      RRPropertyValuePtr value)
+{
+	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+	drmmode_ptr drmmode = drmmode_crtc->drmmode;
+	size_t expected_bytes = 0;
+	void **blob_data = NULL;
+	Bool use_default = FALSE;
+
+	/* Update properties on the driver-private CRTC */
+	switch (cm_prop_index) {
+	case CM_GAMMA_LUT:
+		/* Calculate the expected size of value in bytes */
+		expected_bytes = sizeof(struct drm_color_lut) *
+					drmmode->gamma_lut_size;
+
+		/* For gamma and degamma, we allow a default SRGB curve to be
+		 * set via setting a single element
+		 *
+		 * Otherwise, value size is in terms of the value format.
+		 * Ensure it's also in bytes (<< 1) before comparing with the
+		 * expected bytes.
+		 */
+		if (value->size == 1)
+			use_default = TRUE;
+		else if (value->type != XA_INTEGER || value->format != 16 ||
+			 (size_t)(value->size << 1) != expected_bytes)
+			return BadLength;
+
+		blob_data = (void**)&drmmode_crtc->gamma_lut;
+		break;
+	case CM_DEGAMMA_LUT:
+		expected_bytes = sizeof(struct drm_color_lut) *
+					drmmode->degamma_lut_size;
+
+		if (value->size == 1)
+			use_default = TRUE;
+		else if (value->type != XA_INTEGER || value->format != 16 ||
+			 (size_t)(value->size << 1) != expected_bytes)
+			return BadLength;
+
+		blob_data = (void**)&drmmode_crtc->degamma_lut;
+		break;
+	case CM_CTM:
+		expected_bytes = sizeof(struct drm_color_ctm);
+
+		if (value->size == 1)
+			use_default = TRUE;
+		if (value->type != XA_INTEGER || value->format != 32 ||
+		    (size_t)(value->size << 2) != expected_bytes)
+			return BadLength;
+
+		blob_data = (void**)&drmmode_crtc->ctm;
+		break;
+	default:
+		return BadName;
+	}
+
+	free(*blob_data);
+	if (!use_default) {
+		*blob_data = malloc(expected_bytes);
+		if (!*blob_data)
+			return BadAlloc;
+		memcpy(*blob_data, value->data, expected_bytes);
+	} else
+		*blob_data = NULL;
+
+	return Success;
+}
+
+/**
  * Push staged color management properties on the CRTC to DRM.
  *
  * @crtc: The CRTC containing staged properties
@@ -2067,8 +2149,21 @@ drmmode_output_set_property(xf86OutputPtr output, Atom property,
 {
 	drmmode_output_private_ptr drmmode_output = output->driver_private;
 	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(output->scrn);
+	enum drmmode_cm_prop cm_prop_index;
 	int i;
 
+	cm_prop_index = get_cm_enum_from_str(NameForAtom(property));
+	if (cm_prop_index >= 0 && cm_prop_index < CM_DEGAMMA_LUT_SIZE) {
+		if (!output->crtc)
+			return FALSE;
+		if (drmmode_crtc_stage_cm_prop(output->crtc, cm_prop_index,
+					       value))
+			return FALSE;
+		if (drmmode_crtc_push_cm_prop(output->crtc, cm_prop_index))
+			return FALSE;
+		return TRUE;
+	}
+
 	for (i = 0; i < drmmode_output->num_props; i++) {
 		drmmode_prop_ptr p = &drmmode_output->props[i];
 
commit 29de2859e296b4e9f0b4ae7564c353c5518f3f08
Author: Leo Li (Sunpeng) <sunpeng.li at amd.com>
Date:   Fri Jun 15 17:05:28 2018 -0400

    Update color properties on output_get_property
    
    Notify RandR of any updated color properties on the output's CRTC when
    its get_property() hook is called.
    
    v2: Remove per-CRTC check for color management support.
    
    Signed-off-by: Leo (Sunpeng) Li <sunpeng.li at amd.com>
    Signed-off-by: Michel Dänzer <michel.daenzer at amd.com>

diff --git a/src/drmmode_display.c b/src/drmmode_display.c
index b30bf76..baffa88 100644
--- a/src/drmmode_display.c
+++ b/src/drmmode_display.c
@@ -2125,6 +2125,27 @@ drmmode_output_set_property(xf86OutputPtr output, Atom property,
 
 static Bool drmmode_output_get_property(xf86OutputPtr output, Atom property)
 {
+	drmmode_crtc_private_ptr drmmode_crtc;
+	enum drmmode_cm_prop cm_prop_id;
+	int ret;
+
+	/* First, see if it's a cm property */
+	cm_prop_id = get_cm_enum_from_str(NameForAtom(property));
+	if (output->crtc && cm_prop_id != CM_INVALID_PROP) {
+		drmmode_crtc = output->crtc->driver_private;
+
+		ret = rr_configure_and_change_cm_property(output, drmmode_crtc,
+							  cm_prop_id);
+		if (ret) {
+			xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
+				   "Error getting color property: %d\n",
+				   ret);
+			return FALSE;
+		}
+		return TRUE;
+	}
+
+	/* Otherwise, must be an output property. */
 	return TRUE;
 }
 
commit 639acf54b4de6f62000d12cc6dbf4f5e49cae888
Author: Leo Li (Sunpeng) <sunpeng.li at amd.com>
Date:   Fri Jun 15 17:04:58 2018 -0400

    Configure color properties when creating output resources
    
    List color management properties on outputs if there is kernel support.
    Otherwise, don't list them at all.
    
    v2:
    - Use switch statement in configure_and_change
    - Also configure LUT sizes for outputs that don't have an attached CRTC.
      We can do this since LUT sizes are now cached on the drmmode object.
    
    Signed-off-by: Leo (Sunpeng) Li <sunpeng.li at amd.com>
    
    [ Michel Dänzer: Drop const from data pointer declaration in
      rr_configure_and_change_cm_property, to avoid warning when building
      against xserver 1.13 ]
    
    Signed-off-by: Michel Dänzer <michel.daenzer at amd.com>

diff --git a/src/drmmode_display.c b/src/drmmode_display.c
index e0ddc10..b30bf76 100644
--- a/src/drmmode_display.c
+++ b/src/drmmode_display.c
@@ -777,6 +777,127 @@ static Bool drmmode_cm_enabled(drmmode_ptr drmmode)
 }
 
 /**
+ * Configure and change a color property on a CRTC, through RandR. Only the
+ * specified output will be affected, even if the CRTC is attached to multiple
+ * outputs. Note that changes will be non-pending: the changes won't be pushed
+ * to kernel driver.
+ *
+ * @output: RandR output to set the property on.
+ * @crtc: The driver-private CRTC object containing the color properties.
+ *        If this is NULL, "disabled" values of 0 will be used.
+ * @cm_prop_index: Color management property to configure and change.
+ *
+ * Return 0 on success, X-defined error code otherwise.
+ */
+static int rr_configure_and_change_cm_property(xf86OutputPtr output,
+					       drmmode_crtc_private_ptr crtc,
+					       enum drmmode_cm_prop cm_prop_index)
+{
+	drmmode_output_private_ptr drmmode_output = output->driver_private;
+	drmmode_ptr drmmode = drmmode_output->drmmode;
+	Bool need_configure = TRUE;
+	unsigned long length = 0;
+	void *data = NULL;
+	int format = 0;
+	uint32_t zero = 0;
+	INT32 range[2];
+	Atom atom;
+	int err;
+
+	if (cm_prop_index == CM_INVALID_PROP)
+		return BadName;
+
+	switch(cm_prop_index) {
+	case CM_GAMMA_LUT_SIZE:
+		format = 32;
+		length = 1;
+		data = &drmmode->gamma_lut_size;
+		range[0] = 0;
+		range[1] = -1;
+		break;
+	case CM_DEGAMMA_LUT_SIZE:
+		format = 32;
+		length = 1;
+		data = &drmmode->degamma_lut_size;
+		range[0] = 0;
+		range[1] = -1;
+		break;
+	case CM_GAMMA_LUT:
+		format = 16;
+		range[0] = 0;
+		range[1] = (1 << 16) - 1; // Max 16 bit unsigned int.
+		if (crtc && crtc->gamma_lut) {
+			/* Convert from 8bit size to 16bit size */
+			length = sizeof(*crtc->gamma_lut) >> 1;
+			length *= drmmode->gamma_lut_size;
+			data = crtc->gamma_lut;
+		} else {
+			length = 1;
+			data = &zero;
+		}
+		break;
+	case CM_DEGAMMA_LUT:
+		format = 16;
+		range[0] = 0;
+		range[1] = (1 << 16) - 1;
+		if (crtc && crtc->degamma_lut) {
+			length = sizeof(*crtc->degamma_lut) >> 1;
+			length *= drmmode->degamma_lut_size;
+			data = crtc->degamma_lut;
+		} else {
+			length = 1;
+			data = &zero;
+		}
+		break;
+	case CM_CTM:
+		/* CTM is fixed-point S31.32 format. */
+		format = 32;
+		need_configure = FALSE;
+		if (crtc && crtc->ctm) {
+			/* Convert from 8bit size to 32bit size */
+			length = sizeof(*crtc->ctm) >> 2;
+			data = crtc->ctm;
+		} else {
+			length = 1;
+			data = &zero;
+		}
+		break;
+	default:
+		return BadName;
+	}
+
+	atom = MakeAtom(cm_prop_names[cm_prop_index],
+			strlen(cm_prop_names[cm_prop_index]),
+			TRUE);
+	if (!atom)
+		return BadAlloc;
+
+	if (need_configure) {
+		err = RRConfigureOutputProperty(output->randr_output, atom,
+						FALSE, TRUE, FALSE, 2, range);
+		if (err) {
+			xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
+				   "Configuring color management property %s failed with %d\n",
+				   cm_prop_names[cm_prop_index], err);
+			return err;
+		}
+	}
+
+	/* Always issue a non-pending change. We'll push cm properties
+	 * ourselves.
+	 */
+	err = RRChangeOutputProperty(output->randr_output, atom,
+				     XA_INTEGER, format,
+				     PropModeReplace,
+				     length, data, FALSE, FALSE);
+	if (err)
+		xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
+			   "Changing color management property %s failed with %d\n",
+			   cm_prop_names[cm_prop_index], err);
+	return err;
+}
+
+/**
  * Push staged color management properties on the CRTC to DRM.
  *
  * @crtc: The CRTC containing staged properties
@@ -1780,6 +1901,7 @@ static void drmmode_output_create_resources(xf86OutputPtr output)
 {
 	AMDGPUInfoPtr info = AMDGPUPTR(output->scrn);
 	drmmode_output_private_ptr drmmode_output = output->driver_private;
+	drmmode_crtc_private_ptr drmmode_crtc;
 	drmModeConnectorPtr mode_output = drmmode_output->mode_output;
 	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(output->scrn);
 	drmModePropertyPtr drmmode_prop, tearfree_prop;
@@ -1903,6 +2025,15 @@ static void drmmode_output_create_resources(xf86OutputPtr output)
 			}
 		}
 	}
+
+	/* Do not configure cm properties on output if there's no support. */
+	if (!drmmode_cm_enabled(drmmode_output->drmmode))
+		return;
+
+	drmmode_crtc = output->crtc ? output->crtc->driver_private : NULL;
+
+	for (i = 0; i < CM_NUM_PROPS; i++)
+		rr_configure_and_change_cm_property(output, drmmode_crtc, i);
 }
 
 static void
commit 3cf5a281d8481c997029dae164d6fdeca66b9447
Author: Leo Li (Sunpeng) <sunpeng.li at amd.com>
Date:   Fri Jun 15 17:04:44 2018 -0400

    Initialize color properties on CRTC during CRTC init
    
    And destroy them on the CRTC destroy hook.
    
    When initializing color management properties on the private
    drmmode_crtc, we want to:
    
    1. Default its color transform matrix (CTM) to identity
    2. Program hardware with default color management values (SRGB for
       de/regamma, identity for CTM)
    
    It's possible that cm initialization fails due to memory error or DRM
    error. In which case, the RandR state may not reflect the actual
    hardware state.
    
    v2:
    - Use switch statement in push_cm_prop
    - Get rid of per-CRTC cm support checks. Keep it simple and only check
      the first CRTC, since kernel will always report all or nothing for AMD
      hardware.
    - Remove per-CRTC LUT size caching, drmmode now holds that. Update
      commit message to reflect this.
    
    Signed-off-by: Leo (Sunpeng) Li <sunpeng.li at amd.com>
    
    [ Michel Dänzer: Replace "drmmode_crtc->ctm == NULL" with
      !drmmode_crtc->ctm ]
    
    Signed-off-by: Michel Dänzer <michel.daenzer at amd.com>

diff --git a/src/drmmode_display.c b/src/drmmode_display.c
index 916ebac..e0ddc10 100644
--- a/src/drmmode_display.c
+++ b/src/drmmode_display.c
@@ -768,6 +768,89 @@ static enum drmmode_cm_prop get_cm_enum_from_str(const char *prop_name)
 	return CM_INVALID_PROP;
 }
 
+/**
+ * Return TRUE if kernel supports non-legacy color management.
+ */
+static Bool drmmode_cm_enabled(drmmode_ptr drmmode)
+{
+	return drmmode->cm_prop_ids[CM_GAMMA_LUT_SIZE] != 0;
+}
+
+/**
+ * Push staged color management properties on the CRTC to DRM.
+ *
+ * @crtc: The CRTC containing staged properties
+ * @cm_prop_index: The color property to push
+ *
+ * Return 0 on success, X-defined error codes on failure.
+ */
+static int drmmode_crtc_push_cm_prop(xf86CrtcPtr crtc,
+				     enum drmmode_cm_prop cm_prop_index)
+{
+	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(crtc->scrn);
+	drmmode_ptr drmmode = drmmode_crtc->drmmode;
+	uint32_t created_blob_id = 0;
+	uint32_t drm_prop_id;
+	size_t expected_bytes = 0;
+	void *blob_data = NULL;
+	int ret;
+
+	switch (cm_prop_index) {
+	case CM_GAMMA_LUT:
+		/* Calculate the expected size of value in bytes */
+		expected_bytes = sizeof(struct drm_color_lut) *
+					drmmode->gamma_lut_size;
+		blob_data = drmmode_crtc->gamma_lut;
+		break;
+	case CM_DEGAMMA_LUT:
+		expected_bytes = sizeof(struct drm_color_lut) *
+					drmmode->degamma_lut_size;
+		blob_data = drmmode_crtc->degamma_lut;
+		break;
+	case CM_CTM:
+		expected_bytes = sizeof(struct drm_color_ctm);
+		blob_data = drmmode_crtc->ctm;
+		break;
+	default:
+		return BadName;
+	}
+
+	if (blob_data) {
+		ret = drmModeCreatePropertyBlob(pAMDGPUEnt->fd,
+						blob_data, expected_bytes,
+						&created_blob_id);
+		if (ret) {
+			xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
+				   "Creating DRM blob failed with errno %d\n",
+				   ret);
+			return BadRequest;
+		}
+	}
+
+	drm_prop_id = drmmode_crtc->drmmode->cm_prop_ids[cm_prop_index];
+	ret = drmModeObjectSetProperty(pAMDGPUEnt->fd,
+				       drmmode_crtc->mode_crtc->crtc_id,
+				       DRM_MODE_OBJECT_CRTC,
+				       drm_prop_id,
+				       (uint64_t)created_blob_id);
+
+	/* If successful, kernel will have a reference already. Safe to destroy
+	 * the blob either way.
+	 */
+	if (blob_data)
+		drmModeDestroyPropertyBlob(pAMDGPUEnt->fd, created_blob_id);
+
+	if (ret) {
+		xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
+			   "Setting DRM property blob failed with errno %d\n",
+			   ret);
+		return BadRequest;
+	}
+
+	return Success;
+}
+
 static void
 drmmode_crtc_gamma_do_set(xf86CrtcPtr crtc, uint16_t *red, uint16_t *green,
 			  uint16_t *blue, int size)
@@ -1314,6 +1397,22 @@ static Bool drmmode_set_scanout_pixmap(xf86CrtcPtr crtc, PixmapPtr ppix)
 	return TRUE;
 }
 
+static void drmmode_crtc_destroy(xf86CrtcPtr crtc)
+{
+	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+
+	drmModeFreeCrtc(drmmode_crtc->mode_crtc);
+
+	/* Free LUTs and CTM */
+	free(drmmode_crtc->gamma_lut);
+	free(drmmode_crtc->degamma_lut);
+	free(drmmode_crtc->ctm);
+
+	free(drmmode_crtc);
+	crtc->driver_private = NULL;
+}
+
+
 static xf86CrtcFuncsRec drmmode_crtc_funcs = {
 	.dpms = drmmode_crtc_dpms,
 	.set_mode_major = drmmode_set_mode_major,
@@ -1330,7 +1429,7 @@ static xf86CrtcFuncsRec drmmode_crtc_funcs = {
 	.shadow_create = drmmode_crtc_shadow_create,
 	.shadow_allocate = drmmode_crtc_shadow_allocate,
 	.shadow_destroy = drmmode_crtc_shadow_destroy,
-	.destroy = NULL,	/* XXX */
+	.destroy = drmmode_crtc_destroy,
 	.set_scanout_pixmap = drmmode_set_scanout_pixmap,
 };
 
@@ -1354,6 +1453,50 @@ void drmmode_crtc_hw_id(xf86CrtcPtr crtc)
 		drmmode_crtc->hw_id = -1;
 }
 
+/**
+ * Initialize color management properties for the given CRTC by programming
+ * the default gamma/degamma LUTs and CTM.
+ *
+ * If the CRTC does not support color management, or if errors occur during
+ * initialization, all color properties on the driver-private CRTC will left
+ * as NULL.
+ *
+ * @drm_fd: DRM file descriptor
+ * @crtc: CRTC to initialize color management on.
+ */
+static void drmmode_crtc_cm_init(int drm_fd, xf86CrtcPtr crtc)
+{
+	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+	drmmode_ptr drmmode = drmmode_crtc->drmmode;
+	int i;
+
+	if (!drmmode_cm_enabled(drmmode))
+		return;
+
+	/* Init CTM to identity. Values are in S31.32 fixed-point format */
+	drmmode_crtc->ctm = calloc(1, sizeof(*drmmode_crtc->ctm));
+	if (!drmmode_crtc->ctm) {
+		xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
+			   "Memory error initializing CTM for CRTC%d",
+			   drmmode_get_crtc_id(crtc));
+		return;
+	}
+
+	drmmode_crtc->ctm->matrix[0] = drmmode_crtc->ctm->matrix[4] =
+		drmmode_crtc->ctm->matrix[8] = (uint64_t)1 << 32;
+
+	/* Push properties to reset properties currently in hardware */
+	for (i = 0; i < CM_DEGAMMA_LUT_SIZE; i++) {
+		if (drmmode_crtc_push_cm_prop(crtc, i))
+			xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
+				   "Failed to initialize color management "
+				   "property %s on CRTC%d. Property value may "
+				   "not reflect actual hardware state.\n",
+				   cm_prop_names[i],
+				   drmmode_get_crtc_id(crtc));
+	}
+}
+
 static unsigned int
 drmmode_crtc_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, drmModeResPtr mode_res, int num)
 {
@@ -1374,6 +1517,8 @@ drmmode_crtc_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, drmModeResPtr mode_res
 	crtc->driver_private = drmmode_crtc;
 	drmmode_crtc_hw_id(crtc);
 
+	drmmode_crtc_cm_init(pAMDGPUEnt->fd, crtc);
+
 	/* Mark num'th crtc as in use on this device. */
 	pAMDGPUEnt->assigned_crtcs |= (1 << num);
 	xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG,
diff --git a/src/drmmode_display.h b/src/drmmode_display.h
index 4aa8e88..0f0227c 100644
--- a/src/drmmode_display.h
+++ b/src/drmmode_display.h
@@ -131,6 +131,9 @@ typedef struct {
 	unsigned present_vblank_msc;
 	Bool present_flip_expected;
 #endif
+	struct drm_color_lut *degamma_lut;
+	struct drm_color_ctm *ctm;
+	struct drm_color_lut *gamma_lut;
 } drmmode_crtc_private_rec, *drmmode_crtc_private_ptr;
 
 typedef struct {
commit 810ed133cd67b3deb38d1af87e252a094e9ee8f2
Author: Leo Li (Sunpeng) <sunpeng.li at amd.com>
Date:   Fri Jun 15 17:02:57 2018 -0400

    Cache color property IDs and LUT sizes during pre-init
    
    DRM creates property types with unique IDs during kernel driver init.
    Cache the color property IDs on DDX init for use later, when we need
    to modify these properties. Also cache the (de)gamma LUT sizes, since
    they are the same for all CRTCs on AMD hardware.
    
    Since these values are the same regardless of the CRTC, they can be
    cached within the private drmmode_rec object. We can also use any color-
    management-enabled CRTC to initially fetch them.
    
    Also introduce an enumeration of possible color management properties,
    to provide a easy and unified way of referring to them.
    
    v2:
    - Reorder cm property enum so that LUT sizes are at the end. This allows
      us to use DEGAMMA_LUT_SIZE as an anchor for iterating over mutable cm
      properties.
    - Cache (de)gamma LUT sizes within drmmode, since it's the same for all
      CRTCs on AMD hardware. Update commit message to reflect this.
    
    Signed-off-by: Leo (Sunpeng) Li <sunpeng.li at amd.com>
    Signed-off-by: Michel Dänzer <michel.daenzer at amd.com>

diff --git a/src/drmmode_display.c b/src/drmmode_display.c
index c55b4b7..916ebac 100644
--- a/src/drmmode_display.c
+++ b/src/drmmode_display.c
@@ -746,6 +746,28 @@ drmmode_crtc_scanout_update(xf86CrtcPtr crtc, DisplayModePtr mode,
 	}
 }
 
+static char *cm_prop_names[] = {
+	"DEGAMMA_LUT",
+	"CTM",
+	"GAMMA_LUT",
+	"DEGAMMA_LUT_SIZE",
+	"GAMMA_LUT_SIZE",
+};
+
+/**
+ * Return the enum of the color management property with the given name.
+ */
+static enum drmmode_cm_prop get_cm_enum_from_str(const char *prop_name)
+{
+	enum drmmode_cm_prop ret;
+
+	for (ret = 0; ret < CM_NUM_PROPS; ret++) {
+		if (!strcmp(prop_name, cm_prop_names[ret]))
+			return ret;
+	}
+	return CM_INVALID_PROP;
+}
+
 static void
 drmmode_crtc_gamma_do_set(xf86CrtcPtr crtc, uint16_t *red, uint16_t *green,
 			  uint16_t *blue, int size)
@@ -2421,6 +2443,80 @@ drmmode_page_flip_target_relative(AMDGPUEntPtr pAMDGPUEnt,
 				 drm_queue_seq);
 }
 
+/**
+ * Initialize DDX color management support. It does two things:
+ *
+ * 1. Cache DRM color management property type IDs, as they do not change. They
+ *    will be used later to modify color management via DRM, or to determine if
+ *    there's kernel support for color management.
+ *
+ * 2. Cache degamma/gamma LUT sizes, since all CRTCs have the same LUT sizes on
+ *    AMD hardware.
+ *
+ * If the cached ID's are all 0 after calling this function, then color
+ * management is not supported. For short, checking if the gamma LUT size
+ * property ID == 0 is sufficient.
+ *
+ * This should be called before CRTCs are initialized within pre_init, as the
+ * cached values will be used there.
+ *
+ * @drm_fd: DRM file descriptor
+ * @drmmode: drmmode object, where the cached IDs are stored
+ * @mode_res: The DRM mode resource containing the CRTC ids
+ */
+static void drmmode_cm_init(int drm_fd, drmmode_ptr drmmode,
+			    drmModeResPtr mode_res)
+{
+	drmModeObjectPropertiesPtr drm_props;
+	drmModePropertyPtr drm_prop;
+	enum drmmode_cm_prop cm_prop;
+	uint32_t cm_enabled = 0;
+	uint32_t cm_all_enabled = (1 << CM_NUM_PROPS) - 1;
+	int i;
+
+	memset(drmmode->cm_prop_ids, 0, sizeof(drmmode->cm_prop_ids));
+	drmmode->gamma_lut_size = drmmode->degamma_lut_size = 0;
+
+	/* AMD hardware has color management support on all pipes. It is
+	 * therefore sufficient to only check the first CRTC.
+	 */
+	drm_props = drmModeObjectGetProperties(drm_fd,
+					       mode_res->crtcs[0],
+					       DRM_MODE_OBJECT_CRTC);
+	if (!drm_props)
+		return;
+
+	for (i = 0; i < drm_props->count_props; i++) {
+		drm_prop = drmModeGetProperty(drm_fd,
+					      drm_props->props[i]);
+		if (!drm_prop)
+			continue;
+
+		cm_prop = get_cm_enum_from_str(drm_prop->name);
+		if (cm_prop == CM_INVALID_PROP)
+			continue;
+
+		if (cm_prop == CM_DEGAMMA_LUT_SIZE)
+			drmmode->degamma_lut_size = drm_props->prop_values[i];
+		else if (cm_prop == CM_GAMMA_LUT_SIZE)
+			drmmode->gamma_lut_size = drm_props->prop_values[i];
+
+		drmmode->cm_prop_ids[cm_prop] = drm_props->props[i];
+		cm_enabled |= 1 << cm_prop;
+
+		drmModeFreeProperty(drm_prop);
+	}
+	drmModeFreeObjectProperties(drm_props);
+
+	/* cm is enabled only if all prop ids are found */
+	if (cm_enabled == cm_all_enabled)
+		return;
+
+	/* Otherwise, disable DDX cm support */
+	memset(drmmode->cm_prop_ids, 0, sizeof(drmmode->cm_prop_ids));
+	drmmode->gamma_lut_size = drmmode->degamma_lut_size = 0;
+}
+
 Bool drmmode_pre_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int cpp)
 {
 	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
@@ -2467,6 +2563,8 @@ Bool drmmode_pre_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int cpp)
 	if (pScrn->depth == 30)
 		info->drmmode_crtc_funcs.gamma_set = NULL;
 
+	drmmode_cm_init(pAMDGPUEnt->fd, drmmode, mode_res);
+
 	for (i = 0; i < mode_res->count_crtcs; i++)
 		if (!xf86IsEntityShared(pScrn->entityList[0]) ||
 		    (crtcs_needed && !(pAMDGPUEnt->assigned_crtcs & (1 << i))))
diff --git a/src/drmmode_display.h b/src/drmmode_display.h
index 25ae9f8..4aa8e88 100644
--- a/src/drmmode_display.h
+++ b/src/drmmode_display.h
@@ -36,6 +36,22 @@
 #include "amdgpu_probe.h"
 #include "amdgpu.h"
 
+/*
+ * Enum of non-legacy color management properties, according to DRM. Note that
+ * the values should be incremental (with the exception of the INVALID member),
+ * as defined by C99. The ordering also matters. Some logics (such as iterators
+ * and bitmasks) depend on these facts.
+ */
+enum drmmode_cm_prop {
+	CM_DEGAMMA_LUT,
+	CM_CTM,
+	CM_GAMMA_LUT,
+	CM_DEGAMMA_LUT_SIZE,
+	CM_GAMMA_LUT_SIZE,
+	CM_NUM_PROPS,
+	CM_INVALID_PROP = -1,
+};
+
 typedef struct {
 	ScrnInfoPtr scrn;
 #ifdef HAVE_LIBUDEV
@@ -49,6 +65,12 @@ typedef struct {
 
 	Bool dri2_flipping;
 	Bool present_flipping;
+
+	/* Cache for DRM property type IDs for CRTC color management */
+	uint32_t cm_prop_ids[CM_NUM_PROPS];
+	/* Lookup table sizes */
+	uint32_t degamma_lut_size;
+	uint32_t gamma_lut_size;
 } drmmode_rec, *drmmode_ptr;
 
 typedef struct {


More information about the xorg-commit mailing list