From: Swati Sharma <swati2.sharma@intel.com>
To: igt-dev@lists.freedesktop.org
Cc: Swati Sharma <swati2.sharma@intel.com>
Subject: [PATCH i-g-t] tests/kms_chamelium_color_pipeline: Add plane color pipeline validation
Date: Wed, 4 Mar 2026 15:07:18 +0530 [thread overview]
Message-ID: <20260304093718.513512-1-swati2.sharma@intel.com> (raw)
Add a Chamelium-backed test to validate DRM plane colorops
using frame capture instead of pipe CRC comparison.
The test generates a software reference framebuffer by
applying the expected color transformation in bypass mode.
It then enables the plane color pipeline in hardware,
renders the input framebuffer, and captures the output
signal using Chamelium.
The test passes if the captured frame matches the
software reference framebuffer within the allowed tolerance.
The test fails if the captured frame differs from the
reference, indicating incorrect color transformation,
pipeline misconfiguration, or hardware output mismatch.
Signed-off-by: Swati Sharma <swati2.sharma@intel.com>
---
.../chamelium/kms_chamelium_color_pipeline.c | 401 ++++++++++++++++++
tests/meson.build | 2 +
2 files changed, 403 insertions(+)
create mode 100644 tests/chamelium/kms_chamelium_color_pipeline.c
diff --git a/tests/chamelium/kms_chamelium_color_pipeline.c b/tests/chamelium/kms_chamelium_color_pipeline.c
new file mode 100644
index 000000000..19828c5f3
--- /dev/null
+++ b/tests/chamelium/kms_chamelium_color_pipeline.c
@@ -0,0 +1,401 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2026 Intel Corporation
+ */
+
+/**
+ * TEST: kms chamelium color pipeline
+ * Category: Display
+ * Description: Test to validate DRM colorops at plane level using Chamelium to verify instead of CRC
+ * Driver requirement: i915, xe
+ * Mega feature: Color Management
+ */
+
+#include "kms_chamelium_helper.h"
+#include "kms_color_helper.h"
+#include "kms_colorop_helper.h"
+
+#define MAX_COLOROPS 5
+
+/**
+ * SUBTEST: plane-%s
+ * Description: Test plane color pipeline with colorops: %arg[1].
+ *
+ * arg[1]:
+ *
+ * @lut1d: 1D LUT
+ * @lut1d-pre-ctm3x4: 1D LUT PRE CTM 3x4
+ * @lut1d-post-ctm3x4: 1D LUT POST CTM 3x4
+ * @ctm3x4: 3X4 CTM
+ * @lut1d-ctm3x4: 1D LUT --> 3X4 CTM
+ * @ctm3x4-lut1d: 3X4 CTM --> 1D LUT
+ * @lut1d-lut1d: 1D LUT --> 1D LUT
+ * @lut1d-ctm3x4-lut1d: 1D LUT --> 3X4 CTM --> 1D LUT
+ * @lut3d-green-only: 3D LUT
+ */
+
+IGT_TEST_DESCRIPTION("Test DRM colorops at plane level using Chamelium to verify instead of CRC");
+
+static void test_cleanup(data_t *data)
+{
+ igt_output_set_crtc(data->output, NULL);
+ igt_display_commit_atomic(&data->display, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
+}
+
+static int test_setup(data_t *data, igt_crtc_t *crtc)
+{
+ int i;
+
+ igt_require(crtc);
+ igt_require(crtc->n_planes > 0);
+
+ igt_output_set_crtc(data->output, crtc);
+
+ data->primary = igt_crtc_get_plane_type(crtc, DRM_PLANE_TYPE_PRIMARY);
+ data->mode = igt_output_get_mode(data->output);
+ igt_require(data->mode);
+
+ /* Disable pipe color props. */
+ disable_ctm(crtc);
+ disable_degamma(crtc);
+ disable_gamma(crtc);
+
+ igt_display_commit_atomic(&data->display, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
+
+ /* Find matching chamelium port */
+ for (i = 0; i < data->port_count; i++) {
+ if (!strcmp(data->output->name,
+ chamelium_port_get_name(data->ports[i])))
+ return i;
+ }
+
+ return -1;
+}
+
+static bool ctm_colorop_only(kms_colorop_t *colorops[])
+{
+ int i;
+
+ if (!colorops[0])
+ return false;
+
+ for (i = 0; colorops[i]; i++) {
+ if (colorops[i]->type != KMS_COLOROP_CTM_3X4)
+ return false;
+ }
+
+ return true;
+}
+
+static void _test_plane_colorops(data_t *data,
+ igt_plane_t *plane,
+ const color_t *fb_colors,
+ const color_t *exp_colors,
+ kms_colorop_t *colorops[],
+ struct chamelium_port *port)
+{
+ igt_display_t *display = &data->display;
+ drmModeModeInfo *mode = data->mode;
+ igt_colorop_t *color_pipeline;
+ struct igt_fb fb, fbref;
+ struct chamelium_frame_dump *frame;
+ bool ret;
+
+ color_pipeline = get_color_pipeline(display, plane, colorops);
+ igt_skip_on(!color_pipeline);
+
+ /* Create HW framebuffer */
+ igt_assert(igt_create_fb(data->drm_fd,
+ mode->hdisplay,
+ mode->vdisplay,
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_MOD_LINEAR,
+ &fb));
+
+ /* Create reference framebuffer */
+ igt_assert(igt_create_fb(data->drm_fd,
+ mode->hdisplay,
+ mode->vdisplay,
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_MOD_LINEAR,
+ &fbref));
+
+ /* ---- Software reference ---- */
+ set_color_pipeline_bypass(plane);
+ paint_rectangles(data, mode, exp_colors, &fbref);
+
+ /* ---- Hardware path ---- */
+ set_color_pipeline(display, plane, colorops, color_pipeline);
+
+ if (ctm_colorop_only(colorops))
+ paint_rectangles(data, mode, fb_colors, &fb);
+ else
+ paint_gradient_rectangles(data, mode, fb_colors, &fb);
+
+ igt_plane_set_fb(plane, &fb);
+ igt_display_commit_atomic(&data->display, 0, NULL);
+
+ chamelium_capture(data->chamelium, port, 0, 0, 0, 0, 1);
+
+ frame = chamelium_read_captured_frame(data->chamelium, 0);
+
+ ret = chamelium_frame_match_or_dump(data->chamelium,
+ port,
+ frame,
+ &fbref,
+ CHAMELIUM_CHECK_ANALOG);
+
+ igt_assert(ret);
+
+ /* Cleanup */
+ set_color_pipeline_bypass(plane);
+ reset_colorops(colorops);
+
+ igt_plane_set_fb(plane, NULL);
+ igt_display_commit_atomic(&data->display, 0, NULL);
+
+ igt_remove_fb(data->drm_fd, &fb);
+ igt_remove_fb(data->drm_fd, &fbref);
+}
+
+static void
+test_plane_colorops(data_t *data, igt_crtc_t *crtc,
+ const color_t *fb_colors,
+ const color_t *exp_colors,
+ kms_colorop_t *colorops[],
+ int port_idx)
+{
+ int n_planes = crtc->n_planes;
+ igt_output_t *output = data->output;
+ igt_plane_t *plane;
+
+ for (int plane_id = 0; plane_id < n_planes; plane_id++) {
+ plane = igt_output_get_plane(output, plane_id);
+
+ if (!igt_plane_has_prop(plane, IGT_PLANE_COLOR_PIPELINE))
+ continue;
+
+ igt_dynamic_f("pipe-%s-plane-%u", kmstest_pipe_name(crtc->pipe), plane_id)
+ _test_plane_colorops(data, plane, fb_colors,
+ exp_colors, colorops,
+ data->ports[port_idx]);
+ }
+}
+
+static void
+run_tests_for_plane(data_t *data)
+{
+ igt_crtc_t *crtc;
+ igt_output_t *output = NULL;
+ int port_idx = 0;
+ static const color_t colors_rgb[] = {
+ { 1.0, 0.0, 0.0 },
+ { 0.0, 1.0, 0.0 },
+ { 0.0, 0.0, 1.0 },
+ };
+ static const color_t colors_red_to_blue[] = {
+ { 0.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0 },
+ { 0.0, 0.0, 1.0 },
+ };
+ static const color_t colors_red_and_green[] = {
+ { 1.0, 1.0, 0.0 },
+ { 1.0, 1.0, 0.0 },
+ { 1.0, 1.0, 0.0 }
+ };
+ static const color_t colors_only_green[] = {
+ { 0.0, 1.0, 0.0 },
+ { 0.0, 1.0, 0.0 },
+ { 0.0, 1.0, 0.0 }
+ };
+ const igt_matrix_3x4_t ctm_red_to_blue = { {
+ 0.0, 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0, 0.0,
+ 1.0, 0.0, 1.0, 0.0,
+ } };
+ const igt_matrix_3x4_t ctm_linear = { {
+ 1.0, 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0, 0.0,
+ 0.0, 0.0, 1.0, 0.0,
+ } };
+ kms_colorop_t lut1d_linear = {
+ .type = KMS_COLOROP_CUSTOM_LUT1D,
+ .name = "Pre/Post CSC GAMMA (linear LUT)",
+ .lut1d = &igt_1dlut_linear,
+ .transform = &igt_color_linear,
+ };
+ kms_colorop_t lut1d_max = {
+ .type = KMS_COLOROP_CUSTOM_LUT1D,
+ .lut1d = &igt_1dlut_max,
+ .name = "Pre/Post CSC GAMMA (max LUT)",
+ .transform = &igt_color_max,
+ };
+ kms_colorop_t lut3d = {
+ .type = KMS_COLOROP_LUT3D,
+ .lut3d = &igt_3dlut_17_green_only,
+ .lut3d_info = {
+ .size = 17,
+ .interpolation = DRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL,
+ },
+ .name = "3dlut passing only green channel (RGB order)",
+ .transform = NULL,
+ };
+ kms_colorop_t ctm_3x4 = {
+ .type = KMS_COLOROP_CTM_3X4,
+ .name = "CTM 3X4 (red to blue)",
+ .matrix_3x4 = &ctm_red_to_blue,
+ };
+ kms_colorop_t ctm_3x4_linear = {
+ .type = KMS_COLOROP_CTM_3X4,
+ .name = "CTM 3X4 (linear)",
+ .matrix_3x4 = &ctm_linear,
+ };
+
+ struct {
+ const char *name;
+ const color_t *fb_colors;
+ const color_t *exp_colors;
+ kms_colorop_t *colorops[MAX_COLOROPS];
+ } plane_colorops_tests[] = {
+ { .name = "lut1d",
+ .fb_colors = colors_rgb,
+ .exp_colors = colors_rgb,
+ .colorops = { &lut1d_max, NULL },
+ },
+ { .name = "lut1d-pre-ctm3x4",
+ .fb_colors = colors_rgb,
+ .exp_colors = colors_rgb,
+ .colorops = { &lut1d_max, &ctm_3x4_linear, NULL },
+ },
+ { .name = "lut1d-post-ctm3x4",
+ .fb_colors = colors_rgb,
+ .exp_colors = colors_rgb,
+ .colorops = { &ctm_3x4_linear, &lut1d_max, NULL },
+ },
+ { .name = "ctm3x4",
+ .fb_colors = colors_rgb,
+ .exp_colors = colors_red_to_blue,
+ .colorops = { &ctm_3x4, NULL },
+ },
+ { .name = "lut1d-ctm3x4",
+ .fb_colors = colors_rgb,
+ .exp_colors = colors_red_to_blue,
+ .colorops = { &lut1d_max, &ctm_3x4, NULL },
+ },
+ { .name = "ctm3x4-lut1d",
+ .fb_colors = colors_rgb,
+ .exp_colors = colors_red_to_blue,
+ .colorops = { &ctm_3x4, &lut1d_max, NULL },
+ },
+ { .name = "lut1d-lut1d",
+ .fb_colors = colors_rgb,
+ .exp_colors = colors_rgb,
+ .colorops = { &lut1d_linear, &lut1d_max, NULL },
+ },
+ { .name = "lut1d-ctm3x4-lut1d",
+ .fb_colors = colors_rgb,
+ .exp_colors = colors_red_to_blue,
+ .colorops = { &lut1d_linear, &ctm_3x4, &lut1d_max, NULL },
+ },
+ { .name = "lut3d-green-only",
+ .fb_colors = colors_red_and_green,
+ .exp_colors = colors_only_green,
+ .colorops = { &lut3d, NULL },
+ },
+ };
+
+ for (int i = 0; i < ARRAY_SIZE(plane_colorops_tests); i++) {
+ igt_describe_f("Test plane color pipeline with colorops: %s", plane_colorops_tests[i].name);
+ igt_subtest_with_dynamic_f("plane-%s", plane_colorops_tests[i].name) {
+ for_each_crtc_with_single_output(&data->display, crtc,
+ output) {
+ data->output = output;
+
+ if (!pipe_output_combo_valid(data, crtc))
+ continue;
+
+ port_idx = test_setup(data, crtc);
+ igt_require(port_idx >= 0);
+
+ data->color_depth = 8;
+ data->drm_format = DRM_FORMAT_XRGB8888;
+
+ test_plane_colorops(data, crtc,
+ plane_colorops_tests[i].fb_colors,
+ plane_colorops_tests[i].exp_colors,
+ plane_colorops_tests[i].colorops,
+ port_idx);
+
+ test_cleanup(data);
+ }
+ }
+ }
+}
+
+int igt_main()
+{
+ int i;
+ int has_plane_color_pipeline = 0;
+ data_t data = {};
+
+ igt_fixture() {
+ data.drm_fd = drm_open_driver_master(DRIVER_ANY);
+
+ /*
+ * Plane color pipeline support requires atomic modesetting.
+ * The atomic capability is enabled here as a prerequisite;
+ * igt_display_require() will reinitialize the display state.
+ */
+ if (drmSetClientCap(data.drm_fd, DRM_CLIENT_CAP_ATOMIC, 1) == 0)
+ data.display.is_atomic = 1;
+
+ if (drmSetClientCap(data.drm_fd, DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE, 1) == 0)
+ has_plane_color_pipeline = 1;
+
+ kmstest_set_vt_graphics_mode();
+
+ igt_display_require(&data.display, data.drm_fd);
+ data.display.has_plane_color_pipeline = has_plane_color_pipeline;
+ igt_require(data.display.is_atomic);
+
+ /* Chamelium init */
+ data.chamelium = chamelium_init(data.drm_fd, &data.display);
+ igt_require(data.chamelium);
+
+ data.ports = chamelium_get_ports(data.chamelium,
+ &data.port_count);
+
+ if (!data.port_count)
+ igt_skip("No ports connected\n");
+
+ /*
+ * The behavior differs based on the availability of port mappings:
+ * - When using port mappings (chamelium_read_port_mappings),
+ * ports are not plugged
+ * - During autodiscovery, all ports are plugged at the end.
+ *
+ * This quick workaround (unplug, plug, and re-probe the connectors)
+ * prevents any ports from being unintentionally skipped in test_setup.
+ */
+ for (i = 0; i < data.port_count; i++) {
+ chamelium_unplug(data.chamelium, data.ports[i]);
+ chamelium_plug(data.chamelium, data.ports[i]);
+ chamelium_wait_for_conn_status_change(&data.display,
+ data.chamelium,
+ data.ports[i],
+ DRM_MODE_CONNECTED);
+ igt_assert_f(chamelium_reprobe_connector(&data.display,
+ data.chamelium,
+ data.ports[i]) == DRM_MODE_CONNECTED,
+ "Output not connected\n");
+ }
+ }
+
+ igt_subtest_group()
+ run_tests_for_plane(&data);
+
+ igt_fixture() {
+ igt_display_fini(&data.display);
+ drm_close_driver(data.drm_fd);
+ }
+}
diff --git a/tests/meson.build b/tests/meson.build
index 7f356de9b..3f50a3b92 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -359,6 +359,7 @@ endif
chamelium_progs = [
'kms_chamelium_audio',
'kms_chamelium_color',
+ 'kms_chamelium_color_pipeline',
'kms_chamelium_edid',
'kms_chamelium_frames',
'kms_chamelium_hpd',
@@ -387,6 +388,7 @@ extra_sources = {
'kms_colorop': [ 'kms_colorop_helper.c' ],
'kms_chamelium_audio': [ join_paths ('chamelium', 'kms_chamelium_helper.c') ],
'kms_chamelium_color': [ 'kms_color_helper.c', join_paths ('chamelium', 'kms_chamelium_helper.c') ],
+ 'kms_chamelium_color_pipeline': [ 'kms_color_helper.c', 'kms_colorop_helper.c', join_paths ('chamelium', 'kms_chamelium_helper.c') ],
'kms_chamelium_edid': [ join_paths ('chamelium', 'kms_chamelium_helper.c') ],
'kms_chamelium_frames': [ join_paths ('chamelium', 'kms_chamelium_helper.c') ],
'kms_chamelium_hpd': [ join_paths ('chamelium', 'kms_chamelium_helper.c') ],
--
2.25.1
next reply other threads:[~2026-03-04 9:28 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-04 9:37 Swati Sharma [this message]
2026-03-04 19:08 ` [PATCH i-g-t] tests/kms_chamelium_color_pipeline: Add plane color pipeline validation Bilal, Mohammed
2026-03-05 2:12 ` ✓ i915.CI.BAT: success for " Patchwork
2026-03-05 11:08 ` ✓ Xe.CI.BAT: " Patchwork
2026-03-05 12:07 ` ✗ Xe.CI.FULL: failure " Patchwork
2026-03-06 2:16 ` ✗ i915.CI.Full: " Patchwork
2026-05-13 5:43 ` [PATCH i-g-t] " Borah, Chaitanya Kumar
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260304093718.513512-1-swati2.sharma@intel.com \
--to=swati2.sharma@intel.com \
--cc=igt-dev@lists.freedesktop.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox