summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/igt_psr.c29
-rw-r--r--lib/igt_psr.h1
-rw-r--r--tests/Makefile.sources1
-rw-r--r--tests/kms_psr2_su.c292
-rw-r--r--tests/meson.build1
5 files changed, 324 insertions, 0 deletions
diff --git a/lib/igt_psr.c b/lib/igt_psr.c
index 6ad2c522..b5847bfd 100644
--- a/lib/igt_psr.c
+++ b/lib/igt_psr.c
@@ -178,3 +178,32 @@ bool psr_sink_support(int debugfs_fd, enum psr_mode mode)
*/
return strstr(buf, "Sink support: yes [0x03]");
}
+
+#define PSR2_SU_BLOCK_STR_LOOKUP "PSR2 SU blocks:\n0\t"
+
+static bool
+psr2_read_last_num_su_blocks_val(int debugfs_fd, uint16_t *num_su_blocks)
+{
+ char buf[PSR_STATUS_MAX_LEN];
+ char *str;
+ int ret;
+
+ ret = igt_debugfs_simple_read(debugfs_fd, "i915_edp_psr_status", buf,
+ sizeof(buf));
+ if (ret < 0)
+ return false;
+
+ str = strstr(buf, PSR2_SU_BLOCK_STR_LOOKUP);
+ if (!str)
+ return false;
+
+ str = &str[strlen(PSR2_SU_BLOCK_STR_LOOKUP)];
+ *num_su_blocks = (uint16_t)strtol(str, NULL, 10);
+
+ return true;
+}
+
+bool psr2_wait_su(int debugfs_fd, uint16_t *num_su_blocks)
+{
+ return igt_wait(psr2_read_last_num_su_blocks_val(debugfs_fd, num_su_blocks), 40, 1);
+}
diff --git a/lib/igt_psr.h b/lib/igt_psr.h
index 7e7017bf..49599cf8 100644
--- a/lib/igt_psr.h
+++ b/lib/igt_psr.h
@@ -40,5 +40,6 @@ bool psr_wait_update(int debugfs_fd, enum psr_mode mode);
bool psr_enable(int debugfs_fd, enum psr_mode);
bool psr_disable(int debugfs_fd);
bool psr_sink_support(int debugfs_fd, enum psr_mode);
+bool psr2_wait_su(int debugfs_fd, uint16_t *num_su_blocks);
#endif
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index a234fa5d..d2c4f9fe 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -62,6 +62,7 @@ TESTS_progs = \
kms_plane_scaling \
kms_properties \
kms_psr \
+ kms_psr2_su \
kms_pwrite_crc \
kms_rmfb \
kms_rotation_crc \
diff --git a/tests/kms_psr2_su.c b/tests/kms_psr2_su.c
new file mode 100644
index 00000000..b9a57582
--- /dev/null
+++ b/tests/kms_psr2_su.c
@@ -0,0 +1,292 @@
+/*
+ * Copyright © 2019 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 "igt.h"
+#include "igt_sysfs.h"
+#include "igt_psr.h"
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/timerfd.h>
+#include "intel_bufmgr.h"
+
+IGT_TEST_DESCRIPTION("Test PSR2 selective update");
+
+#define SQUARE_SIZE 100
+/* each selective update block is 4 lines tall */
+#define EXPECTED_NUM_SU_BLOCKS ((SQUARE_SIZE / 4) + (SQUARE_SIZE % 4 ? 1 : 0))
+
+/*
+ * Minimum is 15 as the number of frames to active PSR2 could be configured
+ * to 15 frames plus a few more in case we miss a selective update between
+ * debugfs reads.
+ */
+#define MAX_SCREEN_CHANGES 20
+
+enum operations {
+ PAGE_FLIP,
+ FRONTBUFFER,
+ LAST
+};
+
+static const char *op_str(enum operations op)
+{
+ static const char * const name[] = {
+ [PAGE_FLIP] = "page_flip",
+ [FRONTBUFFER] = "frontbuffer"
+ };
+
+ return name[op];
+}
+
+typedef struct {
+ int drm_fd;
+ int debugfs_fd;
+ igt_display_t display;
+ drm_intel_bufmgr *bufmgr;
+ drmModeModeInfo *mode;
+ igt_output_t *output;
+ struct igt_fb fb[2];
+ enum operations op;
+ cairo_t *cr;
+ int change_screen_timerfd;
+ uint32_t screen_changes;
+} data_t;
+
+static void setup_output(data_t *data)
+{
+ igt_display_t *display = &data->display;
+ igt_output_t *output;
+ enum pipe pipe;
+
+ for_each_pipe_with_valid_output(display, pipe, output) {
+ drmModeConnectorPtr c = output->config.connector;
+
+ if (c->connector_type != DRM_MODE_CONNECTOR_eDP)
+ continue;
+
+ igt_output_set_pipe(output, pipe);
+ data->output = output;
+ data->mode = igt_output_get_mode(output);
+
+ return;
+ }
+}
+
+static void display_init(data_t *data)
+{
+ igt_display_require(&data->display, data->drm_fd);
+ setup_output(data);
+}
+
+static void display_fini(data_t *data)
+{
+ igt_display_fini(&data->display);
+}
+
+static void prepare(data_t *data)
+{
+ igt_plane_t *primary;
+
+ /* all green frame */
+ igt_create_color_fb(data->drm_fd,
+ data->mode->hdisplay, data->mode->vdisplay,
+ DRM_FORMAT_XRGB8888,
+ LOCAL_DRM_FORMAT_MOD_NONE,
+ 0.0, 1.0, 0.0,
+ &data->fb[0]);
+
+ if (data->op == PAGE_FLIP) {
+ cairo_t *cr;
+
+ igt_create_color_fb(data->drm_fd,
+ data->mode->hdisplay, data->mode->vdisplay,
+ DRM_FORMAT_XRGB8888,
+ LOCAL_DRM_FORMAT_MOD_NONE,
+ 0.0, 1.0, 0.0,
+ &data->fb[1]);
+
+ cr = igt_get_cairo_ctx(data->drm_fd, &data->fb[1]);
+ /* paint a white square */
+ igt_paint_color_alpha(cr, 0, 0, SQUARE_SIZE, SQUARE_SIZE,
+ 1.0, 1.0, 1.0, 1.0);
+ igt_put_cairo_ctx(data->drm_fd, &data->fb[1], cr);
+ } else if (data->op == FRONTBUFFER) {
+ data->cr = igt_get_cairo_ctx(data->drm_fd, &data->fb[0]);
+ }
+
+ primary = igt_output_get_plane_type(data->output,
+ DRM_PLANE_TYPE_PRIMARY);
+
+ igt_plane_set_fb(primary, &data->fb[0]);
+ igt_display_commit2(&data->display, COMMIT_ATOMIC);
+}
+
+static bool update_screen_and_test(data_t *data)
+{
+ uint16_t su_blocks;
+ bool ret = false;
+
+ switch (data->op) {
+ case PAGE_FLIP: {
+ igt_plane_t *primary;
+
+ primary = igt_output_get_plane_type(data->output,
+ DRM_PLANE_TYPE_PRIMARY);
+
+ igt_plane_set_fb(primary, &data->fb[data->screen_changes & 1]);
+ igt_display_commit2(&data->display, COMMIT_ATOMIC);
+ break;
+ }
+ case FRONTBUFFER: {
+ drmModeClip clip;
+
+ clip.x1 = clip.y1 = 0;
+ clip.x2 = clip.y2 = SQUARE_SIZE;
+
+ if (data->screen_changes & 1) {
+ /* go back to all green frame with a square */
+ igt_paint_color_alpha(data->cr, 0, 0, SQUARE_SIZE,
+ SQUARE_SIZE, 1.0, 1.0, 1.0, 1.0);
+ } else {
+ /* go back to all green frame */
+ igt_paint_color_alpha(data->cr, 0, 0, SQUARE_SIZE,
+ SQUARE_SIZE, 0, 1.0, 0, 1.0);
+ }
+
+ drmModeDirtyFB(data->drm_fd, data->fb[0].fb_id, &clip, 1);
+ break;
+ }
+ default:
+ igt_assert_f(data->op, "Operation not handled\n");
+ }
+
+ if (psr2_wait_su(data->debugfs_fd, &su_blocks))
+ ret = su_blocks == EXPECTED_NUM_SU_BLOCKS;
+
+ return ret;
+}
+
+static void run(data_t *data)
+{
+ bool result = false;
+
+ igt_assert(psr_wait_entry(data->debugfs_fd, PSR_MODE_2));
+
+ for (data->screen_changes = 0;
+ data->screen_changes < MAX_SCREEN_CHANGES && !result;
+ data->screen_changes++) {
+ uint64_t exp;
+ int r;
+
+ r = read(data->change_screen_timerfd, &exp, sizeof(exp));
+ if (r == sizeof(uint64_t) && exp)
+ result = update_screen_and_test(data);
+ }
+
+ igt_debug("Screen changes: %u\n", data->screen_changes);
+ igt_assert_f(result,
+ "No matching selective update blocks read from debugfs\n");
+}
+
+static void cleanup(data_t *data)
+{
+ igt_plane_t *primary;
+
+ primary = igt_output_get_plane_type(data->output,
+ DRM_PLANE_TYPE_PRIMARY);
+ igt_plane_set_fb(primary, NULL);
+ igt_display_commit2(&data->display, COMMIT_ATOMIC);
+
+ if (data->op == PAGE_FLIP)
+ igt_remove_fb(data->drm_fd, &data->fb[1]);
+ else if (data->op == FRONTBUFFER)
+ igt_put_cairo_ctx(data->drm_fd, &data->fb[0], data->cr);
+
+ igt_remove_fb(data->drm_fd, &data->fb[0]);
+}
+
+int main(int argc, char *argv[])
+{
+ data_t data = {};
+
+ igt_subtest_init_parse_opts(&argc, argv, "", NULL,
+ NULL, NULL, NULL);
+ igt_skip_on_simulation();
+
+ igt_fixture {
+ struct itimerspec interval;
+ int r;
+
+ data.drm_fd = drm_open_driver_master(DRIVER_INTEL);
+ data.debugfs_fd = igt_debugfs_dir(data.drm_fd);
+ kmstest_set_vt_graphics_mode();
+
+ igt_require_f(psr_sink_support(data.debugfs_fd, PSR_MODE_2),
+ "Sink does not support PSR2\n");
+
+ data.bufmgr = drm_intel_bufmgr_gem_init(data.drm_fd, 4096);
+ igt_assert(data.bufmgr);
+ drm_intel_bufmgr_gem_enable_reuse(data.bufmgr);
+
+ display_init(&data);
+
+ /* Test if PSR2 can be enabled */
+ igt_require_f(psr_enable(data.debugfs_fd, PSR_MODE_2),
+ "Error enabling PSR2\n");
+ data.op = FRONTBUFFER;
+ prepare(&data);
+ r = psr_wait_entry(data.debugfs_fd, PSR_MODE_2);
+ cleanup(&data);
+ igt_require_f(r, "PSR2 can not be enabled\n");
+
+ /* blocking timerfd */
+ data.change_screen_timerfd = timerfd_create(CLOCK_MONOTONIC, 0);
+ igt_require(data.change_screen_timerfd != -1);
+ /* Changing screen at 30hz to support 30hz panels */
+ interval.it_value.tv_nsec = NSEC_PER_SEC / 30;
+ interval.it_value.tv_sec = 0;
+ interval.it_interval.tv_nsec = interval.it_value.tv_nsec;
+ interval.it_interval.tv_sec = interval.it_value.tv_sec;
+ r = timerfd_settime(data.change_screen_timerfd, 0, &interval, NULL);
+ igt_require_f(r != -1, "Error setting timerfd\n");
+ }
+
+ for (data.op = PAGE_FLIP; data.op < LAST; data.op++) {
+ igt_subtest_f("%s", op_str(data.op)) {
+ prepare(&data);
+ run(&data);
+ cleanup(&data);
+ }
+ }
+
+ igt_fixture {
+ close(data.debugfs_fd);
+ drm_intel_bufmgr_destroy(data.bufmgr);
+ display_fini(&data);
+ }
+
+ igt_exit();
+}
diff --git a/tests/meson.build b/tests/meson.build
index 0f12df26..ec980651 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -49,6 +49,7 @@ test_progs = [
'kms_plane_scaling',
'kms_properties',
'kms_psr',
+ 'kms_psr2_su',
'kms_pwrite_crc',
'kms_rmfb',
'kms_rotation_crc',