[PATCH i-g-t] Add a new test to validate the deep sleep state during extended vblank

Jeevan B jeevan.b at intel.com
Thu Jan 18 16:21:00 UTC 2024


Add a new test to validate deep sleep states during extended vblank
scenarios, where two frames are committed simultaneously for a give
time with reduced refresh rate.

Signed-off-by: Jeevan B <jeevan.b at intel.com>
---
 tests/intel/kms_pm_dc.c | 262 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 262 insertions(+)

diff --git a/tests/intel/kms_pm_dc.c b/tests/intel/kms_pm_dc.c
index 0d5824e67..da5d0e819 100644
--- a/tests/intel/kms_pm_dc.c
+++ b/tests/intel/kms_pm_dc.c
@@ -74,6 +74,10 @@
  * Description: This test validates display engine entry to DC6 state while PSR is active
  * Functionality: pm_dc, psr1
  *
+ * SUBTEST: deep-pkgc
+ * Description: This test validates display engine entry to Deep PKGC state for extended vblank
+ * Functionality: pm_dc
+ *
  * SUBTEST: dc9-dpms
  * Description: This test validates display engine entry to DC9 state
  */
@@ -89,6 +93,14 @@
 #define PACKAGE_CSTATE_PATH  "pmc_core/package_cstate_show"
 #define KMS_POLL_DISABLE 0
 #define DC9_RESETS_DC_COUNTERS(devid) (!(IS_DG1(devid) || IS_DG2(devid) || AT_LEAST_DISPLAY(devid, 14)))
+#define DRM_MODE_FMT    "\"%s\": %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x"
+#define NSECS_PER_SEC (1000000000ull)
+#define TEST_DURATION_NS (5000000000ull)
+#define DRM_MODE_ARG(m) \
+                (m)->name, (m)->vrefresh, (m)->clock, \
+                (m)->hdisplay, (m)->hsync_start, (m)->hsync_end, (m)->htotal, \
+                (m)->vdisplay, (m)->vsync_start, (m)->vsync_end, (m)->vtotal, \
+                (m)->type, (m)->flags
 
 IGT_TEST_DESCRIPTION("Tests to validate display power DC states.");
 
@@ -98,6 +110,11 @@ typedef struct {
 	double r, g, b;
 } color_t;
 
+typedef struct range {
+        unsigned int min;
+        unsigned int max;
+} range_t;
+
 typedef struct {
 	int drm_fd;
 	int msr_fd;
@@ -110,12 +127,32 @@ typedef struct {
 	enum psr_mode op_psr_mode;
 	drmModeModeInfo *mode;
 	igt_output_t *output;
+	range_t range;
 	bool runtime_suspend_disabled;
+	igt_plane_t *primary;
 } data_t;
 
+typedef struct vtest_ns {
+        uint64_t min;
+        uint64_t mid;
+        uint64_t max;
+} vtest_ns_t;
+
 static bool dc_state_wait_entry(int drm_fd, int dc_flag, int prev_dc_count);
 static void check_dc_counter(data_t *data, int dc_flag, uint32_t prev_dc_count);
 
+/* Returns true if driver supports VRR. */
+static bool has_vrr(igt_output_t *output)
+{
+        return igt_output_has_prop(output, IGT_CONNECTOR_VRR_CAPABLE);
+}
+
+/* Returns true if an output supports VRR. */
+static bool vrr_capable(igt_output_t *output)
+{
+        return igt_output_get_prop(output, IGT_CONNECTOR_VRR_CAPABLE);
+}
+
 static void setup_output(data_t *data)
 {
 	igt_display_t *display = &data->display;
@@ -136,6 +173,53 @@ static void setup_output(data_t *data)
 	}
 }
 
+/* Read min and max vrr range from the connector debugfs. */
+static range_t
+get_vrr_range(data_t *data, igt_output_t *output)
+{
+        char buf[256];
+        char *start_loc;
+        int fd, res;
+        range_t range;
+
+        fd = igt_debugfs_connector_dir(data->drm_fd, output->name, O_RDONLY);
+        igt_assert(fd >= 0);
+
+        res = igt_debugfs_simple_read(fd, "vrr_range", buf, sizeof(buf));
+        igt_require(res > 0);
+
+        close(fd);
+
+        igt_assert(start_loc = strstr(buf, "Min: "));
+        igt_assert_eq(sscanf(start_loc, "Min: %u", &range.min), 1);
+
+        igt_assert(start_loc = strstr(buf, "Max: "));
+        igt_assert_eq(sscanf(start_loc, "Max: %u", &range.max), 1);
+
+        return range;
+}
+
+/* Instead of running on default mode, loop through the connector modes
+ * and find the mode with max refresh rate to exercise full vrr range.
+ */
+static drmModeModeInfo
+output_mode_with_maxrate(igt_output_t *output, unsigned int vrr_max)
+{
+        int i;
+        drmModeConnectorPtr connector = output->config.connector;
+        drmModeModeInfo mode = *igt_output_get_mode(output);
+
+        igt_debug("Default Mode " DRM_MODE_FMT "\n", DRM_MODE_ARG(&mode));
+
+        for (i = 0; i < connector->count_modes; i++)
+                if (connector->modes[i].vrefresh > mode.vrefresh &&
+                    connector->modes[i].vrefresh <= vrr_max)
+                        mode = connector->modes[i];
+        igt_debug("Override Mode " DRM_MODE_FMT "\n", DRM_MODE_ARG(&mode));
+
+        return mode;
+}
+
 static void display_fini(data_t *data)
 {
 	igt_display_fini(&data->display);
@@ -582,6 +666,173 @@ static unsigned int read_pkgc_counter(int debugfs_root_fd)
 	return get_dc_counter(str);
 }
 
+/* Converts a timespec structure to nanoseconds. */
+static uint64_t timespec_to_ns(struct timespec *ts)
+{
+        return ts->tv_sec * NSECS_PER_SEC + ts->tv_nsec;
+}
+
+/*
+ * Returns the current CLOCK_MONOTONIC time in nanoseconds.
+ * The regular IGT helpers can't be used since they default to
+ * CLOCK_MONOTONIC_RAW - which isn't what the kernel uses for its timestamps.
+ */
+static uint64_t get_time_ns(void)
+{
+        struct timespec ts;
+        memset(&ts, 0, sizeof(ts));
+        errno = 0;
+
+        if (!clock_gettime(CLOCK_MONOTONIC, &ts))
+                return timespec_to_ns(&ts);
+
+        igt_warn("Could not read monotonic time: %s\n", strerror(errno));
+        igt_fail(-errno);
+
+        return 0;
+}
+
+/*
+ * Gets an event from DRM and returns its timestamp in nanoseconds.
+ * Asserts if the event from DRM is not matched with requested one.
+ *
+ * This blocks until the event is received.
+ */
+static uint64_t get_kernel_event_ns(data_t *data, uint32_t event)
+{
+        struct drm_event_vblank ev;
+
+        igt_set_timeout(1, "Waiting for an event\n");
+        igt_assert_eq(read(data->drm_fd, &ev, sizeof(ev)), sizeof(ev));
+        igt_assert_eq(ev.base.type, event);
+        igt_reset_timeout();
+
+        return ev.tv_sec * NSECS_PER_SEC + ev.tv_usec * 1000ull;
+}
+
+/* Performs an atomic non-blocking page-flip on a pipe. */
+static void
+do_flip(data_t *data, igt_fb_t *fb)
+{
+        int ret;
+
+        igt_set_timeout(1, "Scheduling page flip\n");
+
+        igt_plane_set_fb(data->primary, fb);
+
+        do {
+                ret = igt_display_try_commit_atomic(&data->display,
+                                  DRM_MODE_ATOMIC_NONBLOCK |
+                                  DRM_MODE_PAGE_FLIP_EVENT,
+                                  data);
+        } while (ret == -EBUSY);
+
+        igt_assert_eq(ret, 0);
+        igt_reset_timeout();
+}
+
+/* Prepare the display for testing on the given pipe. */
+static void prepare_test(data_t *data, igt_output_t *output, enum pipe pipe)
+{
+        drmModeModeInfo mode;
+        cairo_t *cr;
+
+        mode = *igt_output_get_mode(output);
+
+        /* Prepare resources */
+        igt_create_color_fb(data->drm_fd, mode.hdisplay, mode.vdisplay,
+                            DRM_FORMAT_XRGB8888, DRM_FORMAT_MOD_LINEAR,
+                            0.50, 0.50, 0.50, &data->fb_rgb);
+
+        igt_create_color_fb(data->drm_fd, mode.hdisplay, mode.vdisplay,
+                            DRM_FORMAT_XRGB8888, DRM_FORMAT_MOD_LINEAR,
+                            0.50, 0.50, 0.50, &data->fb_rgr);
+
+        cr = igt_get_cairo_ctx(data->drm_fd, &data->fb_rgb);
+
+        igt_paint_color(cr, 0, 0, mode.hdisplay / 10, mode.vdisplay / 10,
+                        1.00, 0.00, 0.00);
+
+        igt_put_cairo_ctx(cr);
+
+        /* Take care of any required modesetting before the test begins. */
+        data->primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
+        igt_plane_set_fb(data->primary, &data->fb_rgb);
+
+        /* Clear vrr_enabled state before enabling it, because
+         * it might be left enabled if the previous test fails.
+         */
+        igt_pipe_set_prop_value(&data->display, pipe, IGT_CRTC_VRR_ENABLED, 0);
+
+        igt_display_commit2(&data->display, COMMIT_ATOMIC);
+}
+
+static void test_deep_pkgc_state(data_t *data)
+{
+        unsigned int pre_val = 0, cur_val = 0;
+        bool flag, front = false, pkgc_flag = false;
+        uint64_t start_ns, target_ns, event_ns;
+        drmModeModeInfo mode;
+        enum pipe pipe;
+        igt_display_t *display = &data->display;
+        igt_output_t *output;
+
+	pre_val = read_pkgc_counter(data->debugfs_root_fd);
+        psr_dpms(data, DRM_MODE_DPMS_OFF);
+
+	for_each_pipe_with_valid_output(display, pipe, output) {
+                igt_display_reset(display);
+                igt_output_set_pipe(output, pipe);
+                data->output = output;
+                data->mode = igt_output_get_mode(output);
+                igt_require(has_vrr(data->output));
+                if (!vrr_capable(data->output)) {
+                        flag = false;
+			continue;
+                }
+
+                /* Capture VRR range */
+                data->range = get_vrr_range(data, data->output);
+                /* Override mode with max vrefresh.
+                 *   - vrr_min range should be less than the override mode vrefresh.
+                 *   - Limit the vrr_max range with the override mode vrefresh.
+                 */
+                mode = output_mode_with_maxrate(data->output, data->range.max);
+                igt_require(mode.vrefresh > data->range.min);
+                data->range.max = mode.vrefresh;
+                igt_output_override_mode(data->output, &mode);
+                prepare_test(data, output, pipe);
+                data->primary = igt_output_get_plane_type(data->output,
+                                            DRM_PLANE_TYPE_PRIMARY);
+		target_ns = NSECS_PER_SEC / (data->range.max - 10);
+		do_flip(data, &data->fb_rgb);
+		start_ns = get_kernel_event_ns(data, DRM_EVENT_FLIP_COMPLETE);
+
+		while(event_ns - start_ns > TEST_DURATION_NS) {
+			front = !front;
+			do_flip(data, front ? &data->fb_rgb : &data->fb_rgr);
+			event_ns = get_kernel_event_ns(data, DRM_EVENT_FLIP_COMPLETE);
+
+			cur_val = read_pkgc_counter(data->debugfs_root_fd);
+			if (cur_val > pre_val) {
+				pkgc_flag = true;
+			}
+
+			if ( pkgc_flag == true)
+				break;
+			while (get_time_ns() < target_ns);
+		}
+
+                psr_dpms(data, DRM_MODE_DPMS_ON);
+                cleanup_dc3co_fbs(data);
+        }
+
+        if (flag)
+                igt_skip("No connector with VRR found\n");
+	igt_assert_f(pkgc_flag, "PKGC10 is not achieved.\n");
+
+}
+
 static void test_pkgc_state_dpms(data_t *data)
 {
 	unsigned int timeout_sec = 6;
@@ -669,6 +920,17 @@ igt_main
 		test_dc_state_psr(&data, CHECK_DC5);
 	}
 
+	igt_describe("This test validates display engine entry to DC8 state "
+		     "while extended vblank");
+	igt_subtest("deep-pkgc") {
+		igt_require_f(igt_pm_pc8_plus_residencies_enabled(data.msr_fd),
+			      "PC8+ residencies not supported\n");
+		if (intel_display_ver(data.devid) >= 20)
+			test_deep_pkgc_state(&data);
+		else
+			igt_skip("Deep PKGC not supported on this platform");
+	}
+
 	igt_describe("This test validates display engine entry to DC6 state "
 		     "while PSR is active");
 	igt_subtest("dc6-psr") {
-- 
2.25.1



More information about the igt-dev mailing list