You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
716 lines
16 KiB
716 lines
16 KiB
4 months ago
|
/*
|
||
|
* 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.
|
||
|
*
|
||
|
* Authors: Paulo Zanoni <paulo.r.zanoni@intel.com>
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include <sys/time.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <inttypes.h>
|
||
|
#include <unistd.h>
|
||
|
#include <signal.h>
|
||
|
#include <time.h>
|
||
|
#include <getopt.h>
|
||
|
#include "igt.h"
|
||
|
|
||
|
#define IA32_TIME_STAMP_COUNTER 0x10
|
||
|
|
||
|
#define MSR_PKG_CST_CONFIG_CONTROL 0xE2
|
||
|
#define PKG_CST_LIMIT_MASK 0x7
|
||
|
#define PKG_CST_LIMIT_C0 0x0
|
||
|
#define PKG_CST_LIMIT_C2 0x1
|
||
|
#define PKG_CST_LIMIT_C3 0x2
|
||
|
#define PKG_CST_LIMIT_C6 0x3
|
||
|
#define PKG_CST_LIMIT_C7 0x4
|
||
|
#define PKG_CST_LIMIT_C7s 0x5
|
||
|
#define PKG_CST_NO_LIMIT 0x7
|
||
|
|
||
|
#define MSR_PKG_C2_RESIDENCY 0x60D
|
||
|
#define MSR_PKG_C3_RESIDENCY 0x3F8
|
||
|
#define MSR_PKG_C6_RESIDENCY 0x3F9
|
||
|
#define MSR_PKG_C7_RESIDENCY 0x3FA
|
||
|
#define MSR_PKG_C8_RESIDENCY 0x630
|
||
|
#define MSR_PKG_C9_RESIDENCY 0x631
|
||
|
#define MSR_PKG_C10_RESIDENCY 0x632
|
||
|
|
||
|
#define NUM_PC_STATES 7
|
||
|
|
||
|
const char *res_msr_names[] = {
|
||
|
"PC2", "PC3", "PC6", "PC7", "PC8", "PC9", "PC10"
|
||
|
};
|
||
|
|
||
|
const uint32_t res_msr_addrs[] = {
|
||
|
MSR_PKG_C2_RESIDENCY,
|
||
|
MSR_PKG_C3_RESIDENCY,
|
||
|
MSR_PKG_C6_RESIDENCY,
|
||
|
MSR_PKG_C7_RESIDENCY,
|
||
|
MSR_PKG_C8_RESIDENCY,
|
||
|
MSR_PKG_C9_RESIDENCY,
|
||
|
MSR_PKG_C10_RESIDENCY,
|
||
|
};
|
||
|
|
||
|
int msr_fd;
|
||
|
|
||
|
uint32_t deepest_pc_state;
|
||
|
uint64_t idle_res;
|
||
|
|
||
|
#define MAX_CONNECTORS 32
|
||
|
#define MAX_PLANES 32
|
||
|
struct {
|
||
|
int fd;
|
||
|
drmModeResPtr res;
|
||
|
drmModeConnectorPtr connectors[MAX_CONNECTORS];
|
||
|
drm_intel_bufmgr *bufmgr;
|
||
|
} drm;
|
||
|
|
||
|
struct {
|
||
|
uint32_t crtc_id;
|
||
|
uint32_t connector_id;
|
||
|
drmModeModeInfoPtr mode;
|
||
|
} modeset;
|
||
|
|
||
|
int vblank_interval_us;
|
||
|
struct igt_fb fbs[2], cursor, *front_fb, *back_fb;
|
||
|
|
||
|
struct {
|
||
|
int draw_size;
|
||
|
bool do_page_flip;
|
||
|
bool do_draw;
|
||
|
bool do_draw_and_flip;
|
||
|
int res_warm_time;
|
||
|
int res_calc_time;
|
||
|
int loop_inc;
|
||
|
char *test_name;
|
||
|
} opts = {
|
||
|
.draw_size = 0,
|
||
|
.do_page_flip = true,
|
||
|
.do_draw = true,
|
||
|
.do_draw_and_flip = true,
|
||
|
.res_warm_time = 1,
|
||
|
.res_calc_time = 4,
|
||
|
.loop_inc = 2,
|
||
|
.test_name = NULL,
|
||
|
};
|
||
|
|
||
|
static uint64_t msr_read(uint32_t addr)
|
||
|
{
|
||
|
int rc;
|
||
|
uint64_t ret;
|
||
|
|
||
|
rc = pread(msr_fd, &ret, sizeof(uint64_t), addr);
|
||
|
igt_assert(rc == sizeof(ret));
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static void setup_msr(void)
|
||
|
{
|
||
|
#if 0
|
||
|
uint64_t control;
|
||
|
const char *limit;
|
||
|
#endif
|
||
|
|
||
|
/* Make sure our Kernel supports MSR and the module is loaded. */
|
||
|
igt_assert(system("modprobe -q msr > /dev/null 2>&1") != -1);
|
||
|
|
||
|
msr_fd = open("/dev/cpu/0/msr", O_RDONLY);
|
||
|
igt_assert_f(msr_fd >= 0,
|
||
|
"Can't open /dev/cpu/0/msr.\n");
|
||
|
|
||
|
#if 0
|
||
|
/* FIXME: why is this code not printing the truth? */
|
||
|
control = msr_read(MSR_PKG_CST_CONFIG_CONTROL);
|
||
|
printf("Control: 0x016%" PRIx64 "\n", control);
|
||
|
switch (control & PKG_CST_LIMIT_MASK) {
|
||
|
case PKG_CST_LIMIT_C0:
|
||
|
limit = "C0";
|
||
|
break;
|
||
|
case PKG_CST_LIMIT_C2:
|
||
|
limit = "C2";
|
||
|
break;
|
||
|
case PKG_CST_LIMIT_C3:
|
||
|
limit = "C3";
|
||
|
break;
|
||
|
case PKG_CST_LIMIT_C6:
|
||
|
limit = "C6";
|
||
|
break;
|
||
|
case PKG_CST_LIMIT_C7:
|
||
|
limit = "C7";
|
||
|
break;
|
||
|
case PKG_CST_LIMIT_C7s:
|
||
|
limit = "C7s";
|
||
|
break;
|
||
|
case PKG_CST_NO_LIMIT:
|
||
|
limit = "no limit";
|
||
|
break;
|
||
|
default:
|
||
|
limit = "unknown";
|
||
|
break;
|
||
|
}
|
||
|
printf("Package C state limit: %s\n", limit);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static void teardown_msr(void)
|
||
|
{
|
||
|
close(msr_fd);
|
||
|
}
|
||
|
|
||
|
static void setup_drm(void)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
drm.fd = drm_open_driver_master(DRIVER_INTEL);
|
||
|
|
||
|
drm.res = drmModeGetResources(drm.fd);
|
||
|
igt_assert(drm.res->count_connectors <= MAX_CONNECTORS);
|
||
|
|
||
|
for (i = 0; i < drm.res->count_connectors; i++)
|
||
|
drm.connectors[i] = drmModeGetConnector(drm.fd,
|
||
|
drm.res->connectors[i]);
|
||
|
|
||
|
drm.bufmgr = drm_intel_bufmgr_gem_init(drm.fd, 4096);
|
||
|
igt_assert(drm.bufmgr);
|
||
|
drm_intel_bufmgr_gem_enable_reuse(drm.bufmgr);
|
||
|
}
|
||
|
|
||
|
static void teardown_drm(void)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
drm_intel_bufmgr_destroy(drm.bufmgr);
|
||
|
|
||
|
for (i = 0; i < drm.res->count_connectors; i++)
|
||
|
drmModeFreeConnector(drm.connectors[i]);
|
||
|
|
||
|
drmModeFreeResources(drm.res);
|
||
|
close(drm.fd);
|
||
|
}
|
||
|
|
||
|
static void draw_rect(struct igt_fb *fb, enum igt_draw_method method,
|
||
|
uint32_t color)
|
||
|
{
|
||
|
drmModeClip clip;
|
||
|
int rc;
|
||
|
|
||
|
switch (opts.draw_size) {
|
||
|
case 0:
|
||
|
clip.x1 = fb->width / 2 - 32;
|
||
|
clip.x2 = fb->width / 2 + 32;
|
||
|
clip.y1 = fb->height / 2 - 32;
|
||
|
clip.y2 = fb->height / 2 + 32;
|
||
|
break;
|
||
|
case 1:
|
||
|
clip.x1 = fb->width / 4;
|
||
|
clip.x2 = fb->width / 4 + fb->width / 2;
|
||
|
clip.y1 = fb->height / 4;
|
||
|
clip.y2 = fb->height / 4 + fb->height / 2;
|
||
|
break;
|
||
|
case 2:
|
||
|
clip.x1 = 0;
|
||
|
clip.x2 = fb->width;
|
||
|
clip.y1 = 0;
|
||
|
clip.y2 = fb->height;
|
||
|
break;
|
||
|
default:
|
||
|
igt_assert(false);
|
||
|
}
|
||
|
|
||
|
igt_draw_rect_fb(drm.fd, drm.bufmgr, NULL, fb, method, clip.x1, clip.y1,
|
||
|
clip.x2 - clip.x1, clip.y2 - clip.y1, color);
|
||
|
|
||
|
if (method == IGT_DRAW_MMAP_WC) {
|
||
|
rc = drmModeDirtyFB(drm.fd, fb->fb_id, &clip, 1);
|
||
|
igt_assert(rc == 0 || rc == -ENOSYS);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void setup_modeset(void)
|
||
|
{
|
||
|
int i;
|
||
|
drmModeConnectorPtr connector;
|
||
|
|
||
|
for (i = 0; i < drm.res->count_connectors; i++) {
|
||
|
connector = drm.connectors[i];
|
||
|
|
||
|
if (connector->connection == DRM_MODE_CONNECTED &&
|
||
|
connector->count_modes > 0)
|
||
|
break;
|
||
|
}
|
||
|
igt_assert(i < drm.res->count_connectors);
|
||
|
|
||
|
modeset.connector_id = connector->connector_id;
|
||
|
modeset.mode = &connector->modes[0];
|
||
|
modeset.crtc_id = kmstest_find_crtc_for_connector(drm.fd, drm.res,
|
||
|
connector, 0);
|
||
|
|
||
|
for (i = 0; i < 2; i++) {
|
||
|
igt_create_fb(drm.fd, modeset.mode->hdisplay,
|
||
|
modeset.mode->vdisplay, DRM_FORMAT_XRGB8888,
|
||
|
LOCAL_I915_FORMAT_MOD_X_TILED, &fbs[i]);
|
||
|
igt_draw_fill_fb(drm.fd, &fbs[i], 0x80);
|
||
|
}
|
||
|
draw_rect(&fbs[1], IGT_DRAW_BLT, 0x800000);
|
||
|
|
||
|
igt_create_fb(drm.fd, 64, 64, DRM_FORMAT_ARGB8888,
|
||
|
LOCAL_DRM_FORMAT_MOD_NONE, &cursor);
|
||
|
igt_draw_fill_fb(drm.fd, &cursor, 0xFF008000);
|
||
|
}
|
||
|
|
||
|
static void teardown_modeset(void)
|
||
|
{
|
||
|
igt_remove_fb(drm.fd, &fbs[0]);
|
||
|
igt_remove_fb(drm.fd, &fbs[1]);
|
||
|
igt_remove_fb(drm.fd, &cursor);
|
||
|
}
|
||
|
|
||
|
static void setup_vblank_interval(void)
|
||
|
{
|
||
|
uint64_t vrefresh, interval;
|
||
|
|
||
|
vrefresh = ((uint64_t) modeset.mode->clock * 1000 * 1000) /
|
||
|
(modeset.mode->htotal * modeset.mode->vtotal);
|
||
|
interval = 1000000000 / vrefresh;
|
||
|
|
||
|
vblank_interval_us = interval;
|
||
|
|
||
|
printf("Interval between vblanks:\t%dus\n", vblank_interval_us);
|
||
|
}
|
||
|
|
||
|
bool alarm_received;
|
||
|
static void alarm_handler(int signal)
|
||
|
{
|
||
|
alarm_received = true;
|
||
|
}
|
||
|
|
||
|
static void setup_alarm(void)
|
||
|
{
|
||
|
struct sigaction sa;
|
||
|
|
||
|
sa.sa_handler = alarm_handler;
|
||
|
sigemptyset(&sa.sa_mask);
|
||
|
sa.sa_flags = 0;
|
||
|
sigaction(SIGALRM, &sa, NULL);
|
||
|
}
|
||
|
|
||
|
static void set_alarm(time_t sec, suseconds_t usec)
|
||
|
{
|
||
|
struct itimerval timerval = {{0, 0}, {sec, usec}};
|
||
|
|
||
|
alarm_received = false;
|
||
|
igt_assert(setitimer(ITIMER_REAL, &timerval, NULL) == 0);
|
||
|
}
|
||
|
|
||
|
static void unset_mode(void)
|
||
|
{
|
||
|
int rc;
|
||
|
|
||
|
kmstest_unset_all_crtcs(drm.fd, drm.res);
|
||
|
rc = drmModeSetCursor(drm.fd, modeset.crtc_id, 0, 0, 0);
|
||
|
igt_assert(rc == 0);
|
||
|
}
|
||
|
|
||
|
static void set_mode(void)
|
||
|
{
|
||
|
int rc;
|
||
|
|
||
|
front_fb = &fbs[0];
|
||
|
back_fb = &fbs[1];
|
||
|
rc = drmModeSetCrtc(drm.fd, modeset.crtc_id, front_fb->fb_id, 0, 0,
|
||
|
&modeset.connector_id, 1, modeset.mode);
|
||
|
igt_assert(rc == 0);
|
||
|
|
||
|
/* TODO: it seems we need a cursor in order to reach PC7 on BDW. Why? */
|
||
|
rc = drmModeMoveCursor(drm.fd, modeset.crtc_id, 0, 0);
|
||
|
igt_assert(rc == 0);
|
||
|
|
||
|
rc = drmModeSetCursor(drm.fd, modeset.crtc_id, cursor.gem_handle,
|
||
|
cursor.width, cursor.height);
|
||
|
igt_assert(rc == 0);
|
||
|
}
|
||
|
|
||
|
static void wait_vblanks(int n_vblanks)
|
||
|
{
|
||
|
drmVBlank vblank;
|
||
|
|
||
|
if (!n_vblanks)
|
||
|
return;
|
||
|
|
||
|
vblank.request.type = DRM_VBLANK_RELATIVE;
|
||
|
vblank.request.sequence = n_vblanks;
|
||
|
vblank.request.signal = 0;
|
||
|
drmWaitVBlank(drm.fd, &vblank);
|
||
|
}
|
||
|
|
||
|
static void page_flip(void)
|
||
|
{
|
||
|
struct igt_fb *tmp_fb;
|
||
|
int rc;
|
||
|
|
||
|
rc = drmModePageFlip(drm.fd, modeset.crtc_id, back_fb->fb_id, 0, NULL);
|
||
|
igt_assert(rc == 0);
|
||
|
|
||
|
tmp_fb = front_fb;
|
||
|
front_fb = back_fb;
|
||
|
back_fb = tmp_fb;
|
||
|
}
|
||
|
|
||
|
static void wait_until_idle(void)
|
||
|
{
|
||
|
uint64_t tsc, pc, res;
|
||
|
|
||
|
do {
|
||
|
set_alarm(0, 500 * 1000);
|
||
|
|
||
|
tsc = msr_read(IA32_TIME_STAMP_COUNTER);
|
||
|
pc = msr_read(deepest_pc_state);
|
||
|
|
||
|
while (!alarm_received)
|
||
|
pause();
|
||
|
|
||
|
pc = msr_read(deepest_pc_state) - pc;
|
||
|
tsc = msr_read(IA32_TIME_STAMP_COUNTER) - tsc;
|
||
|
|
||
|
res = pc * 100 / tsc;
|
||
|
|
||
|
/*printf("res:%02"PRIu64"\n", res);*/
|
||
|
} while (res < idle_res && idle_res - res > 3);
|
||
|
|
||
|
if (res > idle_res && res - idle_res > 3)
|
||
|
fprintf(stderr, "The calculated idle residency may be too low "
|
||
|
"(got %02"PRIu64"%%)\n", res);
|
||
|
}
|
||
|
|
||
|
static uint64_t do_measurement(void (*callback)(void *ptr), void *ptr)
|
||
|
{
|
||
|
uint64_t tsc, pc;
|
||
|
|
||
|
wait_until_idle();
|
||
|
|
||
|
set_alarm(opts.res_warm_time, 0);
|
||
|
callback(ptr);
|
||
|
|
||
|
set_alarm(opts.res_calc_time, 0);
|
||
|
|
||
|
tsc = msr_read(IA32_TIME_STAMP_COUNTER);
|
||
|
pc = msr_read(deepest_pc_state);
|
||
|
|
||
|
callback(ptr);
|
||
|
|
||
|
pc = msr_read(deepest_pc_state) - pc;
|
||
|
tsc = msr_read(IA32_TIME_STAMP_COUNTER) - tsc;
|
||
|
|
||
|
return pc * 100 / tsc;
|
||
|
}
|
||
|
|
||
|
static void setup_idle(void)
|
||
|
{
|
||
|
uint64_t tsc, pc[NUM_PC_STATES], res, best_res;
|
||
|
int pc_i, best_pc_i = 0, retries, consecutive_not_best;
|
||
|
|
||
|
for (retries = 0; ; retries++) {
|
||
|
|
||
|
set_alarm(opts.res_warm_time, 0);
|
||
|
while (!alarm_received)
|
||
|
pause();
|
||
|
|
||
|
set_alarm(opts.res_calc_time, 0);
|
||
|
|
||
|
tsc = msr_read(IA32_TIME_STAMP_COUNTER);
|
||
|
for (pc_i = best_pc_i; pc_i < NUM_PC_STATES; pc_i++)
|
||
|
pc[pc_i] = msr_read(res_msr_addrs[pc_i]);
|
||
|
|
||
|
while (!alarm_received)
|
||
|
pause();
|
||
|
|
||
|
for (pc_i = best_pc_i; pc_i < NUM_PC_STATES; pc_i++)
|
||
|
pc[pc_i] = msr_read(res_msr_addrs[pc_i]) - pc[pc_i];
|
||
|
tsc = msr_read(IA32_TIME_STAMP_COUNTER) - tsc;
|
||
|
|
||
|
for (pc_i = NUM_PC_STATES -1; pc_i >= best_pc_i; pc_i--)
|
||
|
if (pc[pc_i] != 0)
|
||
|
break;
|
||
|
igt_require_f(pc_i >= 0, "We're not reaching any PC states!\n");
|
||
|
|
||
|
res = pc[pc_i] * 100 / tsc;
|
||
|
|
||
|
if (retries == 0 || pc_i > best_pc_i || res > best_res) {
|
||
|
best_pc_i = pc_i;
|
||
|
best_res = res;
|
||
|
consecutive_not_best = 0;
|
||
|
} else {
|
||
|
consecutive_not_best++;
|
||
|
if (consecutive_not_best > 2)
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
deepest_pc_state = res_msr_addrs[best_pc_i];
|
||
|
idle_res = best_res;
|
||
|
|
||
|
printf("Stable idle residency retries:\t%d\n", retries);
|
||
|
printf("Deepest PC state reached when idle:\t%s\n",
|
||
|
res_msr_names[best_pc_i]);
|
||
|
printf("Idle residency for this state:\t%02"PRIu64"%%\n", idle_res);
|
||
|
}
|
||
|
|
||
|
static void print_result(int ops, int vblanks, uint64_t res)
|
||
|
{
|
||
|
printf("- %02d ops every %02d vblanks:\t%02"PRIu64"%%\n",
|
||
|
ops, vblanks, res);
|
||
|
fflush(stdout);
|
||
|
}
|
||
|
|
||
|
struct page_flip_data {
|
||
|
int n_vblanks;
|
||
|
};
|
||
|
|
||
|
static void page_flip_cb(void *ptr)
|
||
|
{
|
||
|
struct page_flip_data *data = ptr;
|
||
|
|
||
|
while (!alarm_received) {
|
||
|
page_flip();
|
||
|
wait_vblanks(data->n_vblanks);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void page_flip_test(void)
|
||
|
{
|
||
|
struct page_flip_data data;
|
||
|
int n_vblanks;
|
||
|
uint64_t res;
|
||
|
|
||
|
printf("\nPage flip test:\n");
|
||
|
|
||
|
for (n_vblanks = 1; n_vblanks <= 64; n_vblanks *= opts.loop_inc) {
|
||
|
data.n_vblanks = n_vblanks;
|
||
|
res = do_measurement(page_flip_cb, &data);
|
||
|
print_result(1, n_vblanks, res);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
struct draw_data {
|
||
|
enum igt_draw_method method;
|
||
|
int n_vblanks;
|
||
|
int ops_per_vblank;
|
||
|
};
|
||
|
|
||
|
static void draw_cb(void *ptr)
|
||
|
{
|
||
|
struct draw_data *data = ptr;
|
||
|
struct timespec req;
|
||
|
int i, ops;
|
||
|
|
||
|
req.tv_sec = 0;
|
||
|
req.tv_nsec = vblank_interval_us * 1000 / data->ops_per_vblank;
|
||
|
|
||
|
for (i = 0; !alarm_received; i++) {
|
||
|
for (ops = 0; ops < data->ops_per_vblank; ops++) {
|
||
|
draw_rect(front_fb, data->method, i << 8);
|
||
|
|
||
|
/* The code that stops the callbacks relies on SIGALRM,
|
||
|
* so we have to use nanosleep since it doesn't use
|
||
|
* signals. */
|
||
|
if (data->ops_per_vblank > 1)
|
||
|
nanosleep(&req, NULL);
|
||
|
}
|
||
|
|
||
|
if (data->n_vblanks)
|
||
|
wait_vblanks(data->n_vblanks);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void draw_test(void)
|
||
|
{
|
||
|
struct draw_data data;
|
||
|
enum igt_draw_method method;
|
||
|
int i;
|
||
|
uint64_t res;
|
||
|
|
||
|
for (method = 0; method < IGT_DRAW_METHOD_COUNT; method++) {
|
||
|
data.method = method;
|
||
|
|
||
|
printf("\nDraw %s test:\n",
|
||
|
igt_draw_get_method_name(method));
|
||
|
|
||
|
data.n_vblanks = 0;
|
||
|
for (i = 32; i >= 2; i /= opts.loop_inc) {
|
||
|
data.ops_per_vblank = i;
|
||
|
res = do_measurement(draw_cb, &data);
|
||
|
print_result(i, 1, res);
|
||
|
}
|
||
|
|
||
|
data.ops_per_vblank = 1;
|
||
|
for (i = 1; i <= 64; i *= opts.loop_inc) {
|
||
|
data.n_vblanks = i ;
|
||
|
res = do_measurement(draw_cb, &data);
|
||
|
print_result(1, i, res);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void draw_and_flip_cb(void *ptr)
|
||
|
{
|
||
|
struct draw_data *data = ptr;
|
||
|
int i, ops;
|
||
|
|
||
|
for (i = 0; !alarm_received; i++) {
|
||
|
for (ops = 0; ops < data->ops_per_vblank; ops++)
|
||
|
draw_rect(back_fb, data->method, i << 8);
|
||
|
|
||
|
page_flip();
|
||
|
wait_vblanks(1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void draw_and_flip_test(void)
|
||
|
{
|
||
|
struct draw_data data;
|
||
|
enum igt_draw_method method;
|
||
|
int i;
|
||
|
uint64_t res;
|
||
|
|
||
|
for (method = 0; method < IGT_DRAW_METHOD_COUNT; method++) {
|
||
|
data.method = method;
|
||
|
|
||
|
/* Doing everything consumes too much time! */
|
||
|
if (method != IGT_DRAW_MMAP_CPU && method != IGT_DRAW_BLT)
|
||
|
continue;
|
||
|
|
||
|
printf("\nDraw and flip %s test:\n",
|
||
|
igt_draw_get_method_name(method));
|
||
|
|
||
|
for (i = 16; i >= 1; i /= opts.loop_inc) {
|
||
|
data.ops_per_vblank = 1;
|
||
|
res = do_measurement(draw_and_flip_cb, &data);
|
||
|
print_result(i, 1, res);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void parse_opts(int argc, char *argv[])
|
||
|
{
|
||
|
int opt;
|
||
|
char short_opts[] = "d:lrbw:c:i:fsn:";
|
||
|
struct option long_opts[] = {
|
||
|
{ "draw-size", required_argument, NULL, 'd'},
|
||
|
{ "no-flip", no_argument, NULL, 'l'},
|
||
|
{ "no-draw", no_argument, NULL, 'r'},
|
||
|
{ "no-draw-and-flip", no_argument, NULL, 'b'},
|
||
|
{ "warm-time", required_argument, NULL, 'w'},
|
||
|
{ "calc-time", required_argument, NULL, 'c'},
|
||
|
{ "loop-increment", required_argument, NULL, 'i'},
|
||
|
{ "fast", no_argument, NULL, 'f'},
|
||
|
{ "slow", no_argument, NULL, 's'},
|
||
|
{ "name", required_argument, NULL, 'n'},
|
||
|
{ 0 },
|
||
|
};
|
||
|
|
||
|
while (1) {
|
||
|
opt = getopt_long(argc, argv, short_opts, long_opts, NULL);
|
||
|
|
||
|
switch (opt) {
|
||
|
case 'd':
|
||
|
if (strcmp(optarg, "s") == 0)
|
||
|
opts.draw_size = 0;
|
||
|
else if (strcmp(optarg, "m") == 0)
|
||
|
opts.draw_size = 1;
|
||
|
else if (strcmp(optarg, "l") == 0)
|
||
|
opts.draw_size = 2;
|
||
|
else
|
||
|
igt_assert(false);
|
||
|
break;
|
||
|
case 'l':
|
||
|
opts.do_page_flip = false;
|
||
|
break;
|
||
|
case 'r':
|
||
|
opts.do_draw = false;
|
||
|
break;
|
||
|
case 'b':
|
||
|
opts.do_draw_and_flip = false;
|
||
|
break;
|
||
|
case 'w':
|
||
|
opts.res_warm_time = atoi(optarg);
|
||
|
break;
|
||
|
case 'c':
|
||
|
opts.res_calc_time = atoi(optarg);
|
||
|
break;
|
||
|
case 'i':
|
||
|
opts.loop_inc = atoi(optarg);
|
||
|
break;
|
||
|
case 'f':
|
||
|
opts.res_warm_time = 1;
|
||
|
opts.res_calc_time = 2;
|
||
|
opts.loop_inc = 4;
|
||
|
break;
|
||
|
case 's':
|
||
|
opts.res_warm_time = 2;
|
||
|
opts.res_calc_time = 6;
|
||
|
opts.loop_inc = 2;
|
||
|
break;
|
||
|
case 'n':
|
||
|
opts.test_name = optarg;
|
||
|
break;
|
||
|
case -1:
|
||
|
return;
|
||
|
default:
|
||
|
igt_assert(false);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int main(int argc, char *argv[])
|
||
|
{
|
||
|
parse_opts(argc, argv);
|
||
|
|
||
|
setup_msr();
|
||
|
setup_drm();
|
||
|
setup_modeset();
|
||
|
setup_vblank_interval();
|
||
|
setup_alarm();
|
||
|
|
||
|
printf("Test name:\t%s\n", opts.test_name);
|
||
|
|
||
|
unset_mode();
|
||
|
set_mode();
|
||
|
|
||
|
setup_idle();
|
||
|
|
||
|
if (opts.do_page_flip)
|
||
|
page_flip_test();
|
||
|
|
||
|
if (opts.do_draw)
|
||
|
draw_test();
|
||
|
|
||
|
if (opts.do_draw_and_flip)
|
||
|
draw_and_flip_test();
|
||
|
|
||
|
teardown_modeset();
|
||
|
teardown_drm();
|
||
|
teardown_msr();
|
||
|
return 0;
|
||
|
}
|