diff options
author | Daniel Vetter <daniel.vetter@ffwll.ch> | 2012-12-15 00:48:47 +0100 |
---|---|---|
committer | Daniel Vetter <daniel.vetter@ffwll.ch> | 2012-12-17 15:30:53 +0100 |
commit | acdf7171264a5139caabb78127c9eef5080c0682 (patch) | |
tree | 96ec8f4619bdb7bef2f844c80e416342a7dabd9c /tests/flip_test.c | |
parent | f56384c2c0264989acae7b8c7efb5030a6c94caf (diff) |
tests: s/flip_test/kms_flip
OCD wins!
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Diffstat (limited to 'tests/flip_test.c')
-rw-r--r-- | tests/flip_test.c | 1066 |
1 files changed, 0 insertions, 1066 deletions
diff --git a/tests/flip_test.c b/tests/flip_test.c deleted file mode 100644 index e05efb1..0000000 --- a/tests/flip_test.c +++ /dev/null @@ -1,1066 +0,0 @@ -/* - * Copyright 2012 Intel Corporation - * Jesse Barnes <jesse.barnes@intel.com> - * - * 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 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 "config.h" - -#include <assert.h> -#include <cairo.h> -#include <errno.h> -#include <math.h> -#include <stdint.h> -#include <unistd.h> -#include <sys/poll.h> -#include <sys/time.h> -#include <sys/mman.h> -#include <sys/ioctl.h> - -#include "i915_drm.h" -#include "drmtest.h" -#include "testdisplay.h" -#include "intel_bufmgr.h" -#include "intel_batchbuffer.h" -#include "intel_gpu_tools.h" - -#define TEST_DPMS (1 << 0) -#define TEST_WITH_DUMMY_LOAD (1 << 1) -#define TEST_PAN (1 << 2) -#define TEST_MODESET (1 << 3) -#define TEST_CHECK_TS (1 << 4) -#define TEST_EBUSY (1 << 5) -#define TEST_EINVAL (1 << 6) -#define TEST_FLIP (1 << 7) -#define TEST_VBLANK (1 << 8) -#define TEST_VBLANK_BLOCK (1 << 9) -#define TEST_VBLANK_ABSOLUTE (1 << 10) -#define TEST_VBLANK_EXPIRED_SEQ (1 << 11) -#define TEST_FB_RECREATE (1 << 12) -#define TEST_RMFB (1 << 13) - -#define EVENT_FLIP (1 << 0) -#define EVENT_VBLANK (1 << 1) - -#ifndef DRM_CAP_TIMESTAMP_MONOTONIC -#define DRM_CAP_TIMESTAMP_MONOTONIC 6 -#endif - -drmModeRes *resources; -int drm_fd; -static drm_intel_bufmgr *bufmgr; -struct intel_batchbuffer *batch; -uint32_t devid; -int test_time = 3; -static bool monotonic_timestamp; - -uint32_t *fb_ptr; - -struct type_name { - int type; - const char *name; -}; - -struct event_state { - const char *name; - - /* - * Event data for the last event that has already passed our check. - * Updated using the below current_* vars in update_state(). - */ - struct timeval last_ts; /* kernel reported timestamp */ - struct timeval last_received_ts; /* the moment we received it */ - unsigned int last_seq; /* kernel reported seq. num */ - - /* - * Event data for for the current event that we just received and - * going to check for validity. Set in event_handler(). - */ - struct timeval current_ts; /* kernel reported timestamp */ - struct timeval current_received_ts; /* the moment we received it */ - unsigned int current_seq; /* kernel reported seq. num */ - - int count; /* # of events of this type */ - - /* Step between the current and next 'target' sequence number. */ - int seq_step; -}; - -struct test_output { - const char *test_name; - uint32_t id; - int mode_valid; - drmModeModeInfo mode; - drmModeEncoder *encoder; - drmModeConnector *connector; - int crtc; - int pipe; - int flags; - unsigned int current_fb_id; - unsigned int fb_width; - unsigned int fb_height; - unsigned int fb_ids[2]; - int bpp, depth; - struct kmstest_fb fb_info[2]; - - struct event_state flip_state; - struct event_state vblank_state; - unsigned int pending_events; -}; - - -static unsigned long gettime_us(void) -{ - struct timespec ts; - - clock_gettime(CLOCK_MONOTONIC, &ts); - - return ts.tv_sec * 1000000 + ts.tv_nsec / 1000; -} - -static void emit_dummy_load(struct test_output *o) -{ - int i, limit; - drm_intel_bo *dummy_bo, *target_bo, *tmp_bo; - struct kmstest_fb *fb_info = &o->fb_info[o->current_fb_id]; - unsigned pitch = fb_info->stride; - - limit = intel_gen(devid) < 6 ? 500 : 5000; - - dummy_bo = drm_intel_bo_alloc(bufmgr, "dummy_bo", fb_info->size, 4096); - assert(dummy_bo); - target_bo = gem_handle_to_libdrm_bo(bufmgr, drm_fd, "imported", fb_info->gem_handle); - assert(target_bo); - - for (i = 0; i < limit; i++) { - BEGIN_BATCH(8); - OUT_BATCH(XY_SRC_COPY_BLT_CMD | - XY_SRC_COPY_BLT_WRITE_ALPHA | - XY_SRC_COPY_BLT_WRITE_RGB); - OUT_BATCH((3 << 24) | /* 32 bits */ - (0xcc << 16) | /* copy ROP */ - pitch); - OUT_BATCH(0 << 16 | 0); - OUT_BATCH((o->mode.vdisplay) << 16 | (o->mode.hdisplay)); - OUT_RELOC_FENCED(dummy_bo, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER, 0); - OUT_BATCH(0 << 16 | 0); - OUT_BATCH(pitch); - OUT_RELOC_FENCED(target_bo, I915_GEM_DOMAIN_RENDER, 0, 0); - ADVANCE_BATCH(); - - if (IS_GEN6(devid) || IS_GEN7(devid)) { - BEGIN_BATCH(3); - OUT_BATCH(XY_SETUP_CLIP_BLT_CMD); - OUT_BATCH(0); - OUT_BATCH(0); - ADVANCE_BATCH(); - } - - tmp_bo = dummy_bo; - dummy_bo = target_bo; - target_bo = tmp_bo; - } - intel_batchbuffer_flush(batch); - - drm_intel_bo_unreference(dummy_bo); - drm_intel_bo_unreference(target_bo); -} - -static int set_dpms(struct test_output *o, int mode) -{ - int i, dpms = 0; - - for (i = 0; i < o->connector->count_props; i++) { - struct drm_mode_get_property prop; - - prop.prop_id = o->connector->props[i]; - prop.count_values = 0; - prop.count_enum_blobs = 0; - if (drmIoctl(drm_fd, DRM_IOCTL_MODE_GETPROPERTY, &prop)) - continue; - - if (strcmp(prop.name, "DPMS")) - continue; - - dpms = prop.prop_id; - break; - } - if (!dpms) { - fprintf(stderr, "DPMS property not found on %d\n", o->id); - errno = ENOENT; - return -1; - } - - return drmModeConnectorSetProperty(drm_fd, o->id, dpms, mode); -} - -static void set_flag(unsigned int *v, unsigned int flag) -{ - assert(!(*v & flag)); - *v |= flag; -} - -static void clear_flag(unsigned int *v, unsigned int flag) -{ - assert(*v & flag); - *v &= ~flag; -} - -static int do_page_flip(struct test_output *o, int fb_id) -{ - int ret; - - ret = drmModePageFlip(drm_fd, o->crtc, fb_id, DRM_MODE_PAGE_FLIP_EVENT, - o); - if (ret == 0) - set_flag(&o->pending_events, EVENT_FLIP); - - return ret; -} - -struct vblank_reply { - unsigned int sequence; - struct timeval ts; -}; - -static int __wait_for_vblank(unsigned int flags, int crtc_idx, - int target_seq, unsigned long ret_data, - struct vblank_reply *reply) -{ - drmVBlank wait_vbl; - int ret; - unsigned crtc_idx_mask; - bool event = !(flags & TEST_VBLANK_BLOCK); - - memset(&wait_vbl, 0, sizeof(wait_vbl)); - - crtc_idx_mask = crtc_idx << DRM_VBLANK_HIGH_CRTC_SHIFT; - assert(!(crtc_idx_mask & ~DRM_VBLANK_HIGH_CRTC_MASK)); - - wait_vbl.request.type = crtc_idx_mask; - if (flags & TEST_VBLANK_ABSOLUTE) - wait_vbl.request.type |= DRM_VBLANK_ABSOLUTE; - else - wait_vbl.request.type |= DRM_VBLANK_RELATIVE; - if (event) { - wait_vbl.request.type |= DRM_VBLANK_EVENT; - wait_vbl.request.signal = ret_data; - } - wait_vbl.request.sequence = target_seq; - - ret = drmWaitVBlank(drm_fd, &wait_vbl); - - if (ret == 0) { - reply->ts.tv_sec = wait_vbl.reply.tval_sec; - reply->ts.tv_usec = wait_vbl.reply.tval_usec; - reply->sequence = wait_vbl.reply.sequence; - } else - ret = -errno; - - return ret; -} - -static int do_wait_for_vblank(struct test_output *o, int pipe_id, - int target_seq, struct vblank_reply *reply) -{ - int ret; - - ret = __wait_for_vblank(o->flags, pipe_id, target_seq, (unsigned long)o, - reply); - if (ret == 0 && !(o->flags & TEST_VBLANK_BLOCK)) - set_flag(&o->pending_events, EVENT_VBLANK); - - return ret; -} - -static bool -analog_tv_connector(struct test_output *o) -{ - uint32_t connector_type = o->connector->connector_type; - - return connector_type == DRM_MODE_CONNECTOR_TV || - connector_type == DRM_MODE_CONNECTOR_9PinDIN || - connector_type == DRM_MODE_CONNECTOR_SVIDEO || - connector_type == DRM_MODE_CONNECTOR_Composite; -} - -static void event_handler(struct event_state *es, unsigned int frame, - unsigned int sec, unsigned int usec) -{ - struct timeval now; - - if (monotonic_timestamp) { - struct timespec ts; - - clock_gettime(CLOCK_MONOTONIC, &ts); - now.tv_sec = ts.tv_sec; - now.tv_usec = ts.tv_nsec / 1000; - } else { - gettimeofday(&now, NULL); - } - es->current_received_ts = now; - - es->current_ts.tv_sec = sec; - es->current_ts.tv_usec = usec; - es->current_seq = frame; -} - -static void page_flip_handler(int fd, unsigned int frame, unsigned int sec, - unsigned int usec, void *data) -{ - struct test_output *o = data; - - clear_flag(&o->pending_events, EVENT_FLIP); - event_handler(&o->flip_state, frame, sec, usec); -} - -static double frame_time(struct test_output *o) -{ - return 1000.0 * 1000.0 / o->mode.vrefresh; -} - -static void fixup_premature_vblank_ts(struct test_output *o, - struct event_state *es) -{ - /* - * In case a power off event preempts the completion of a - * wait-for-vblank event the kernel will return a wf-vblank event with - * a zeroed-out timestamp. In order that check_state() doesn't - * complain replace this ts with a valid ts. As we can't calculate the - * exact timestamp, just use the time we received the event. - */ - struct timeval tv; - - if (!(o->flags & (TEST_DPMS | TEST_MODESET))) - return; - - if (o->vblank_state.current_ts.tv_sec != 0 || - o->vblank_state.current_ts.tv_usec != 0) - return; - - tv.tv_sec = 0; - tv.tv_usec = 1; - timersub(&es->current_received_ts, &tv, &es->current_ts); -} - -static void vblank_handler(int fd, unsigned int frame, unsigned int sec, - unsigned int usec, void *data) -{ - struct test_output *o = data; - - clear_flag(&o->pending_events, EVENT_VBLANK); - event_handler(&o->vblank_state, frame, sec, usec); - fixup_premature_vblank_ts(o, &o->vblank_state); -} - -static void check_state(struct test_output *o, struct event_state *es) -{ - struct timeval diff; - double usec_interflip; - - timersub(&es->current_ts, &es->current_received_ts, &diff); - if ((!analog_tv_connector(o)) && - (diff.tv_sec > 0 || (diff.tv_sec == 0 && diff.tv_usec > 2000))) { - fprintf(stderr, "%s ts delayed for too long: %is, %iusec\n", - es->name, (int)diff.tv_sec, (int)diff.tv_usec); - exit(5); - } - - if (es->count == 0) - return; - - if (!timercmp(&es->last_received_ts, &es->current_ts, <)) { - fprintf(stderr, "%s ts before the %s was issued!\n", - es->name, es->name); - - timersub(&es->current_ts, &es->last_received_ts, &diff); - fprintf(stderr, "timerdiff %is, %ius\n", - (int) diff.tv_sec, (int) diff.tv_usec); - exit(6); - } - - /* This bounding matches the one in DRM_IOCTL_WAIT_VBLANK. */ - if (!(o->flags & (TEST_DPMS | TEST_MODESET))) { - /* check only valid if no modeset happens in between, that - * increments by (1 << 23) on each step. */ - if (es->current_seq - (es->last_seq + es->seq_step) > 1UL << 23) { - fprintf(stderr, "unexpected %s seq %u, should be >= %u\n", - es->name, es->current_seq, es->last_seq + es->seq_step); - exit(10); - } - } - - if ((o->flags & TEST_CHECK_TS) && (!analog_tv_connector(o))) { - timersub(&es->current_ts, &es->last_ts, &diff); - usec_interflip = (double)es->seq_step * frame_time(o); - if (fabs((((double) diff.tv_usec) - usec_interflip) / - usec_interflip) > 0.005) { - fprintf(stderr, "inter-%s ts jitter: %is, %ius\n", - es->name, - (int) diff.tv_sec, (int) diff.tv_usec); - exit(9); - } - - if (es->current_seq != es->last_seq + es->seq_step) { - fprintf(stderr, "unexpected %s seq %u, expected %u\n", - es->name, es->current_seq, - es->last_seq + es->seq_step); - exit(9); - } - } -} - -static void check_state_correlation(struct test_output *o, - struct event_state *es1, - struct event_state *es2) -{ - struct timeval tv_diff; - double ftime; - double usec_diff; - int seq_diff; - - if (es1->count == 0 || es2->count == 0) - return; - - timersub(&es2->current_ts, &es1->current_ts, &tv_diff); - usec_diff = tv_diff.tv_sec * 1000 * 1000 + tv_diff.tv_usec; - - seq_diff = es2->current_seq - es1->current_seq; - ftime = frame_time(o); - usec_diff -= seq_diff * ftime; - - if (fabs(usec_diff) / ftime > 0.005) { - fprintf(stderr, - "timestamp mismatch between %s and %s (diff %.4f sec)\n", - es1->name, es2->name, usec_diff / 1000 / 1000); - exit(14); - } -} - -static void check_all_state(struct test_output *o, - unsigned int completed_events) -{ - bool flip, vblank; - - flip = completed_events & EVENT_FLIP; - vblank = completed_events & EVENT_VBLANK; - - if (flip) - check_state(o, &o->flip_state); - if (vblank) - check_state(o, &o->vblank_state); - - if (flip && vblank) - check_state_correlation(o, &o->flip_state, &o->vblank_state); -} - -static void recreate_fb(struct test_output *o) -{ - drmModeFBPtr r; - struct kmstest_fb *fb_info = &o->fb_info[o->current_fb_id]; - uint32_t new_fb_id; - - /* Call rmfb/getfb/addfb to ensure those don't introduce stalls */ - r = drmModeGetFB(drm_fd, fb_info->fb_id); - assert(r); - - do_or_die(drmModeAddFB(drm_fd, o->fb_width, o->fb_height, o->depth, - o->bpp, fb_info->stride, - r->handle, &new_fb_id)); - - drmFree(r); - gem_close(drm_fd, r->handle); - do_or_die(drmModeRmFB(drm_fd, fb_info->fb_id)); - - o->fb_ids[o->current_fb_id] = new_fb_id; - o->fb_info[o->current_fb_id].fb_id = new_fb_id; -} - -/* Return mask of completed events. */ -static unsigned int run_test_step(struct test_output *o) -{ - unsigned int new_fb_id; - /* for funny reasons page_flip returns -EBUSY on disabled crtcs ... */ - int expected_einval = o->flags & TEST_MODESET ? -EBUSY : -EINVAL; - unsigned int completed_events = 0; - bool do_flip; - bool do_vblank; - struct vblank_reply vbl_reply; - unsigned int target_seq; - - target_seq = o->vblank_state.seq_step; - if (o->flags & TEST_VBLANK_ABSOLUTE) - target_seq += o->vblank_state.last_seq; - - /* - * It's possible that we don't have a pending flip here, in case both - * wf-vblank and flip were scheduled and the wf-vblank event was - * delivered earlier. The same applies to vblank events w.r.t flip. - */ - do_flip = (o->flags & TEST_FLIP) && !(o->pending_events & EVENT_FLIP); - do_vblank = (o->flags & TEST_VBLANK) && - !(o->pending_events & EVENT_VBLANK); - - if (o->flags & TEST_WITH_DUMMY_LOAD) - emit_dummy_load(o); - - - o->current_fb_id = !o->current_fb_id; - if (o->flags & TEST_FB_RECREATE) - recreate_fb(o); - new_fb_id = o->fb_ids[o->current_fb_id]; - - if ((o->flags & TEST_VBLANK_EXPIRED_SEQ) && - !(o->pending_events & EVENT_VBLANK) && o->flip_state.count > 0) { - struct vblank_reply reply; - unsigned int exp_seq; - unsigned long start; - - exp_seq = o->flip_state.current_seq; - start = gettime_us(); - do_or_die(__wait_for_vblank(TEST_VBLANK_ABSOLUTE | - TEST_VBLANK_BLOCK, o->pipe, exp_seq, - 0, &reply)); - assert(gettime_us() - start < 500); - assert(reply.sequence == exp_seq); - assert(timercmp(&reply.ts, &o->flip_state.last_ts, ==)); - } - - if (do_flip && (o->flags & TEST_EINVAL) && o->flip_state.count > 0) - assert(do_page_flip(o, new_fb_id) == expected_einval); - - if (do_vblank && (o->flags & TEST_EINVAL) && o->vblank_state.count > 0) - assert(do_wait_for_vblank(o, o->pipe, target_seq, &vbl_reply) - == -EINVAL); - - if (o->flags & TEST_MODESET) { - if (drmModeSetCrtc(drm_fd, o->crtc, - o->fb_ids[o->current_fb_id], - 0, 0, - &o->id, 1, &o->mode)) { - fprintf(stderr, "failed to restore output mode: %s\n", - strerror(errno)); - exit(7); - } - } - - if (o->flags & TEST_DPMS) - do_or_die(set_dpms(o, DRM_MODE_DPMS_ON)); - - printf("."); fflush(stdout); - - if (do_flip) - do_or_die(do_page_flip(o, new_fb_id)); - - if (do_vblank) { - do_or_die(do_wait_for_vblank(o, o->pipe, target_seq, - &vbl_reply)); - if (o->flags & TEST_VBLANK_BLOCK) { - event_handler(&o->vblank_state, vbl_reply.sequence, - vbl_reply.ts.tv_sec, - vbl_reply.ts.tv_usec); - completed_events = EVENT_VBLANK; - } - } - - if (do_flip && (o->flags & TEST_EBUSY)) - assert(do_page_flip(o, new_fb_id) == -EBUSY); - - if (do_flip && (o->flags & TEST_RMFB)) - recreate_fb(o); - - /* pan before the flip completes */ - if (o->flags & TEST_PAN) { - int count = do_flip ? - o->flip_state.count : o->vblank_state.count; - int x_ofs = count * 10 > o->mode.hdisplay ? - o->mode.hdisplay : count * 10; - - if (drmModeSetCrtc(drm_fd, o->crtc, o->fb_ids[o->current_fb_id], - x_ofs, 0, &o->id, 1, &o->mode)) { - fprintf(stderr, "failed to pan (%dx%d@%dHz): %s\n", - o->fb_width, o->fb_height, - o->mode.vrefresh, strerror(errno)); - exit(7); - } - } - - if (o->flags & TEST_DPMS) - do_or_die(set_dpms(o, DRM_MODE_DPMS_OFF)); - - if (o->flags & TEST_MODESET && !(o->flags & TEST_RMFB)) { - if (drmModeSetCrtc(drm_fd, o->crtc, - 0, /* no fb */ - 0, 0, - NULL, 0, NULL)) { - fprintf(stderr, "failed to disable output: %s\n", - strerror(errno)); - exit(7); - } - } - - if (do_vblank && (o->flags & TEST_EINVAL) && o->vblank_state.count > 0) - assert(do_wait_for_vblank(o, o->pipe, target_seq, &vbl_reply) - == -EINVAL); - - if (do_flip && (o->flags & TEST_EINVAL)) - assert(do_page_flip(o, new_fb_id) == expected_einval); - - return completed_events; -} - -static void update_state(struct event_state *es) -{ - es->last_received_ts = es->current_received_ts; - es->last_ts = es->current_ts; - es->last_seq = es->current_seq; - es->count++; -} - -static void update_all_state(struct test_output *o, - unsigned int completed_events) -{ - if (completed_events & EVENT_FLIP) - update_state(&o->flip_state); - - if (completed_events & EVENT_VBLANK) - update_state(&o->vblank_state); -} - -static void connector_find_preferred_mode(struct test_output *o, int crtc_id) -{ - drmModeConnector *connector; - drmModeEncoder *encoder = NULL; - int i, j; - - /* First, find the connector & mode */ - o->mode_valid = 0; - o->crtc = 0; - connector = drmModeGetConnector(drm_fd, o->id); - assert(connector); - - if (connector->connection != DRM_MODE_CONNECTED) { - drmModeFreeConnector(connector); - return; - } - - if (!connector->count_modes) { - fprintf(stderr, "connector %d has no modes\n", o->id); - drmModeFreeConnector(connector); - return; - } - - if (connector->connector_id != o->id) { - fprintf(stderr, "connector id doesn't match (%d != %d)\n", - connector->connector_id, o->id); - drmModeFreeConnector(connector); - return; - } - - for (j = 0; j < connector->count_modes; j++) { - o->mode = connector->modes[j]; - if (o->mode.type & DRM_MODE_TYPE_PREFERRED) { - o->mode_valid = 1; - break; - } - } - - if (!o->mode_valid) { - if (connector->count_modes > 0) { - /* use the first mode as test mode */ - o->mode = connector->modes[0]; - o->mode_valid = 1; - } - else { - fprintf(stderr, "failed to find any modes on connector %d\n", - o->id); - return; - } - } - - /* Now get the encoder */ - for (i = 0; i < connector->count_encoders; i++) { - encoder = drmModeGetEncoder(drm_fd, connector->encoders[i]); - - if (!encoder) { - fprintf(stderr, "could not get encoder %i: %s\n", - resources->encoders[i], strerror(errno)); - drmModeFreeEncoder(encoder); - continue; - } - - break; - } - - o->encoder = encoder; - - if (i == resources->count_encoders) { - fprintf(stderr, "failed to find encoder\n"); - o->mode_valid = 0; - return; - } - - /* Find first CRTC not in use */ - for (i = 0; i < resources->count_crtcs; i++) { - if (resources->crtcs[i] != crtc_id) - continue; - if (resources->crtcs[i] && - (o->encoder->possible_crtcs & (1<<i))) { - o->crtc = resources->crtcs[i]; - break; - } - } - - if (!o->crtc) { - fprintf(stderr, "could not find requested crtc %d\n", crtc_id); - o->mode_valid = 0; - return; - } - - o->connector = connector; -} - -static void -paint_flip_mode(cairo_t *cr, int width, int height, void *priv) -{ - bool odd_frame = (bool) priv; - - if (odd_frame) - cairo_rectangle(cr, width/4, height/2, width/4, height/8); - else - cairo_rectangle(cr, width/2, height/2, width/4, height/8); - - cairo_set_source_rgb(cr, 1, 1, 1); - cairo_fill(cr); -} - -static int -fb_is_bound(struct test_output *o, int fb) -{ - struct drm_mode_crtc mode; - - mode.crtc_id = o->crtc; - if (drmIoctl(drm_fd, DRM_IOCTL_MODE_GETCRTC, &mode)) - return 0; - - return mode.mode_valid && mode.fb_id == fb; -} - -static void check_final_state(struct test_output *o, struct event_state *es, - unsigned int ellapsed) -{ - if (es->count == 0) { - fprintf(stderr, "no %s event received\n", es->name); - exit(12); - } - - /* Verify we drop no frames, but only if it's not a TV encoder, since - * those use some funny fake timings behind userspace's back. */ - if (o->flags & TEST_CHECK_TS && !analog_tv_connector(o)) { - int expected; - int count = es->count; - - count *= es->seq_step; - expected = ellapsed * o->mode.vrefresh / (1000 * 1000); - if (count < expected * 99/100) { - fprintf(stderr, "dropped frames, expected %d, counted %d, encoder type %d\n", - expected, count, o->encoder->encoder_type); - exit(3); - } - } -} - -/* - * Wait until at least one pending event completes. Return mask of completed - * events. - */ -static unsigned int wait_for_events(struct test_output *o) -{ - drmEventContext evctx; - struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 }; - fd_set fds; - unsigned int event_mask; - int ret; - - event_mask = o->pending_events; - assert(event_mask); - - memset(&evctx, 0, sizeof evctx); - evctx.version = DRM_EVENT_CONTEXT_VERSION; - evctx.vblank_handler = vblank_handler; - evctx.page_flip_handler = page_flip_handler; - - /* make timeout lax with the dummy load */ - if (o->flags & TEST_WITH_DUMMY_LOAD) - timeout.tv_sec *= 10; - - FD_ZERO(&fds); - FD_SET(0, &fds); - FD_SET(drm_fd, &fds); - ret = select(drm_fd + 1, &fds, NULL, NULL, &timeout); - - if (ret <= 0) { - fprintf(stderr, "select timed out or error (ret %d)\n", - ret); - exit(1); - } else if (FD_ISSET(0, &fds)) { - fprintf(stderr, "no fds active, breaking\n"); - exit(2); - } - - do_or_die(drmHandleEvent(drm_fd, &evctx)); - - event_mask ^= o->pending_events; - assert(event_mask); - - return event_mask; -} - -/* Returned the ellapsed time in us */ -static unsigned event_loop(struct test_output *o, unsigned duration_sec) -{ - unsigned long start, end; - - start = gettime_us(); - - while (1) { - unsigned int completed_events; - - completed_events = run_test_step(o); - if (o->pending_events) - completed_events |= wait_for_events(o); - check_all_state(o, completed_events); - update_all_state(o, completed_events); - - if ((gettime_us() - start) / 1000000 >= duration_sec) - break; - } - - end = gettime_us(); - - /* Flush any remaining events */ - if (o->pending_events) - wait_for_events(o); - - return end - start; -} - -static void run_test_on_crtc(struct test_output *o, int crtc, int duration) -{ - unsigned ellapsed; - - o->bpp = 32; - o->depth = 24; - - connector_find_preferred_mode(o, crtc); - if (!o->mode_valid) - return; - - fprintf(stdout, "Beginning %s on crtc %d, connector %d\n", - o->test_name, crtc, o->id); - - o->fb_width = o->mode.hdisplay; - o->fb_height = o->mode.vdisplay; - - if (o->flags & TEST_PAN) - o->fb_width *= 2; - - o->fb_ids[0] = kmstest_create_fb(drm_fd, o->fb_width, o->fb_height, - o->bpp, o->depth, false, &o->fb_info[0], - paint_flip_mode, (void *)false); - o->fb_ids[1] = kmstest_create_fb(drm_fd, o->fb_width, o->fb_height, - o->bpp, o->depth, false, &o->fb_info[1], - paint_flip_mode, (void *)true); - - if (!o->fb_ids[0] || !o->fb_ids[1]) { - fprintf(stderr, "failed to create fbs\n"); - exit(3); - } - - kmstest_dump_mode(&o->mode); - if (drmModeSetCrtc(drm_fd, o->crtc, o->fb_ids[0], 0, 0, - &o->id, 1, &o->mode)) { - fprintf(stderr, "failed to set mode (%dx%d@%dHz): %s\n", - o->fb_width, o->fb_height, o->mode.vrefresh, - strerror(errno)); - exit(3); - } - assert(fb_is_bound(o, o->fb_ids[0])); - - /* quiescent the hw a bit so ensure we don't miss a single frame */ - if (o->flags & TEST_CHECK_TS) - sleep(1); - - if (do_page_flip(o, o->fb_ids[1])) { - fprintf(stderr, "failed to page flip: %s\n", strerror(errno)); - exit(4); - } - wait_for_events(o); - - o->current_fb_id = 1; - o->flip_state.seq_step = 1; - if (o->flags & TEST_VBLANK_ABSOLUTE) - o->vblank_state.seq_step = 5; - else - o->vblank_state.seq_step = 1; - - ellapsed = event_loop(o, duration); - - if (o->flags & TEST_FLIP) - check_final_state(o, &o->flip_state, ellapsed); - if (o->flags & TEST_VBLANK) - check_final_state(o, &o->vblank_state, ellapsed); - - fprintf(stdout, "\n%s on crtc %d, connector %d: PASSED\n\n", - o->test_name, crtc, o->id); - - kmstest_remove_fb(drm_fd, o->fb_ids[1]); - kmstest_remove_fb(drm_fd, o->fb_ids[0]); - - drmModeFreeEncoder(o->encoder); - drmModeFreeConnector(o->connector); -} - -static int get_pipe_from_crtc_id(int crtc_id) -{ - struct drm_i915_get_pipe_from_crtc_id pfci; - int ret; - - memset(&pfci, 0, sizeof(pfci)); - pfci.crtc_id = crtc_id; - ret = drmIoctl(drm_fd, DRM_IOCTL_I915_GET_PIPE_FROM_CRTC_ID, &pfci); - assert(ret == 0); - - return pfci.pipe; -} - -static int run_test(int duration, int flags, const char *test_name) -{ - struct test_output o; - int c, i; - - resources = drmModeGetResources(drm_fd); - if (!resources) { - fprintf(stderr, "drmModeGetResources failed: %s\n", - strerror(errno)); - exit(5); - } - - /* Find any connected displays */ - for (c = 0; c < resources->count_connectors; c++) { - for (i = 0; i < resources->count_crtcs; i++) { - int crtc; - - memset(&o, 0, sizeof(o)); - o.test_name = test_name; - o.id = resources->connectors[c]; - o.flags = flags; - o.flip_state.name = "flip"; - o.vblank_state.name = "vblank"; - crtc = resources->crtcs[i]; - o.pipe = get_pipe_from_crtc_id(crtc); - - run_test_on_crtc(&o, crtc, duration); - } - } - - drmModeFreeResources(resources); - return 1; -} - -static void get_timestamp_format(void) -{ - uint64_t cap_mono; - int ret; - - ret = drmGetCap(drm_fd, DRM_CAP_TIMESTAMP_MONOTONIC, &cap_mono); - assert(ret == 0 || errno == EINVAL); - monotonic_timestamp = ret == 0 && cap_mono == 1; - printf("Using %s timestamps\n", - monotonic_timestamp ? "monotonic" : "real"); -} - -int main(int argc, char **argv) -{ - struct { - int duration; - int flags; - const char *name; - } tests[] = { - { 15, TEST_VBLANK, "wf_vblank" }, - { 15, TEST_VBLANK | TEST_CHECK_TS, "wf_vblank-ts-check" }, - { 15, TEST_VBLANK | TEST_VBLANK_BLOCK | TEST_CHECK_TS, - "blocking-wf_vblank" }, - { 5, TEST_VBLANK | TEST_VBLANK_ABSOLUTE, - "absolute-wf_vblank" }, - { 5, TEST_VBLANK | TEST_VBLANK_BLOCK | TEST_VBLANK_ABSOLUTE, - "blocking-absolute-wf_vblank" }, - { 30, TEST_VBLANK | TEST_DPMS | TEST_EINVAL, "wf_vblank-vs-dpms" }, - { 30, TEST_VBLANK | TEST_DPMS | TEST_WITH_DUMMY_LOAD, - "delayed-wf_vblank-vs-dpms" }, - { 30, TEST_VBLANK | TEST_MODESET | TEST_EINVAL, "wf_vblank-vs-modeset" }, - { 30, TEST_VBLANK | TEST_MODESET | TEST_WITH_DUMMY_LOAD, - "delayed-wf_vblank-vs-modeset" }, - - { 15, TEST_FLIP | TEST_EBUSY , "plain-flip" }, - { 15, TEST_FLIP | TEST_CHECK_TS | TEST_EBUSY , "plain-flip-ts-check" }, - { 15, TEST_FLIP | TEST_CHECK_TS | TEST_EBUSY | TEST_FB_RECREATE, - "plain-flip-fb-recreate" }, - { 15, TEST_FLIP | TEST_EBUSY | TEST_RMFB | TEST_MODESET , "flip-vs-rmfb" }, - { 30, TEST_FLIP | TEST_DPMS | TEST_EINVAL, "flip-vs-dpms" }, - { 30, TEST_FLIP | TEST_DPMS | TEST_WITH_DUMMY_LOAD, "delayed-flip-vs-dpms" }, - { 5, TEST_FLIP | TEST_PAN, "flip-vs-panning" }, - { 30, TEST_FLIP | TEST_PAN | TEST_WITH_DUMMY_LOAD, "delayed-flip-vs-panning" }, - { 30, TEST_FLIP | TEST_MODESET | TEST_EINVAL, "flip-vs-modeset" }, - { 30, TEST_FLIP | TEST_MODESET | TEST_WITH_DUMMY_LOAD, "delayed-flip-vs-modeset" }, - { 5, TEST_FLIP | TEST_VBLANK_EXPIRED_SEQ, - "flip-vs-expired-vblank" }, - - { 15, TEST_FLIP | TEST_VBLANK | TEST_VBLANK_ABSOLUTE | - TEST_CHECK_TS, "flip-vs-absolute-wf_vblank" }, - { 15, TEST_FLIP | TEST_VBLANK | TEST_CHECK_TS, - "flip-vs-wf_vblank" }, - { 15, TEST_FLIP | TEST_VBLANK | TEST_VBLANK_BLOCK | - TEST_CHECK_TS, "flip-vs-blocking-wf-vblank" }, - }; - int i; - - drmtest_subtest_init(argc, argv); - - drm_fd = drm_open_any(); - - if (!drmtest_only_list_subtests()) - get_timestamp_format(); - - bufmgr = drm_intel_bufmgr_gem_init(drm_fd, 4096); - devid = intel_get_drm_devid(drm_fd); - batch = intel_batchbuffer_alloc(bufmgr, devid); - - for (i = 0; i < sizeof(tests) / sizeof (tests[0]); i++) { - if (drmtest_run_subtest(tests[i].name)) { - printf("running testcase: %s\n", tests[i].name); - run_test(tests[i].duration, tests[i].flags, tests[i].name); - } - } - - close(drm_fd); - - return 0; -} |