summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tests/kms_atomic_transition.c310
1 files changed, 308 insertions, 2 deletions
diff --git a/tests/kms_atomic_transition.c b/tests/kms_atomic_transition.c
index b5b02653..23c65198 100644
--- a/tests/kms_atomic_transition.c
+++ b/tests/kms_atomic_transition.c
@@ -41,6 +41,8 @@ struct plane_parms {
uint32_t width, height;
};
+#define hweight32 __builtin_popcount
+
static void
wm_setup_plane(igt_display_t *display, enum pipe pipe,
uint32_t mask, struct plane_parms *parms)
@@ -202,11 +204,307 @@ run_transition_test(igt_display_t *display, enum pipe pipe, igt_output_t *output
igt_remove_fb(display->drm_fd, &argb_fb);
}
+static void commit_display(igt_display_t *display, unsigned event_mask, bool nonblocking)
+{
+ unsigned flags;
+ int num_events = hweight32(event_mask);
+ ssize_t ret;
+
+ flags = DRM_MODE_ATOMIC_ALLOW_MODESET | DRM_MODE_PAGE_FLIP_EVENT;
+ if (nonblocking)
+ flags |= DRM_MODE_ATOMIC_NONBLOCK;
+
+ igt_display_commit_atomic(display, flags, NULL);
+
+ while (num_events) {
+ char buf[32];
+ struct drm_event *e = (void *)buf;
+ struct drm_event_vblank *vblank = (void *)buf;
+ uint32_t crtc_id, pipe = I915_MAX_PIPES;
+
+ ret = read(display->drm_fd, buf, sizeof(buf));
+ if (ret < 0 && (errno == EINTR || errno == EAGAIN))
+ continue;
+
+ igt_assert(ret >= 0);
+ igt_assert_eq(e->type, DRM_EVENT_FLIP_COMPLETE);
+
+ crtc_id = vblank->reserved;
+ if (crtc_id) {
+ for_each_pipe(display, pipe)
+ if (display->pipes[pipe].crtc_id == crtc_id)
+ break;
+
+ igt_assert_lt(pipe, display->n_pipes);
+
+ igt_debug("Retrieved vblank seq: %u on %u/%u\n", vblank->sequence, vblank->reserved, pipe);
+ } else
+ igt_debug("Retrieved vblank seq: %u on unk/unk\n", vblank->sequence);
+
+ num_events--;
+ }
+}
+
+static unsigned set_combinations(igt_display_t *display, unsigned mask, struct igt_fb *fb)
+{
+ igt_output_t *output;
+ enum pipe pipe;
+ unsigned event_mask = 0;
+
+ for_each_connected_output(display, output)
+ igt_output_set_pipe(output, PIPE_NONE);
+
+ for_each_pipe(display, pipe) {
+ igt_plane_t *plane = &display->pipes[pipe].planes[IGT_PLANE_PRIMARY];
+ drmModeModeInfo *mode = NULL;
+
+ if (!(mask & (1 << pipe))) {
+ if (display->pipes[pipe].mode_blob) {
+ event_mask |= 1 << pipe;
+ igt_plane_set_fb(plane, NULL);
+ }
+
+ continue;
+ }
+
+ event_mask |= 1 << pipe;
+
+ for_each_valid_output_on_pipe(display, pipe, output) {
+ if (output->pending_crtc_idx_mask)
+ continue;
+
+ mode = igt_output_get_mode(output);
+ break;
+ }
+
+ if (!mode)
+ return 0;
+
+ igt_output_set_pipe(output, pipe);
+ igt_plane_set_fb(plane, fb);
+ igt_fb_set_size(fb, plane, mode->hdisplay, mode->vdisplay);
+ igt_plane_set_size(plane, mode->hdisplay, mode->vdisplay);
+ }
+
+ return event_mask;
+}
+
+static void refresh_primaries(igt_display_t *display)
+{
+ enum pipe pipe;
+ igt_plane_t *plane;
+
+ for_each_pipe(display, pipe)
+ for_each_plane_on_pipe(display, pipe, plane)
+ if (plane->is_primary && plane->fb)
+ plane->fb_changed = true;
+}
+
+static void collect_crcs_mask(igt_pipe_crc_t **pipe_crcs, unsigned mask, igt_crc_t *crcs)
+{
+ int i;
+
+ for (i = 0; i < I915_MAX_PIPES; i++) {
+ if (!((1 << i) & mask))
+ continue;
+
+ if (!pipe_crcs[i])
+ continue;
+
+ igt_pipe_crc_collect_crc(pipe_crcs[i], &crcs[i]);
+ }
+}
+
+static void run_modeset_tests(igt_display_t *display, int howmany, bool nonblocking)
+{
+ struct igt_fb fbs[2];
+ int i, j, ret = 0;
+ unsigned iter_max = 1 << I915_MAX_PIPES;
+ igt_pipe_crc_t *pipe_crcs[I915_MAX_PIPES];
+ igt_output_t *output;
+ unsigned width = 0, height = 0;
+
+ for_each_connected_output(display, output) {
+ drmModeModeInfo *mode = igt_output_get_mode(output);
+
+ igt_output_set_pipe(output, PIPE_NONE);
+
+ width = max(width, mode->hdisplay);
+ height = max(height, mode->vdisplay);
+ }
+
+ igt_create_pattern_fb(display->drm_fd, width, height,
+ DRM_FORMAT_XRGB8888, 0, &fbs[0]);
+ igt_create_color_pattern_fb(display->drm_fd, width, height,
+ DRM_FORMAT_XRGB8888, 0, .5, .5, .5, &fbs[1]);
+
+ for_each_pipe(display, i) {
+ igt_plane_t *plane = &display->pipes[i].planes[IGT_PLANE_PRIMARY];
+ drmModeModeInfo *mode = NULL;
+
+ if (is_i915_device(display->drm_fd))
+ pipe_crcs[i] = igt_pipe_crc_new(i, INTEL_PIPE_CRC_SOURCE_AUTO);
+
+ for_each_valid_output_on_pipe(display, i, output) {
+ if (output->pending_crtc_idx_mask)
+ continue;
+
+ igt_output_set_pipe(output, i);
+ mode = igt_output_get_mode(output);
+ break;
+ }
+
+ if (mode) {
+ igt_plane_set_fb(plane, &fbs[1]);
+ igt_fb_set_size(&fbs[1], plane, mode->hdisplay, mode->vdisplay);
+ igt_plane_set_size(plane, mode->hdisplay, mode->vdisplay);
+ } else
+ igt_plane_set_fb(plane, NULL);
+ }
+
+ /*
+ * When i915 supports nonblocking modeset, this if branch can be removed.
+ * It's only purpose is to ensure nonblocking modeset works.
+ */
+ if (nonblocking) {
+ /*
+ * Make sure we only skip when the suggested configuration is
+ * unsupported by committing it first with TEST_ONLY, if it's
+ * unsupported -EINVAL is returned. If the second commit returns
+ * -EINVAL, it's from not being able to support nonblocking modeset.
+ */
+ igt_display_commit_atomic(display, DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
+
+ ret = igt_display_try_commit_atomic(display, DRM_MODE_ATOMIC_ALLOW_MODESET | DRM_MODE_ATOMIC_NONBLOCK, NULL);
+
+ if (ret == -EINVAL)
+ goto cleanup;
+
+ igt_assert_eq(ret, 0);
+
+ /*
+ * We don't know the initial state, so we can't tell for sure how many events we would get.
+ * Create a new atomic state with all crtcs updated and commit it synchronously.
+ */
+ for_each_pipe(display, i)
+ display->pipes[i].mode_changed = true;
+ }
+
+ igt_display_commit2(display, COMMIT_ATOMIC);
+
+ for (i = 0; i < iter_max; i++) {
+ igt_crc_t crcs[5][I915_MAX_PIPES];
+ unsigned event_mask;
+
+ if (hweight32(i) > howmany)
+ continue;
+
+ event_mask = set_combinations(display, i, &fbs[0]);
+ if (!event_mask && i)
+ continue;
+
+ commit_display(display, event_mask, nonblocking);
+
+ collect_crcs_mask(pipe_crcs, i, crcs[0]);
+
+ for (j = iter_max - 1; j > i + 1; j--) {
+ if (hweight32(j) > howmany)
+ continue;
+
+ if (hweight32(i) < howmany && hweight32(j) < howmany)
+ continue;
+
+ event_mask = set_combinations(display, j, &fbs[1]);
+ if (!event_mask)
+ continue;
+
+ commit_display(display, event_mask, nonblocking);
+
+ collect_crcs_mask(pipe_crcs, j, crcs[1]);
+
+ refresh_primaries(display);
+ commit_display(display, j, nonblocking);
+ collect_crcs_mask(pipe_crcs, j, crcs[2]);
+
+ event_mask = set_combinations(display, i, &fbs[0]);
+ if (!event_mask)
+ continue;
+
+ commit_display(display, event_mask, nonblocking);
+ collect_crcs_mask(pipe_crcs, i, crcs[3]);
+
+ refresh_primaries(display);
+ commit_display(display, i, nonblocking);
+ collect_crcs_mask(pipe_crcs, i, crcs[4]);
+
+ if (!is_i915_device(display->drm_fd))
+ continue;
+
+ for (int k = 0; k < I915_MAX_PIPES; k++) {
+ if (i & (1 << k)) {
+ igt_assert_crc_equal(&crcs[0][k], &crcs[3][k]);
+ igt_assert_crc_equal(&crcs[0][k], &crcs[4][k]);
+ }
+
+ if (j & (1 << k))
+ igt_assert_crc_equal(&crcs[1][k], &crcs[2][k]);
+ }
+ }
+ }
+
+cleanup:
+ set_combinations(display, 0, NULL);
+ igt_display_commit2(display, COMMIT_ATOMIC);
+
+ if (is_i915_device(display->drm_fd))
+ for_each_pipe(display, i)
+ igt_pipe_crc_free(pipe_crcs[i]);
+
+ igt_remove_fb(display->drm_fd, &fbs[1]);
+ igt_remove_fb(display->drm_fd, &fbs[0]);
+
+ if (ret == -EINVAL)
+ igt_skip("Atomic nonblocking modesets are not supported.\n");
+
+}
+
+static void run_modeset_transition(igt_display_t *display, int requested_outputs, bool nonblocking)
+{
+ igt_output_t *outputs[I915_MAX_PIPES] = {};
+ int num_outputs = 0;
+ enum pipe pipe;
+
+ for_each_pipe(display, pipe) {
+ igt_output_t *output;
+
+ for_each_valid_output_on_pipe(display, pipe, output) {
+ int i;
+
+ for (i = pipe - 1; i >= 0; i--)
+ if (outputs[i] == output)
+ break;
+
+ if (i < 0) {
+ outputs[pipe] = output;
+ num_outputs++;
+ break;
+ }
+ }
+ }
+
+ igt_require_f(num_outputs >= requested_outputs,
+ "Should have at least %i outputs, found %i\n",
+ requested_outputs, num_outputs);
+
+ run_modeset_tests(display, requested_outputs, nonblocking);
+}
+
igt_main
{
igt_display_t display;
igt_output_t *output;
enum pipe pipe;
+ int i;
igt_skip_on_simulation();
@@ -227,14 +525,22 @@ igt_main
igt_require_f(valid_outputs, "no valid crtc/connector combinations found\n");
}
- igt_subtest_f("plane-all-transition")
+ igt_subtest("plane-all-transition")
for_each_pipe_with_valid_output(&display, pipe, output)
run_transition_test(&display, pipe, output, false);
- igt_subtest_f("plane-modeset-transition")
+ igt_subtest("plane-modeset-transition")
for_each_pipe_with_valid_output(&display, pipe, output)
run_transition_test(&display, pipe, output, true);
+ for (i = 1; i <= I915_MAX_PIPES; i++) {
+ igt_subtest_f("%ix-modeset-transitions", i)
+ run_modeset_transition(&display, i, false);
+
+ igt_subtest_f("%ix-modeset-transitions-nonblocking", i)
+ run_modeset_transition(&display, i, true);
+ }
+
igt_fixture {
igt_display_fini(&display);
}