blob: 1540daa29316b3a49743eee0fa1a963400a6aa0a [file] [log] [blame]
/*
* 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 <errno.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <poll.h>
#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);
}
}