/* * Copyright © 2016 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_rand.h" #include "drmtest.h" #include "sw_sync.h" #include #include #include #include #include #include #include #ifndef DRM_CAP_CURSOR_WIDTH #define DRM_CAP_CURSOR_WIDTH 0x8 #endif #ifndef DRM_CAP_CURSOR_HEIGHT #define DRM_CAP_CURSOR_HEIGHT 0x9 #endif struct plane_parms { struct igt_fb *fb; uint32_t width, height, mask; }; /* globals for fence support */ int *timeline; pthread_t *thread; int *seqno; static void run_primary_test(igt_display_t *display, enum pipe pipe, igt_output_t *output) { drmModeModeInfo *mode; igt_plane_t *primary; igt_fb_t fb; int i, ret; unsigned flags = DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_ALLOW_MODESET; igt_output_set_pipe(output, pipe); primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY); mode = igt_output_get_mode(output); igt_plane_set_fb(primary, NULL); ret = igt_display_try_commit_atomic(display, flags, NULL); igt_skip_on_f(ret == -EINVAL, "Primary plane cannot be disabled separately from output\n"); igt_create_fb(display->drm_fd, mode->hdisplay, mode->vdisplay, DRM_FORMAT_XRGB8888, LOCAL_DRM_FORMAT_MOD_NONE, &fb); igt_plane_set_fb(primary, &fb); for (i = 0; i < 4; i++) { igt_display_commit2(display, COMMIT_ATOMIC); if (!(i & 1)) igt_wait_for_vblank(display->drm_fd, pipe); igt_plane_set_fb(primary, (i & 1) ? &fb : NULL); igt_display_commit2(display, COMMIT_ATOMIC); if (i & 1) igt_wait_for_vblank(display->drm_fd, pipe); igt_plane_set_fb(primary, (i & 1) ? NULL : &fb); } igt_plane_set_fb(primary, NULL); igt_output_set_pipe(output, PIPE_NONE); igt_remove_fb(display->drm_fd, &fb); } static void *fence_inc_thread(void *arg) { int t = *((int *) arg); pthread_detach(pthread_self()); usleep(5000); sw_sync_timeline_inc(t, 1); return NULL; } static void configure_fencing(igt_plane_t *plane) { int i, fd, ret; i = plane->index; seqno[i]++; fd = sw_sync_timeline_create_fence(timeline[i], seqno[i]); igt_plane_set_fence_fd(plane, fd); close(fd); ret = pthread_create(&thread[i], NULL, fence_inc_thread, &timeline[i]); igt_assert_eq(ret, 0); } static int wm_setup_plane(igt_display_t *display, enum pipe pipe, uint32_t mask, struct plane_parms *parms, bool fencing) { igt_plane_t *plane; int planes_set_up = 0; /* * Make sure these buffers are suited for display use * because most of the modeset operations must be fast * later on. */ for_each_plane_on_pipe(display, pipe, plane) { int i = plane->index; if (!mask || !(parms[i].mask & mask)) { if (plane->values[IGT_PLANE_FB_ID]) { igt_plane_set_fb(plane, NULL); planes_set_up++; } continue; } if (fencing) configure_fencing(plane); igt_plane_set_fb(plane, parms[i].fb); igt_fb_set_size(parms[i].fb, plane, parms[i].width, parms[i].height); igt_plane_set_size(plane, parms[i].width, parms[i].height); planes_set_up++; } return planes_set_up; } static void ev_page_flip(int fd, unsigned seq, unsigned tv_sec, unsigned tv_usec, void *user_data) { igt_debug("Retrieved vblank seq: %u on unk\n", seq); } static drmEventContext drm_events = { .version = 2, .page_flip_handler = ev_page_flip }; enum transition_type { TRANSITION_PLANES, TRANSITION_AFTER_FREE, TRANSITION_MODESET, TRANSITION_MODESET_FAST, TRANSITION_MODESET_DISABLE, }; static void set_sprite_wh(igt_display_t *display, enum pipe pipe, struct plane_parms *parms, struct igt_fb *sprite_fb, bool alpha, unsigned w, unsigned h) { igt_plane_t *plane; for_each_plane_on_pipe(display, pipe, plane) { int i = plane->index; if (plane->type == DRM_PLANE_TYPE_PRIMARY || plane->type == DRM_PLANE_TYPE_CURSOR) continue; if (!parms[i].mask) continue; parms[i].width = w; parms[i].height = h; } igt_remove_fb(display->drm_fd, sprite_fb); igt_create_fb(display->drm_fd, w, h, alpha ? DRM_FORMAT_ARGB8888 : DRM_FORMAT_XRGB8888, LOCAL_DRM_FORMAT_MOD_NONE, sprite_fb); } #define is_atomic_check_failure_errno(errno) \ (errno != -EINVAL && errno != 0) #define is_atomic_check_plane_size_errno(errno) \ (errno == -EINVAL) static void setup_parms(igt_display_t *display, enum pipe pipe, const drmModeModeInfo *mode, struct igt_fb *primary_fb, struct igt_fb *argb_fb, struct igt_fb *sprite_fb, struct plane_parms *parms, unsigned *iter_max) { uint64_t cursor_width, cursor_height; unsigned sprite_width, sprite_height, prev_w, prev_h; bool max_sprite_width, max_sprite_height, alpha = true; uint32_t n_planes = display->pipes[pipe].n_planes; uint32_t n_overlays = 0, overlays[n_planes]; igt_plane_t *plane; uint32_t iter_mask = 3; do_or_die(drmGetCap(display->drm_fd, DRM_CAP_CURSOR_WIDTH, &cursor_width)); if (cursor_width >= mode->hdisplay) cursor_width = mode->hdisplay; do_or_die(drmGetCap(display->drm_fd, DRM_CAP_CURSOR_HEIGHT, &cursor_height)); if (cursor_height >= mode->vdisplay) cursor_height = mode->vdisplay; for_each_plane_on_pipe(display, pipe, plane) { int i = plane->index; if (plane->type == DRM_PLANE_TYPE_PRIMARY) { parms[i].fb = primary_fb; parms[i].width = mode->hdisplay; parms[i].height = mode->vdisplay; parms[i].mask = 1 << 0; } else if (plane->type == DRM_PLANE_TYPE_CURSOR) { parms[i].fb = argb_fb; parms[i].width = cursor_width; parms[i].height = cursor_height; parms[i].mask = 1 << 1; } else { if (!n_overlays) alpha = igt_plane_has_format_mod(plane, DRM_FORMAT_ARGB8888, LOCAL_DRM_FORMAT_MOD_NONE); parms[i].fb = sprite_fb; parms[i].mask = 1 << 2; iter_mask |= 1 << 2; overlays[n_overlays++] = i; } } if (n_overlays >= 2) { uint32_t i; /* * Create 2 groups for overlays, make sure 1 plane is put * in each then spread the rest out. */ iter_mask |= 1 << 3; parms[overlays[n_overlays - 1]].mask = 1 << 3; for (i = 1; i < n_overlays - 1; i++) { int val = hars_petruska_f54_1_random_unsafe_max(2); parms[overlays[i]].mask = 1 << (2 + val); } } igt_create_fb(display->drm_fd, cursor_width, cursor_height, DRM_FORMAT_ARGB8888, LOCAL_DRM_FORMAT_MOD_NONE, argb_fb); igt_create_fb(display->drm_fd, cursor_width, cursor_height, DRM_FORMAT_ARGB8888, LOCAL_DRM_FORMAT_MOD_NONE, sprite_fb); *iter_max = iter_mask + 1; if (!n_overlays) return; /* * Pre gen9 not all sizes are supported, find the biggest possible * size that can be enabled on all sprite planes. */ prev_w = sprite_width = cursor_width; prev_h = sprite_height = cursor_height; max_sprite_width = (sprite_width == mode->hdisplay); max_sprite_height = (sprite_height == mode->vdisplay); while (!max_sprite_width && !max_sprite_height) { int ret; set_sprite_wh(display, pipe, parms, sprite_fb, alpha, sprite_width, sprite_height); wm_setup_plane(display, pipe, (1 << n_planes) - 1, parms, false); ret = igt_display_try_commit_atomic(display, DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); igt_assert(!is_atomic_check_failure_errno(ret)); if (!is_atomic_check_plane_size_errno(ret)) { prev_w = sprite_width; prev_h = sprite_height; sprite_width *= max_sprite_width ? 1 : 2; if (sprite_width >= mode->hdisplay) { max_sprite_width = true; sprite_width = mode->hdisplay; } sprite_height *= max_sprite_height ? 1 : 2; if (sprite_height >= mode->vdisplay) { max_sprite_height = true; sprite_height = mode->vdisplay; } continue; } if (cursor_width == sprite_width && cursor_height == sprite_height) { igt_plane_t *removed_plane = NULL; igt_assert_f(n_planes >= 3, "No planes left to proceed with!"); if (n_overlays > 0) { uint32_t plane_to_remove = hars_petruska_f54_1_random_unsafe_max(n_overlays); removed_plane = &display->pipes[pipe].planes[overlays[plane_to_remove]]; igt_plane_set_fb(removed_plane, NULL); while (plane_to_remove < (n_overlays - 1)) { overlays[plane_to_remove] = overlays[plane_to_remove + 1]; plane_to_remove++; } n_overlays--; } if (removed_plane) { parms[removed_plane->index].mask = 0; igt_info("Removed plane %d\n", removed_plane->index); } n_planes--; igt_info("Reduced available planes to %d\n", n_planes); continue; } sprite_width = prev_w; sprite_height = prev_h; if (!max_sprite_width) max_sprite_width = true; else max_sprite_height = true; } set_sprite_wh(display, pipe, parms, sprite_fb, alpha, sprite_width, sprite_height); igt_info("Running test on pipe %s with resolution %dx%d and sprite size %dx%d alpha %i\n", kmstest_pipe_name(pipe), mode->hdisplay, mode->vdisplay, sprite_width, sprite_height, alpha); } static void prepare_fencing(igt_display_t *display, enum pipe pipe) { igt_plane_t *plane; int n_planes; igt_require_sw_sync(); n_planes = display->pipes[pipe].n_planes; timeline = calloc(sizeof(*timeline), n_planes); igt_assert_f(timeline != NULL, "Failed to allocate memory for timelines\n"); thread = calloc(sizeof(*thread), n_planes); igt_assert_f(thread != NULL, "Failed to allocate memory for thread\n"); seqno = calloc(sizeof(*seqno), n_planes); igt_assert_f(seqno != NULL, "Failed to allocate memory for seqno\n"); for_each_plane_on_pipe(display, pipe, plane) timeline[plane->index] = sw_sync_timeline_create(); } static void unprepare_fencing(igt_display_t *display, enum pipe pipe) { igt_plane_t *plane; for_each_plane_on_pipe(display, pipe, plane) close(timeline[plane->index]); free(timeline); free(thread); free(seqno); } static void atomic_commit(igt_display_t *display, enum pipe pipe, unsigned int flags, void *data, bool fencing) { if (fencing) igt_pipe_request_out_fence(&display->pipes[pipe]); igt_display_commit_atomic(display, flags, data); } static int fd_completed(int fd) { struct pollfd pfd = { fd, POLLIN }; int ret; ret = poll(&pfd, 1, 0); igt_assert(ret >= 0); return ret; } static void wait_for_transition(igt_display_t *display, enum pipe pipe, bool nonblocking, bool fencing) { if (fencing) { int fence_fd = display->pipes[pipe].out_fence_fd; if (!nonblocking) igt_assert(fd_completed(fence_fd)); igt_assert(sync_fence_wait(fence_fd, 30000) == 0); } else { if (!nonblocking) igt_assert(fd_completed(display->drm_fd)); drmHandleEvent(display->drm_fd, &drm_events); } } /* * 1. Set primary plane to a known fb. * 2. Make sure getcrtc returns the correct fb id. * 3. Call rmfb on the fb. * 4. Make sure getcrtc returns 0 fb id. * * RMFB is supposed to free the framebuffers from any and all planes, * so test this and make sure it works. */ static void run_transition_test(igt_display_t *display, enum pipe pipe, igt_output_t *output, enum transition_type type, bool nonblocking, bool fencing) { struct igt_fb fb, argb_fb, sprite_fb; drmModeModeInfo *mode, override_mode; igt_plane_t *plane; igt_pipe_t *pipe_obj = &display->pipes[pipe]; uint32_t iter_max, i; struct plane_parms parms[pipe_obj->n_planes]; unsigned flags = 0; int ret; if (fencing) prepare_fencing(display, pipe); else flags |= DRM_MODE_PAGE_FLIP_EVENT; if (nonblocking) flags |= DRM_MODE_ATOMIC_NONBLOCK; if (type >= TRANSITION_MODESET) flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; mode = igt_output_get_mode(output); override_mode = *mode; /* try to force a modeset */ override_mode.flags ^= DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NHSYNC; igt_create_fb(display->drm_fd, mode->hdisplay, mode->vdisplay, DRM_FORMAT_XRGB8888, LOCAL_DRM_FORMAT_MOD_NONE, &fb); igt_output_set_pipe(output, pipe); wm_setup_plane(display, pipe, 0, NULL, false); if (flags & DRM_MODE_ATOMIC_ALLOW_MODESET) { igt_output_set_pipe(output, PIPE_NONE); igt_display_commit2(display, COMMIT_ATOMIC); igt_output_set_pipe(output, pipe); } igt_display_commit2(display, COMMIT_ATOMIC); setup_parms(display, pipe, mode, &fb, &argb_fb, &sprite_fb, parms, &iter_max); /* * In some configurations the tests may not run to completion with all * sprite planes lit up at 4k resolution, try decreasing width/size of secondary * planes to fix this */ while (1) { wm_setup_plane(display, pipe, iter_max - 1, parms, false); if (fencing) igt_pipe_request_out_fence(pipe_obj); ret = igt_display_try_commit_atomic(display, DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); igt_assert(!is_atomic_check_failure_errno(ret)); if (!is_atomic_check_plane_size_errno(ret) || pipe_obj->n_planes < 3) break; ret = 0; for_each_plane_on_pipe(display, pipe, plane) { i = plane->index; if (plane->type == DRM_PLANE_TYPE_PRIMARY || plane->type == DRM_PLANE_TYPE_CURSOR) continue; parms[i].width /= 2; ret = 1; igt_info("Reducing sprite %i to %ux%u\n", i - 1, parms[i].width, parms[i].height); break; } if (!ret) igt_skip("Cannot run tests without proper size sprite planes\n"); } igt_display_commit2(display, COMMIT_ATOMIC); if (type == TRANSITION_AFTER_FREE) { int fence_fd = -1; wm_setup_plane(display, pipe, 0, parms, fencing); atomic_commit(display, pipe, flags, (void *)(unsigned long)0, fencing); if (fencing) { fence_fd = pipe_obj->out_fence_fd; pipe_obj->out_fence_fd = -1; } /* force planes to be part of commit */ for_each_plane_on_pipe(display, pipe, plane) { if (parms[plane->index].mask) igt_plane_set_position(plane, 0, 0); } igt_display_commit2(display, COMMIT_ATOMIC); if (fence_fd != -1) { igt_assert(fd_completed(fence_fd)); close(fence_fd); } else { igt_assert(fd_completed(display->drm_fd)); wait_for_transition(display, pipe, false, fencing); } goto cleanup; } for (i = 0; i < iter_max; i++) { int n_enable_planes = igt_hweight(i); if (type == TRANSITION_MODESET_FAST && n_enable_planes > 1 && n_enable_planes < pipe_obj->n_planes) continue; igt_output_set_pipe(output, pipe); if (!wm_setup_plane(display, pipe, i, parms, fencing)) continue; atomic_commit(display, pipe, flags, (void *)(unsigned long)i, fencing); wait_for_transition(display, pipe, nonblocking, fencing); if (type == TRANSITION_MODESET_DISABLE) { igt_output_set_pipe(output, PIPE_NONE); if (!wm_setup_plane(display, pipe, 0, parms, fencing)) continue; atomic_commit(display, pipe, flags, (void *) 0UL, fencing); wait_for_transition(display, pipe, nonblocking, fencing); } else { uint32_t j; /* i -> i+1 will be done when i increases, can be skipped here */ for (j = iter_max - 1; j > i + 1; j--) { n_enable_planes = igt_hweight(j); if (type == TRANSITION_MODESET_FAST && n_enable_planes > 1 && n_enable_planes < pipe_obj->n_planes) continue; if (!wm_setup_plane(display, pipe, j, parms, fencing)) continue; if (type >= TRANSITION_MODESET) igt_output_override_mode(output, &override_mode); atomic_commit(display, pipe, flags, (void *)(unsigned long) j, fencing); wait_for_transition(display, pipe, nonblocking, fencing); if (!wm_setup_plane(display, pipe, i, parms, fencing)) continue; if (type >= TRANSITION_MODESET) igt_output_override_mode(output, NULL); atomic_commit(display, pipe, flags, (void *)(unsigned long) i, fencing); wait_for_transition(display, pipe, nonblocking, fencing); } } } cleanup: if (fencing) unprepare_fencing(display, pipe); igt_output_set_pipe(output, PIPE_NONE); for_each_plane_on_pipe(display, pipe, plane) igt_plane_set_fb(plane, NULL); igt_display_commit2(display, COMMIT_ATOMIC); igt_remove_fb(display->drm_fd, &fb); igt_remove_fb(display->drm_fd, &argb_fb); igt_remove_fb(display->drm_fd, &sprite_fb); } static void commit_display(igt_display_t *display, unsigned event_mask, bool nonblocking) { unsigned flags; int num_events = igt_hweight(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); igt_debug("Event mask: %x, waiting for %i events\n", event_mask, num_events); igt_set_timeout(30, "Waiting for events timed out\n"); while (num_events) { char buf[32]; struct drm_event *e = (void *)buf; struct drm_event_vblank *vblank = (void *)buf; igt_set_timeout(3, "Timed out while reading drm_fd\n"); ret = read(display->drm_fd, buf, sizeof(buf)); igt_reset_timeout(); if (ret < 0 && (errno == EINTR || errno == EAGAIN)) continue; igt_assert(ret >= 0); igt_assert_eq(e->type, DRM_EVENT_FLIP_COMPLETE); igt_debug("Retrieved vblank seq: %u on unk/unk\n", vblank->sequence); num_events--; } igt_reset_timeout(); } 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 = igt_pipe_get_plane_type(&display->pipes[pipe], DRM_PLANE_TYPE_PRIMARY); drmModeModeInfo *mode = NULL; if (!(mask & (1 << pipe))) { if (igt_pipe_is_prop_changed(display, pipe, IGT_CRTC_ACTIVE)) { 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_pipe != PIPE_NONE) 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, int mask) { enum pipe pipe; igt_plane_t *plane; for_each_pipe(display, pipe) { if (!((1 << pipe) & mask)) continue; for_each_plane_on_pipe(display, pipe, plane) if (plane->type == DRM_PLANE_TYPE_PRIMARY) igt_plane_set_position(plane, 0, 0); } } static void collect_crcs_mask(igt_pipe_crc_t **pipe_crcs, unsigned mask, igt_crc_t *crcs) { int i; for (i = 0; i < IGT_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, bool fencing) { struct igt_fb fbs[2]; int i, j; unsigned iter_max = 1 << display->n_pipes; igt_pipe_crc_t *pipe_crcs[IGT_MAX_PIPES] = { 0 }; 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_pipe_t *pipe = &display->pipes[i]; igt_plane_t *plane = igt_pipe_get_plane_type(pipe, DRM_PLANE_TYPE_PRIMARY); drmModeModeInfo *mode = NULL; if (is_i915_device(display->drm_fd)) pipe_crcs[i] = igt_pipe_crc_new(display->drm_fd, i, INTEL_PIPE_CRC_SOURCE_AUTO); for_each_valid_output_on_pipe(display, i, output) { if (output->pending_pipe != PIPE_NONE) 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); if (fencing) igt_pipe_request_out_fence(&display->pipes[i]); } else igt_plane_set_fb(plane, NULL); } igt_display_commit2(display, COMMIT_ATOMIC); for (i = 0; i < iter_max; i++) { igt_crc_t crcs[5][IGT_MAX_PIPES]; unsigned event_mask; if (igt_hweight(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 (igt_hweight(j) > howmany) continue; if (igt_hweight(i) < howmany && igt_hweight(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, j); 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, i); 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 < IGT_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]); } } } 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]); } static void run_modeset_transition(igt_display_t *display, int requested_outputs, bool nonblocking, bool fencing) { igt_output_t *outputs[IGT_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, fencing); } static bool output_is_internal_panel(igt_output_t *output) { switch (output->config.connector->connector_type) { case DRM_MODE_CONNECTOR_LVDS: case DRM_MODE_CONNECTOR_eDP: case DRM_MODE_CONNECTOR_DSI: case DRM_MODE_CONNECTOR_DPI: return true; default: return false; } } igt_main { igt_display_t display; igt_output_t *output; enum pipe pipe; int i; igt_skip_on_simulation(); igt_fixture { display.drm_fd = drm_open_driver_master(DRIVER_ANY); kmstest_set_vt_graphics_mode(); igt_display_require(&display, display.drm_fd); igt_require(display.is_atomic); igt_display_require_output(&display); } igt_subtest("plane-primary-toggle-with-vblank-wait") for_each_pipe_with_valid_output(&display, pipe, output) run_primary_test(&display, pipe, output); igt_subtest("plane-all-transition") for_each_pipe_with_valid_output(&display, pipe, output) run_transition_test(&display, pipe, output, TRANSITION_PLANES, false, false); igt_subtest("plane-all-transition-fencing") for_each_pipe_with_valid_output(&display, pipe, output) run_transition_test(&display, pipe, output, TRANSITION_PLANES, false, true); igt_subtest("plane-all-transition-nonblocking") for_each_pipe_with_valid_output(&display, pipe, output) run_transition_test(&display, pipe, output, TRANSITION_PLANES, true, false); igt_subtest("plane-all-transition-nonblocking-fencing") for_each_pipe_with_valid_output(&display, pipe, output) run_transition_test(&display, pipe, output, TRANSITION_PLANES, true, true); igt_subtest("plane-use-after-nonblocking-unbind") for_each_pipe_with_valid_output(&display, pipe, output) run_transition_test(&display, pipe, output, TRANSITION_AFTER_FREE, true, false); igt_subtest("plane-use-after-nonblocking-unbind-fencing") for_each_pipe_with_valid_output(&display, pipe, output) run_transition_test(&display, pipe, output, TRANSITION_AFTER_FREE, true, true); /* * Test modeset cases on internal panels separately with a reduced * number of combinations, to avoid long runtimes due to modesets on * panels with long power cycle delays. */ igt_subtest("plane-all-modeset-transition") for_each_pipe_with_valid_output(&display, pipe, output) { if (output_is_internal_panel(output)) continue; run_transition_test(&display, pipe, output, TRANSITION_MODESET, false, false); } igt_subtest("plane-all-modeset-transition-fencing") for_each_pipe_with_valid_output(&display, pipe, output) { if (output_is_internal_panel(output)) continue; run_transition_test(&display, pipe, output, TRANSITION_MODESET, false, true); } igt_subtest("plane-all-modeset-transition-internal-panels") { int tested = 0; for_each_pipe_with_valid_output(&display, pipe, output) { if (!output_is_internal_panel(output)) continue; run_transition_test(&display, pipe, output, TRANSITION_MODESET_FAST, false, false); tested++; } igt_skip_on_f(!tested, "No output with internal panel found\n"); } igt_subtest("plane-all-modeset-transition-fencing-internal-panels") { int tested = 0; for_each_pipe_with_valid_output(&display, pipe, output) { if (!output_is_internal_panel(output)) continue; run_transition_test(&display, pipe, output, TRANSITION_MODESET_FAST, false, true); tested++; } igt_skip_on_f(!tested, "No output with internal panel found\n"); } igt_subtest("plane-toggle-modeset-transition") for_each_pipe_with_valid_output(&display, pipe, output) run_transition_test(&display, pipe, output, TRANSITION_MODESET_DISABLE, false, false); for (i = 1; i <= IGT_MAX_PIPES; i++) { igt_subtest_f("%ix-modeset-transitions", i) run_modeset_transition(&display, i, false, false); igt_subtest_f("%ix-modeset-transitions-nonblocking", i) run_modeset_transition(&display, i, true, false); igt_subtest_f("%ix-modeset-transitions-fencing", i) run_modeset_transition(&display, i, false, true); igt_subtest_f("%ix-modeset-transitions-nonblocking-fencing", i) run_modeset_transition(&display, i, true, true); } igt_fixture { igt_display_fini(&display); } }