[igt-dev] [PATCH i-g-t 2/2] tests/kms_color_chamelium: add subtests to validate color.

Kunal Joshi kunal1.joshi at intel.com
Tue Jan 14 13:35:04 UTC 2020


To validate color subtests using chamelium, subtests modified
to do frame dump comparison instead of crc comparison.
Tests require chamelium and will validate color features
at pipe level.

Signed-off-by: Kunal Joshi <kunal1.joshi at intel.com>
Signed-off-by: Swati Sharma <swati2.sharma at intel.com>
Suggested-by: Uma Shankar <uma.shankar at intel.com>
---
 tests/Makefile.am           |    1 +
 tests/kms_color_chamelium.c | 1119 +++++++++++++++++++++++++++++++++++++++++++
 tests/meson.build           |    1 +
 3 files changed, 1121 insertions(+)
 create mode 100644 tests/kms_color_chamelium.c

diff --git a/tests/Makefile.am b/tests/Makefile.am
index 9a320bc..1794569 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -13,6 +13,7 @@ endif
 if HAVE_CHAMELIUM
 TESTS_progs += \
 	kms_chamelium \
+	kms_color_chamelium \
 	$(NULL)
 endif
 
diff --git a/tests/kms_color_chamelium.c b/tests/kms_color_chamelium.c
new file mode 100644
index 0000000..0c87bf5
--- /dev/null
+++ b/tests/kms_color_chamelium.c
@@ -0,0 +1,1119 @@
+/*
+ * Copyright © 2020 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 <math.h>
+#include <unistd.h>
+
+#include "drm.h"
+#include "drmtest.h"
+#include "igt.h"
+
+IGT_TEST_DESCRIPTION("Test Color Features at Pipe level");
+
+/* Internal */
+typedef struct {
+	double r, g, b;
+} color_t;
+
+typedef struct {
+	int drm_fd;
+	uint32_t devid;
+	igt_display_t display;
+
+	uint32_t color_depth;
+	uint64_t degamma_lut_size;
+	uint64_t gamma_lut_size;
+
+	struct chamelium *chamelium;
+	struct chamelium_port **ports;
+	int port_count;
+} data_t;
+
+typedef struct {
+	int size;
+	double coeffs[];
+} gamma_lut_t;
+
+static void paint_gradient_rectangles(data_t *data,
+				      drmModeModeInfo *mode,
+				      color_t *colors,
+				      struct igt_fb *fb)
+{
+	cairo_t *cr = igt_get_cairo_ctx(data->drm_fd, fb);
+	int i, l = mode->hdisplay / 3;
+	int rows_remaining = mode->hdisplay % 3;
+
+	/* Paint 3 gradient rectangles with red/green/blue between 1.0 and
+	 * 0.5. We want to avoid 0 so each max LUTs only affect their own
+	 * rectangle.
+	 */
+	for (i = 0 ; i < 3; i++) {
+		igt_paint_color_gradient_range(cr, i * l, 0, l, mode->vdisplay,
+					       colors[i].r != 0 ? 0.2 : 0,
+					       colors[i].g != 0 ? 0.2 : 0,
+					       colors[i].b != 0 ? 0.2 : 0,
+					       colors[i].r,
+					       colors[i].g,
+					       colors[i].b);
+	}
+
+	if (rows_remaining > 0)
+		igt_paint_color_gradient_range(cr, i * l, 0, rows_remaining,
+					       mode->vdisplay,
+					       colors[i-1].r != 0 ? 0.2 : 0,
+					       colors[i-1].g != 0 ? 0.2 : 0,
+					       colors[i-1].b != 0 ? 0.2 : 0,
+					       colors[i-1].r,
+					       colors[i-1].g,
+					       colors[i-1].b);
+
+	igt_put_cairo_ctx(data->drm_fd, fb, cr);
+}
+
+static void paint_rectangles(data_t *data,
+			     drmModeModeInfo *mode,
+			     color_t *colors,
+			     struct igt_fb *fb)
+{
+	cairo_t *cr = igt_get_cairo_ctx(data->drm_fd, fb);
+	int i, l = mode->hdisplay / 3;
+	int rows_remaining = mode->hdisplay % 3;
+
+	/* Paint 3 solid rectangles. */
+	for (i = 0 ; i < 3; i++) {
+		igt_paint_color(cr, i * l, 0, l, mode->vdisplay,
+				colors[i].r, colors[i].g, colors[i].b);
+	}
+
+	if (rows_remaining > 0)
+		igt_paint_color(cr, i * l, 0, rows_remaining, mode->vdisplay,
+				colors[i-1].r, colors[i-1].g, colors[i-1].b);
+
+	igt_put_cairo_ctx(data->drm_fd, fb, cr);
+}
+
+static gamma_lut_t *alloc_lut(int lut_size)
+{
+	gamma_lut_t *gamma;
+
+	igt_assert_lt(0, lut_size);
+
+	gamma = malloc(sizeof(*gamma) + lut_size * sizeof(gamma->coeffs[0]));
+	igt_assert(gamma);
+	gamma->size = lut_size;
+
+	return gamma;
+}
+
+static void free_lut(gamma_lut_t *gamma)
+{
+	if (!gamma)
+		return;
+
+	free(gamma);
+}
+
+static gamma_lut_t *generate_table(int lut_size, double exp)
+{
+	gamma_lut_t *gamma = alloc_lut(lut_size);
+	int i;
+
+	gamma->coeffs[0] = 0.0;
+	for (i = 1; i < lut_size; i++)
+		gamma->coeffs[i] = pow(i * 1.0 / (lut_size - 1), exp);
+
+	return gamma;
+}
+
+static gamma_lut_t *generate_table_max(int lut_size)
+{
+	gamma_lut_t *gamma = alloc_lut(lut_size);
+	int i;
+
+	gamma->coeffs[0] = 0.0;
+	for (i = 1; i < lut_size; i++)
+		gamma->coeffs[i] = 1.0;
+
+	return gamma;
+}
+
+static struct drm_color_lut *coeffs_to_lut(data_t *data,
+					   const gamma_lut_t *gamma,
+					   uint32_t color_depth,
+					   int off)
+{
+	struct drm_color_lut *lut;
+	int i, lut_size = gamma->size;
+	uint32_t max_value = (1 << 16) - 1;
+	uint32_t mask;
+
+	if (is_i915_device(data->drm_fd))
+		mask = ((1 << color_depth) - 1) << 8;
+	else
+		mask = max_value;
+
+	lut = malloc(sizeof(struct drm_color_lut) * lut_size);
+
+	if (IS_CHERRYVIEW(data->devid))
+		lut_size -= 1;
+	for (i = 0; i < lut_size; i++) {
+		uint32_t v = (gamma->coeffs[i] * max_value);
+
+		v &= mask;
+
+		lut[i].red = v;
+		lut[i].green = v;
+		lut[i].blue = v;
+	}
+
+	if (IS_CHERRYVIEW(data->devid))
+		lut[lut_size].red =
+			lut[lut_size].green =
+			lut[lut_size].blue = lut[lut_size - 1].red;
+
+	return lut;
+}
+
+static void set_degamma(data_t *data,
+			igt_pipe_t *pipe,
+			const gamma_lut_t *gamma)
+{
+	size_t size = sizeof(struct drm_color_lut) * gamma->size;
+	struct drm_color_lut *lut = coeffs_to_lut(data, gamma,
+						  data->color_depth, 0);
+
+	free(lut);
+}
+
+static void set_gamma(data_t *data,
+		      igt_pipe_t *pipe,
+		      const gamma_lut_t *gamma)
+{
+	size_t size = sizeof(struct drm_color_lut) * gamma->size;
+	struct drm_color_lut *lut = coeffs_to_lut(data, gamma,
+						  data->color_depth, 0);
+
+	igt_pipe_obj_replace_prop_blob(pipe, IGT_CRTC_GAMMA_LUT, lut, size);
+
+	free(lut);
+}
+
+static void set_ctm(igt_pipe_t *pipe, const double *coefficients)
+{
+	struct drm_color_ctm ctm;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ctm.matrix); i++) {
+		if (coefficients[i] < 0) {
+			ctm.matrix[i] =
+				(int64_t) (-coefficients[i] *
+				((int64_t) 1L << 32));
+			ctm.matrix[i] |= 1ULL << 63;
+		} else
+			ctm.matrix[i] =
+				(int64_t) (coefficients[i] *
+				((int64_t) 1L << 32));
+	}
+
+	igt_pipe_obj_replace_prop_blob(pipe, IGT_CRTC_CTM, &ctm, sizeof(ctm));
+}
+
+static void disable_prop(igt_pipe_t *pipe, enum igt_atomic_crtc_properties prop)
+{
+	if (igt_pipe_obj_has_prop(pipe, prop))
+		igt_pipe_obj_replace_prop_blob(pipe, prop, NULL, 0);
+}
+
+#define disable_degamma(pipe) disable_prop(pipe, IGT_CRTC_DEGAMMA_LUT)
+#define disable_gamma(pipe) disable_prop(pipe, IGT_CRTC_GAMMA_LUT)
+#define disable_ctm(pipe) disable_prop(pipe, IGT_CRTC_CTM)
+
+/*
+ * Draw 3 gradient rectangles in red, green and blue, with a maxed out
+ * degamma LUT and verify we have the same frame dump as drawing solid color
+ * rectangles with linear degamma LUT.
+ */
+static void test_pipe_degamma(data_t *data,
+			      igt_plane_t *primary)
+{
+	igt_output_t *output;
+	gamma_lut_t *degamma_linear, *degamma_full;
+	gamma_lut_t *gamma_linear;
+	color_t red_green_blue[] = {
+		{ 1.0, 0.0, 0.0 },
+		{ 0.0, 1.0, 0.0 },
+		{ 0.0, 0.0, 1.0 }
+	};
+
+	int i;
+	struct chamelium_port *port;
+	char *connected_ports[4];
+	bool valid_output = false;
+
+	for (i = 0; i < data->port_count; i++)
+		connected_ports[i] =
+			(char *) chamelium_port_get_name(data->ports[i]);
+
+	igt_require(igt_pipe_obj_has_prop(primary->pipe, IGT_CRTC_DEGAMMA_LUT));
+	igt_require(igt_pipe_obj_has_prop(primary->pipe, IGT_CRTC_GAMMA_LUT));
+
+	degamma_linear = generate_table(data->degamma_lut_size, 1.0);
+	degamma_full = generate_table_max(data->degamma_lut_size);
+
+	gamma_linear = generate_table(data->gamma_lut_size, 1.0);
+
+	for_each_valid_output_on_pipe(&data->display,
+				      primary->pipe->pipe,
+				      output) {
+		drmModeModeInfo *mode;
+		struct igt_fb fb_modeset, fb;
+		struct chamelium_frame_dump *frame_fullgamma, *frame_fullcolors;
+		int fb_id, fb_modeset_id;
+
+		for (i = 0; i < data->port_count; i++)
+			valid_output |=
+				(strcmp(output->name, connected_ports[i]) == 0);
+		if (!valid_output)
+			continue;
+		else
+			for (i = 0; i < data->port_count; i++)
+				if (strcmp(output->name,
+					   connected_ports[i]) == 0)
+					port = data->ports[i];
+
+		igt_output_set_pipe(output, primary->pipe->pipe);
+		mode = igt_output_get_mode(output);
+
+		/* Create a framebuffer at the size of the output. */
+		fb_id = igt_create_fb(data->drm_fd,
+				      mode->hdisplay,
+				      mode->vdisplay,
+				      DRM_FORMAT_XRGB8888,
+				      LOCAL_DRM_FORMAT_MOD_NONE,
+				      &fb);
+		igt_assert(fb_id);
+
+		fb_modeset_id = igt_create_fb(data->drm_fd,
+					      mode->hdisplay,
+					      mode->vdisplay,
+					      DRM_FORMAT_XRGB8888,
+					      LOCAL_DRM_FORMAT_MOD_NONE,
+					      &fb_modeset);
+		igt_assert(fb_modeset_id);
+
+		igt_plane_set_fb(primary, &fb_modeset);
+		disable_ctm(primary->pipe);
+		disable_degamma(primary->pipe);
+		set_gamma(data, primary->pipe, gamma_linear);
+		igt_display_commit(&data->display);
+
+		/* Draw solid colors with no degamma transformation. */
+		paint_rectangles(data, mode, red_green_blue, &fb);
+		igt_plane_set_fb(primary, &fb);
+		igt_display_commit(&data->display);
+		chamelium_capture(data->chamelium, port, 0, 0, 0, 0, 1);
+		frame_fullgamma =
+			chamelium_read_captured_frame(data->chamelium, 0);
+
+		/* Draw a gradient with degamma LUT to remap all
+		 * values to max red/green/blue.
+		 */
+		paint_gradient_rectangles(data, mode, red_green_blue, &fb);
+		igt_plane_set_fb(primary, &fb);
+		set_degamma(data, primary->pipe, degamma_full);
+		igt_display_commit(&data->display);
+		chamelium_capture(data->chamelium, port, 0, 0, 0, 0, 1);
+		frame_fullcolors =
+			chamelium_read_captured_frame(data->chamelium, 0);
+
+		/* Verify that the frame dump of the software computed output
+		 * is equal to the frame dump of the degamma LUT transformation
+		 * output.
+		 */
+		igt_assert(chamelium_assert_frame_dump_eq(data->chamelium,
+							  frame_fullgamma,
+							  frame_fullcolors));
+
+		igt_plane_set_fb(primary, NULL);
+		igt_output_set_pipe(output, PIPE_NONE);
+	}
+
+	free_lut(degamma_linear);
+	free_lut(degamma_full);
+	free_lut(gamma_linear);
+}
+
+/*
+ * Draw 3 gradient rectangles in red, green and blue, with a maxed out
+ * gamma LUT and verify we have the same frame dump as drawing solid
+ * color rectangles.
+ */
+static void test_pipe_gamma(data_t *data,
+			    igt_plane_t *primary)
+{
+	igt_output_t *output;
+	gamma_lut_t *gamma_full;
+	color_t red_green_blue[] = {
+		{ 1.0, 0.0, 0.0 },
+		{ 0.0, 1.0, 0.0 },
+		{ 0.0, 0.0, 1.0 }
+	};
+
+	int i;
+	struct chamelium_port *port;
+	char *connected_ports[4];
+	bool valid_output = false;
+
+	for (i = 0; i < data->port_count; i++)
+		connected_ports[i] =
+			(char *) chamelium_port_get_name(data->ports[i]);
+
+	igt_require(igt_pipe_obj_has_prop(primary->pipe, IGT_CRTC_GAMMA_LUT));
+
+	gamma_full = generate_table_max(data->gamma_lut_size);
+
+	for_each_valid_output_on_pipe(&data->display,
+				      primary->pipe->pipe,
+				      output) {
+		drmModeModeInfo *mode;
+		struct igt_fb fb_modeset, fb;
+		struct chamelium_frame_dump *frame_fullgamma, *frame_fullcolors;
+		int fb_id, fb_modeset_id;
+
+		for (i = 0; i < data->port_count; i++)
+			valid_output |=
+				(strcmp(output->name, connected_ports[i]) == 0);
+		if (!valid_output)
+			continue;
+		else
+			for (i = 0; i < data->port_count; i++)
+				if (strcmp(output->name,
+					   connected_ports[i]) == 0)
+					port = data->ports[i];
+
+		igt_output_set_pipe(output, primary->pipe->pipe);
+		mode = igt_output_get_mode(output);
+
+		/* Create a framebuffer at the size of the output. */
+		fb_id = igt_create_fb(data->drm_fd,
+				      mode->hdisplay,
+				      mode->vdisplay,
+				      DRM_FORMAT_XRGB8888,
+				      LOCAL_DRM_FORMAT_MOD_NONE,
+				      &fb);
+		igt_assert(fb_id);
+
+		fb_modeset_id = igt_create_fb(data->drm_fd,
+					      mode->hdisplay,
+					      mode->vdisplay,
+					      DRM_FORMAT_XRGB8888,
+					      LOCAL_DRM_FORMAT_MOD_NONE,
+					      &fb_modeset);
+		igt_assert(fb_modeset_id);
+
+		igt_plane_set_fb(primary, &fb_modeset);
+		disable_ctm(primary->pipe);
+		disable_degamma(primary->pipe);
+		set_gamma(data, primary->pipe, gamma_full);
+		igt_display_commit(&data->display);
+
+		/* Draw solid colors with no gamma transformation. */
+		paint_rectangles(data, mode, red_green_blue, &fb);
+		igt_plane_set_fb(primary, &fb);
+		igt_display_commit(&data->display);
+		chamelium_capture(data->chamelium, port, 0, 0, 0, 0, 1);
+		frame_fullgamma =
+			chamelium_read_captured_frame(data->chamelium, 0);
+
+		/* Draw a gradient with gamma LUT to remap all values
+		 * to max red/green/blue.
+		 */
+		paint_gradient_rectangles(data, mode, red_green_blue, &fb);
+		igt_plane_set_fb(primary, &fb);
+		igt_display_commit(&data->display);
+		chamelium_capture(data->chamelium, port, 0, 0, 0, 0, 1);
+		frame_fullcolors =
+			chamelium_read_captured_frame(data->chamelium, 0);
+
+		/* Verify that the frame dump of the software computed output is
+		 * equal to the frame dump of the degamma LUT transformation
+		 * output.
+		 */
+		igt_assert(chamelium_assert_frame_dump_eq(data->chamelium,
+							  frame_fullgamma,
+							  frame_fullcolors));
+
+		igt_plane_set_fb(primary, NULL);
+		igt_output_set_pipe(output, PIPE_NONE);
+	}
+
+	free_lut(gamma_full);
+}
+
+/*
+ * Draw 3 rectangles using before colors with the ctm matrix apply and verify
+ * the frame dump is equal to using after colors with an identify ctm matrix.
+ */
+static bool test_pipe_ctm(data_t *data,
+			  igt_plane_t *primary,
+			  color_t *before,
+			  color_t *after,
+			  double *ctm_matrix)
+{
+	const double ctm_identity[] = {
+		1.0, 0.0, 0.0,
+		0.0, 1.0, 0.0,
+		0.0, 0.0, 1.0
+	};
+	gamma_lut_t *degamma_linear, *gamma_linear;
+	igt_output_t *output;
+
+	int i;
+	bool ret = true;
+	struct chamelium_port *port;
+	char *connected_ports[4];
+	bool valid_output = false;
+
+	igt_require(igt_pipe_obj_has_prop(primary->pipe, IGT_CRTC_CTM));
+
+	for (i = 0; i < data->port_count; i++)
+		connected_ports[i] =
+			(char *) chamelium_port_get_name(data->ports[i]);
+
+	degamma_linear = generate_table(data->degamma_lut_size, 1.0);
+	gamma_linear = generate_table(data->gamma_lut_size, 1.0);
+
+	for_each_valid_output_on_pipe(&data->display,
+				      primary->pipe->pipe,
+				      output) {
+		drmModeModeInfo *mode;
+		struct igt_fb fb_modeset, fb;
+		struct chamelium_frame_dump *frame_software, *frame_hardware;
+		int fb_id, fb_modeset_id;
+
+		for (i = 0; i < data->port_count; i++)
+			valid_output |=
+				(strcmp(output->name, connected_ports[i]) == 0);
+		if (!valid_output)
+			continue;
+		else
+			for (i = 0; i < data->port_count; i++)
+				if (strcmp(output->name,
+					   connected_ports[i]) == 0)
+					port = data->ports[i];
+
+		igt_output_set_pipe(output, primary->pipe->pipe);
+		mode = igt_output_get_mode(output);
+
+		/* Create a framebuffer at the size of the output. */
+		fb_id = igt_create_fb(data->drm_fd,
+				      mode->hdisplay,
+				      mode->vdisplay,
+				      DRM_FORMAT_XRGB8888,
+				      LOCAL_DRM_FORMAT_MOD_NONE,
+				      &fb);
+		igt_assert(fb_id);
+
+		fb_modeset_id = igt_create_fb(data->drm_fd,
+					      mode->hdisplay,
+					      mode->vdisplay,
+					      DRM_FORMAT_XRGB8888,
+					      LOCAL_DRM_FORMAT_MOD_NONE,
+					      &fb_modeset);
+		igt_assert(fb_modeset_id);
+		igt_plane_set_fb(primary, &fb_modeset);
+
+		if (memcmp(before, after, sizeof(color_t))) {
+			set_degamma(data, primary->pipe, degamma_linear);
+			set_gamma(data, primary->pipe, gamma_linear);
+		} else {
+			/* Disable Degamma and Gamma for ctm max test */
+			disable_degamma(primary->pipe);
+			disable_gamma(primary->pipe);
+		}
+
+		disable_ctm(primary->pipe);
+		igt_display_commit(&data->display);
+
+		paint_rectangles(data, mode, after, &fb);
+		igt_plane_set_fb(primary, &fb);
+		set_ctm(primary->pipe, ctm_identity);
+		igt_display_commit(&data->display);
+		chamelium_capture(data->chamelium, port, 0, 0, 0, 0, 1);
+		frame_software =
+			chamelium_read_captured_frame(data->chamelium, 0);
+
+		/* With CTM transformation. */
+		paint_rectangles(data, mode, before, &fb);
+		igt_plane_set_fb(primary, &fb);
+		set_ctm(primary->pipe, ctm_matrix);
+		igt_display_commit(&data->display);
+		chamelium_capture(data->chamelium, port, 0, 0, 0, 0, 1);
+		frame_hardware =
+			chamelium_read_captured_frame(data->chamelium, 0);
+
+		/* Verify that the frame dump of the software computed output is
+		 * equal to the frame dump of the CTM matrix transformation
+		 * output.
+		 */
+		ret &= chamelium_assert_frame_dump_eq(data->chamelium,
+						      frame_software,
+						      frame_hardware);
+
+		igt_plane_set_fb(primary, NULL);
+		igt_output_set_pipe(output, PIPE_NONE);
+	}
+
+	free_lut(degamma_linear);
+	free_lut(gamma_linear);
+
+	return ret;
+}
+
+static void test_pipe_limited_range_ctm(data_t *data,
+					igt_plane_t *primary)
+{
+	double limited_result = 235.0 / 255.0;
+	color_t red_green_blue_limited[] = {
+		{ limited_result, 0.0, 0.0 },
+		{ 0.0, limited_result, 0.0 },
+		{ 0.0, 0.0, limited_result }
+	};
+	color_t red_green_blue_full[] = {
+		{ 0.5, 0.0, 0.0 },
+		{ 0.0, 0.5, 0.0 },
+		{ 0.0, 0.0, 0.5 }
+	};
+	double ctm[] = { 1.0, 0.0, 0.0,
+			0.0, 1.0, 0.0,
+			0.0, 0.0, 1.0 };
+	gamma_lut_t *degamma_linear, *gamma_linear;
+	igt_output_t *output;
+	bool has_broadcast_rgb_output = false;
+
+	int i;
+	struct chamelium_port *port;
+	char *connected_ports[4];
+	bool valid_output = false;
+
+	degamma_linear = generate_table(data->degamma_lut_size, 1.0);
+	gamma_linear = generate_table(data->gamma_lut_size, 1.0);
+
+	for (i = 0; i < data->port_count; i++)
+		connected_ports[i] =
+			(char *) chamelium_port_get_name(data->ports[i]);
+
+	for_each_valid_output_on_pipe(&data->display,
+				      primary->pipe->pipe,
+				      output) {
+		drmModeModeInfo *mode;
+		struct igt_fb fb_modeset, fb;
+		struct chamelium_frame_dump *frame_full, *frame_limited;
+		int fb_id, fb_modeset_id;
+
+		for (i = 0; i < data->port_count; i++)
+			valid_output |=
+				(strcmp(output->name, connected_ports[i]) == 0);
+		if (!valid_output)
+			continue;
+		else
+			for (i = 0; i < data->port_count; i++)
+				if (strcmp(output->name,
+				    connected_ports[i]) == 0)
+					port = data->ports[i];
+
+		if (!igt_output_has_prop(output, IGT_CONNECTOR_BROADCAST_RGB))
+			continue;
+
+		has_broadcast_rgb_output = true;
+
+		igt_output_set_pipe(output, primary->pipe->pipe);
+		mode = igt_output_get_mode(output);
+
+		/* Create a framebuffer at the size of the output. */
+		fb_id = igt_create_fb(data->drm_fd,
+				      mode->hdisplay,
+				      mode->vdisplay,
+				      DRM_FORMAT_XRGB8888,
+				      LOCAL_DRM_FORMAT_MOD_NONE,
+				      &fb);
+		igt_assert(fb_id);
+
+		fb_modeset_id = igt_create_fb(data->drm_fd,
+					      mode->hdisplay,
+					      mode->vdisplay,
+					      DRM_FORMAT_XRGB8888,
+					      LOCAL_DRM_FORMAT_MOD_NONE,
+					      &fb_modeset);
+		igt_assert(fb_modeset_id);
+		igt_plane_set_fb(primary, &fb_modeset);
+
+		set_degamma(data, primary->pipe, degamma_linear);
+		set_gamma(data, primary->pipe, gamma_linear);
+		set_ctm(primary->pipe, ctm);
+
+		igt_output_set_prop_value(output,
+					  IGT_CONNECTOR_BROADCAST_RGB,
+					  BROADCAST_RGB_FULL);
+		paint_rectangles(data, mode, red_green_blue_limited, &fb);
+		igt_plane_set_fb(primary, &fb);
+		igt_display_commit(&data->display);
+
+		/* Set the output into limited range. */
+		igt_output_set_prop_value(output,
+					  IGT_CONNECTOR_BROADCAST_RGB,
+					  BROADCAST_RGB_16_235);
+		paint_rectangles(data, mode, red_green_blue_full, &fb);
+		igt_plane_set_fb(primary, &fb);
+		igt_display_commit(&data->display);
+		chamelium_capture(data->chamelium, port, 0, 0, 0, 0, 1);
+		frame_full =
+			chamelium_read_captured_frame(data->chamelium, 0);
+
+
+		/* And reset.. */
+		igt_output_set_prop_value(output,
+					  IGT_CONNECTOR_BROADCAST_RGB,
+					  BROADCAST_RGB_FULL);
+		igt_plane_set_fb(primary, NULL);
+		igt_output_set_pipe(output, PIPE_NONE);
+		chamelium_capture(data->chamelium, port, 0, 0, 0, 0, 1);
+		frame_limited =
+			chamelium_read_captured_frame(data->chamelium, 0);
+
+
+		/* Verify that the frame dump of the software computed output is
+		 * equal to the frame dump of the CTM matrix transformation
+		 * output.
+		 */
+		igt_assert(chamelium_assert_frame_dump_eq(data->chamelium,
+							  frame_full,
+							  frame_limited));
+	}
+
+	free_lut(gamma_linear);
+	free_lut(degamma_linear);
+
+	igt_require(has_broadcast_rgb_output);
+}
+
+static void
+run_tests_for_pipe(data_t *data, enum pipe p)
+{
+	igt_pipe_t *pipe;
+	igt_plane_t *primary;
+	double delta;
+	int i;
+	color_t red_green_blue[] = {
+		{ 1.0, 0.0, 0.0 },
+		{ 0.0, 1.0, 0.0 },
+		{ 0.0, 0.0, 1.0 }
+	};
+
+	igt_fixture {
+
+		igt_require(p < data->display.n_pipes);
+
+		pipe = &data->display.pipes[p];
+		igt_require(pipe->n_planes >= 0);
+
+		primary = igt_pipe_get_plane_type(pipe, DRM_PLANE_TYPE_PRIMARY);
+
+		if (igt_pipe_obj_has_prop(&data->display.pipes[p],
+					  IGT_CRTC_DEGAMMA_LUT_SIZE)) {
+			data->degamma_lut_size =
+				igt_pipe_obj_get_prop(&data->display.pipes[p],
+						IGT_CRTC_DEGAMMA_LUT_SIZE);
+			igt_assert_lt(0, data->degamma_lut_size);
+		}
+
+		if (igt_pipe_obj_has_prop(&data->display.pipes[p],
+					  IGT_CRTC_GAMMA_LUT_SIZE)) {
+			data->gamma_lut_size =
+				igt_pipe_obj_get_prop(&data->display.pipes[p],
+						      IGT_CRTC_GAMMA_LUT_SIZE);
+			igt_assert_lt(0, data->gamma_lut_size);
+		}
+
+		igt_display_require_output_on_pipe(&data->display, p);
+	}
+
+	data->color_depth = 8;
+	delta = 1.0 / (1 << data->color_depth);
+
+	igt_describe("Compare after applying ctm matrix & identity matrix");
+	igt_subtest_f("pipe-%s-ctm-red-to-blue", kmstest_pipe_name(p)) {
+		color_t blue_green_blue[] = {
+			{ 0.0, 0.0, 1.0 },
+			{ 0.0, 1.0, 0.0 },
+			{ 0.0, 0.0, 1.0 }
+		};
+		double ctm[] = { 0.0, 0.0, 0.0,
+				0.0, 1.0, 0.0,
+				1.0, 0.0, 1.0 };
+		igt_assert(test_pipe_ctm(data, primary, red_green_blue,
+					 blue_green_blue, ctm));
+	}
+
+	igt_describe("Compare after applying ctm matrix & identity matrix");
+	igt_subtest_f("pipe-%s-ctm-green-to-red", kmstest_pipe_name(p)) {
+		color_t red_red_blue[] = {
+			{ 1.0, 0.0, 0.0 },
+			{ 1.0, 0.0, 0.0 },
+			{ 0.0, 0.0, 1.0 }
+		};
+		double ctm[] = { 1.0, 1.0, 0.0,
+				0.0, 0.0, 0.0,
+				0.0, 0.0, 1.0 };
+		igt_assert(test_pipe_ctm(data, primary, red_green_blue,
+					 red_red_blue, ctm));
+	}
+
+	igt_describe("Compare after applying ctm matrix & identity matrix");
+	igt_subtest_f("pipe-%s-ctm-blue-to-red", kmstest_pipe_name(p)) {
+		color_t red_green_red[] = {
+			{ 1.0, 0.0, 0.0 },
+			{ 0.0, 1.0, 0.0 },
+			{ 1.0, 0.0, 0.0 }
+		};
+		double ctm[] = { 1.0, 0.0, 1.0,
+				0.0, 1.0, 0.0,
+				0.0, 0.0, 0.0 };
+		igt_assert(test_pipe_ctm(data, primary, red_green_blue,
+					 red_green_red, ctm));
+	}
+
+	/* We tests a few values around the expected result because
+	 * the it depends on the hardware we're dealing with, we can
+	 * either get clamped or rounded values and we also need to
+	 * account for odd number of items in the LUTs.
+	 */
+	igt_describe("Compare after applying ctm matrix & identity matrix");
+	igt_subtest_f("pipe-%s-ctm-0-25", kmstest_pipe_name(p)) {
+		color_t expected_colors[] = {
+			{ 0.0, }, { 0.0, }, { 0.0, }
+		};
+		double ctm[] = { 0.25, 0.0,  0.0,
+				 0.0,  0.25, 0.0,
+				 0.0,  0.0,  0.25 };
+		bool success = false;
+
+		for (i = 0; i < 5; i++) {
+			expected_colors[0].r =
+				expected_colors[1].g =
+				expected_colors[2].b =
+				0.25 + delta * (i - 2);
+			success |= test_pipe_ctm(data, primary,
+						 red_green_blue,
+						 expected_colors, ctm);
+		}
+		igt_assert(success);
+	}
+
+	igt_describe("Compare after applying ctm matrix & identity matrix");
+	igt_subtest_f("pipe-%s-ctm-0-5", kmstest_pipe_name(p)) {
+		color_t expected_colors[] = {
+			{ 0.0, }, { 0.0, }, { 0.0, }
+		};
+		double ctm[] = { 0.5, 0.0, 0.0,
+				 0.0, 0.5, 0.0,
+				 0.0, 0.0, 0.5 };
+		bool success = false;
+
+		for (i = 0; i < 5; i++) {
+			expected_colors[0].r =
+				expected_colors[1].g =
+				expected_colors[2].b =
+				0.5 + delta * (i - 2);
+			success |= test_pipe_ctm(data, primary,
+						 red_green_blue,
+						 expected_colors, ctm);
+		}
+		igt_assert(success);
+	}
+
+	igt_describe("Compare after applying ctm matrix & identity matrix");
+	igt_subtest_f("pipe-%s-ctm-0-75", kmstest_pipe_name(p)) {
+		color_t expected_colors[] = {
+			{ 0.0, }, { 0.0, }, { 0.0, }
+		};
+		double ctm[] = { 0.75, 0.0,  0.0,
+				 0.0,  0.75, 0.0,
+				 0.0,  0.0,  0.75 };
+		bool success = false;
+
+		for (i = 0; i < 7; i++) {
+			expected_colors[0].r =
+				expected_colors[1].g =
+				expected_colors[2].b =
+				0.75 + delta * (i - 3);
+			success |= test_pipe_ctm(data, primary,
+						 red_green_blue,
+						 expected_colors, ctm);
+		}
+		igt_assert(success);
+	}
+
+	igt_describe("Compare after applying ctm matrix & identity matrix");
+	igt_subtest_f("pipe-%s-ctm-max", kmstest_pipe_name(p)) {
+		color_t full_rgb[] = {
+			{ 1.0, 0.0, 0.0 },
+			{ 0.0, 1.0, 0.0 },
+			{ 0.0, 0.0, 1.0 }
+		};
+		double ctm[] = { 100.0,   0.0,   0.0,
+				 0.0,   100.0,   0.0,
+				 0.0,     0.0, 100.0 };
+
+		/* CherryView generates values on 10bits that we
+		 * produce with an 8 bits per color framebuffer.
+		 */
+		igt_require(!IS_CHERRYVIEW(data->devid));
+
+		igt_assert(test_pipe_ctm(data, primary, red_green_blue,
+					 full_rgb, ctm));
+	}
+
+	igt_describe("Compare after applying ctm matrix & identity matrix");
+	igt_subtest_f("pipe-%s-ctm-negative", kmstest_pipe_name(p)) {
+		color_t all_black[] = {
+			{ 0.0, 0.0, 0.0 },
+			{ 0.0, 0.0, 0.0 },
+			{ 0.0, 0.0, 0.0 }
+		};
+		double ctm[] = { -1.0,  0.0,  0.0,
+				 0.0, -1.0,  0.0,
+				 0.0,  0.0, -1.0 };
+		igt_assert(test_pipe_ctm(data, primary, red_green_blue,
+					 all_black, ctm));
+	}
+
+	igt_describe("Compare after applying ctm matrix & identity matrix");
+	igt_subtest_f("pipe-%s-ctm-limited-range", kmstest_pipe_name(p))
+		test_pipe_limited_range_ctm(data, primary);
+
+	igt_describe("Compare maxed out gamma LUT and solid color linear LUT");
+	igt_subtest_f("pipe-%s-degamma", kmstest_pipe_name(p))
+		test_pipe_degamma(data, primary);
+
+	igt_describe("Compare maxed out gamma LUT and solid color linear LUT");
+	igt_subtest_f("pipe-%s-gamma", kmstest_pipe_name(p))
+		test_pipe_gamma(data, primary);
+
+	igt_fixture {
+		disable_degamma(primary->pipe);
+		disable_gamma(primary->pipe);
+		disable_ctm(primary->pipe);
+		igt_display_commit(&data->display);
+	}
+}
+
+static int
+pipe_set_property_blob_id(igt_pipe_t *pipe,
+			  enum igt_atomic_crtc_properties prop,
+			  uint32_t blob_id)
+{
+	int ret;
+
+	igt_pipe_obj_replace_prop_blob(pipe, prop, NULL, 0);
+
+	igt_pipe_obj_set_prop_value(pipe, prop, blob_id);
+
+	ret = igt_display_try_commit2(pipe->display,
+				      pipe->display->is_atomic ?
+				      COMMIT_ATOMIC : COMMIT_LEGACY);
+
+	igt_pipe_obj_set_prop_value(pipe, prop, 0);
+
+	return ret;
+}
+
+static int
+pipe_set_property_blob(igt_pipe_t *pipe,
+		       enum igt_atomic_crtc_properties prop,
+		       void *ptr, size_t length)
+{
+	igt_pipe_obj_replace_prop_blob(pipe, prop, ptr, length);
+
+	return igt_display_try_commit2(pipe->display,
+				       pipe->display->is_atomic ?
+				       COMMIT_ATOMIC : COMMIT_LEGACY);
+}
+
+static void
+invalid_gamma_lut_sizes(data_t *data)
+{
+	igt_display_t *display = &data->display;
+	igt_pipe_t *pipe = &display->pipes[0];
+	size_t gamma_lut_size = data->gamma_lut_size *
+				sizeof(struct drm_color_lut);
+	struct drm_color_lut *gamma_lut;
+
+	igt_require(igt_pipe_obj_has_prop(pipe, IGT_CRTC_GAMMA_LUT));
+
+	gamma_lut = malloc(gamma_lut_size * 2);
+
+	igt_display_commit2(display,
+			    display->is_atomic ?
+			    COMMIT_ATOMIC : COMMIT_LEGACY);
+
+	igt_assert_eq(pipe_set_property_blob(pipe,
+					     IGT_CRTC_GAMMA_LUT,
+					     gamma_lut, 1),
+					     -EINVAL);
+	igt_assert_eq(pipe_set_property_blob(pipe,
+					     IGT_CRTC_GAMMA_LUT,
+					     gamma_lut, gamma_lut_size + 1),
+					     -EINVAL);
+	igt_assert_eq(pipe_set_property_blob(pipe,
+					     IGT_CRTC_GAMMA_LUT,
+					     gamma_lut,
+					     gamma_lut_size - 1),
+					     -EINVAL);
+	igt_assert_eq(pipe_set_property_blob(pipe,
+					     IGT_CRTC_GAMMA_LUT,
+					     gamma_lut,
+					     gamma_lut_size +
+					     sizeof(struct drm_color_lut)),
+					     -EINVAL);
+	igt_assert_eq(pipe_set_property_blob_id(pipe,
+						IGT_CRTC_GAMMA_LUT,
+						pipe->crtc_id),
+						-EINVAL);
+	igt_assert_eq(pipe_set_property_blob_id(pipe,
+						IGT_CRTC_GAMMA_LUT,
+						4096 * 4096),
+						-EINVAL);
+
+	free(gamma_lut);
+}
+
+static void
+invalid_degamma_lut_sizes(data_t *data)
+{
+	igt_display_t *display = &data->display;
+	igt_pipe_t *pipe = &display->pipes[0];
+	size_t degamma_lut_size = data->degamma_lut_size *
+				  sizeof(struct drm_color_lut);
+	struct drm_color_lut *degamma_lut;
+
+	igt_require(igt_pipe_obj_has_prop(pipe, IGT_CRTC_DEGAMMA_LUT));
+
+	degamma_lut = malloc(degamma_lut_size * 2);
+
+	igt_display_commit2(display,
+			    display->is_atomic ?
+			    COMMIT_ATOMIC : COMMIT_LEGACY);
+
+	igt_assert_eq(pipe_set_property_blob(pipe, IGT_CRTC_DEGAMMA_LUT,
+					     degamma_lut, 1), -EINVAL);
+	igt_assert_eq(pipe_set_property_blob(pipe, IGT_CRTC_DEGAMMA_LUT,
+					     degamma_lut, degamma_lut_size + 1),
+					     -EINVAL);
+	igt_assert_eq(pipe_set_property_blob(pipe, IGT_CRTC_DEGAMMA_LUT,
+					     degamma_lut,
+					     degamma_lut_size - 1),
+					     -EINVAL);
+	igt_assert_eq(pipe_set_property_blob(pipe, IGT_CRTC_DEGAMMA_LUT,
+					     degamma_lut,
+					     degamma_lut_size +
+					     sizeof(struct drm_color_lut)),
+					     -EINVAL);
+	igt_assert_eq(pipe_set_property_blob_id(pipe, IGT_CRTC_DEGAMMA_LUT,
+						pipe->crtc_id), -EINVAL);
+	igt_assert_eq(pipe_set_property_blob_id(pipe, IGT_CRTC_DEGAMMA_LUT,
+						4096 * 4096), -EINVAL);
+
+	free(degamma_lut);
+}
+
+static void
+invalid_ctm_matrix_sizes(data_t *data)
+{
+	igt_display_t *display = &data->display;
+	igt_pipe_t *pipe = &display->pipes[0];
+	void *ptr;
+
+	igt_require(igt_pipe_obj_has_prop(pipe, IGT_CRTC_CTM));
+
+	ptr = malloc(sizeof(struct drm_color_ctm) * 4);
+
+	igt_assert_eq(pipe_set_property_blob(pipe, IGT_CRTC_CTM, ptr, 1),
+					     -EINVAL);
+	igt_assert_eq(pipe_set_property_blob(pipe, IGT_CRTC_CTM, ptr,
+					     sizeof(struct drm_color_ctm) + 1),
+					     -EINVAL);
+	igt_assert_eq(pipe_set_property_blob(pipe, IGT_CRTC_CTM, ptr,
+					     sizeof(struct drm_color_ctm) - 1),
+					     -EINVAL);
+	igt_assert_eq(pipe_set_property_blob(pipe, IGT_CRTC_CTM, ptr,
+					     sizeof(struct drm_color_ctm) * 2),
+					     -EINVAL);
+	igt_assert_eq(pipe_set_property_blob_id(pipe, IGT_CRTC_CTM,
+						pipe->crtc_id), -EINVAL);
+	igt_assert_eq(pipe_set_property_blob_id(pipe, IGT_CRTC_CTM,
+						4096 * 4096), -EINVAL);
+
+	free(ptr);
+}
+
+igt_main
+{
+	data_t data = {};
+	enum pipe pipe;
+
+	igt_fixture {
+		data.drm_fd = drm_open_driver_master(DRIVER_ANY);
+		if (is_i915_device(data.drm_fd))
+			data.devid = intel_get_drm_devid(data.drm_fd);
+		data.chamelium = chamelium_init(data.drm_fd);
+		igt_require(data.chamelium);
+
+		data.ports = chamelium_get_ports(data.chamelium,
+						 &data.port_count);
+
+		kmstest_set_vt_graphics_mode();
+		igt_display_require(&data.display, data.drm_fd);
+		igt_require(data.display.is_atomic);
+	}
+
+	for_each_pipe_static(pipe)
+		igt_subtest_group
+			run_tests_for_pipe(&data, pipe);
+	igt_describe("Negative test case gamma lut size");
+	igt_subtest_f("pipe-invalid-gamma-lut-sizes")
+		invalid_gamma_lut_sizes(&data);
+
+	igt_describe("Negative test case degamma lut size");
+	igt_subtest_f("pipe-invalid-degamma-lut-sizes")
+		invalid_degamma_lut_sizes(&data);
+
+	igt_describe("Negative test case ctm matrix size");
+	igt_subtest_f("pipe-invalid-ctm-matrix-sizes")
+		invalid_ctm_matrix_sizes(&data);
+
+	igt_fixture {
+		igt_display_fini(&data.display);
+	}
+}
diff --git a/tests/meson.build b/tests/meson.build
index 570de54..74a5541 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -256,6 +256,7 @@ endif
 if chamelium.found()
 	test_progs += [
 		'kms_chamelium',
+		'kms_color_chamelium',
 	]
 	test_deps += chamelium
 endif
-- 
2.7.4



More information about the igt-dev mailing list