modetest.c revision d049871a
1/*
2 * DRM based mode setting test program
3 * Copyright 2008 Tungsten Graphics
4 *   Jakob Bornecrantz <jakob@tungstengraphics.com>
5 * Copyright 2008 Intel Corporation
6 *   Jesse Barnes <jesse.barnes@intel.com>
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24 * IN THE SOFTWARE.
25 */
26
27/*
28 * This fairly simple test program dumps output in a similar format to the
29 * "xrandr" tool everyone knows & loves.  It's necessarily slightly different
30 * since the kernel separates outputs into encoder and connector structures,
31 * each with their own unique ID.  The program also allows test testing of the
32 * memory management and mode setting APIs by allowing the user to specify a
33 * connector and mode to use for mode setting.  If all works as expected, a
34 * blue background should be painted on the monitor attached to the specified
35 * connector after the selected mode is set.
36 *
37 * TODO: use cairo to write the mode info on the selected output once
38 *       the mode has been programmed, along with possible test patterns.
39 */
40#include "config.h"
41
42#include <assert.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <stdint.h>
46#include <unistd.h>
47#include <string.h>
48#include <errno.h>
49#include <sys/poll.h>
50#include <sys/time.h>
51
52#include "xf86drm.h"
53#include "xf86drmMode.h"
54#include "intel_bufmgr.h"
55#include "i915_drm.h"
56
57#ifdef HAVE_CAIRO
58#include <math.h>
59#include <cairo.h>
60#endif
61
62drmModeRes *resources;
63int fd, modes;
64
65#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
66
67struct type_name {
68	int type;
69	char *name;
70};
71
72#define type_name_fn(res) \
73char * res##_str(int type) {			\
74	int i;						\
75	for (i = 0; i < ARRAY_SIZE(res##_names); i++) { \
76		if (res##_names[i].type == type)	\
77			return res##_names[i].name;	\
78	}						\
79	return "(invalid)";				\
80}
81
82struct type_name encoder_type_names[] = {
83	{ DRM_MODE_ENCODER_NONE, "none" },
84	{ DRM_MODE_ENCODER_DAC, "DAC" },
85	{ DRM_MODE_ENCODER_TMDS, "TMDS" },
86	{ DRM_MODE_ENCODER_LVDS, "LVDS" },
87	{ DRM_MODE_ENCODER_TVDAC, "TVDAC" },
88};
89
90type_name_fn(encoder_type)
91
92struct type_name connector_status_names[] = {
93	{ DRM_MODE_CONNECTED, "connected" },
94	{ DRM_MODE_DISCONNECTED, "disconnected" },
95	{ DRM_MODE_UNKNOWNCONNECTION, "unknown" },
96};
97
98type_name_fn(connector_status)
99
100struct type_name connector_type_names[] = {
101	{ DRM_MODE_CONNECTOR_Unknown, "unknown" },
102	{ DRM_MODE_CONNECTOR_VGA, "VGA" },
103	{ DRM_MODE_CONNECTOR_DVII, "DVI-I" },
104	{ DRM_MODE_CONNECTOR_DVID, "DVI-D" },
105	{ DRM_MODE_CONNECTOR_DVIA, "DVI-A" },
106	{ DRM_MODE_CONNECTOR_Composite, "composite" },
107	{ DRM_MODE_CONNECTOR_SVIDEO, "s-video" },
108	{ DRM_MODE_CONNECTOR_LVDS, "LVDS" },
109	{ DRM_MODE_CONNECTOR_Component, "component" },
110	{ DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN" },
111	{ DRM_MODE_CONNECTOR_DisplayPort, "displayport" },
112	{ DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" },
113	{ DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" },
114	{ DRM_MODE_CONNECTOR_TV, "TV" },
115	{ DRM_MODE_CONNECTOR_eDP, "embedded displayport" },
116};
117
118type_name_fn(connector_type)
119
120void dump_encoders(void)
121{
122	drmModeEncoder *encoder;
123	int i;
124
125	printf("Encoders:\n");
126	printf("id\tcrtc\ttype\tpossible crtcs\tpossible clones\t\n");
127	for (i = 0; i < resources->count_encoders; i++) {
128		encoder = drmModeGetEncoder(fd, resources->encoders[i]);
129
130		if (!encoder) {
131			fprintf(stderr, "could not get encoder %i: %s\n",
132				resources->encoders[i], strerror(errno));
133			continue;
134		}
135		printf("%d\t%d\t%s\t0x%08x\t0x%08x\n",
136		       encoder->encoder_id,
137		       encoder->crtc_id,
138		       encoder_type_str(encoder->encoder_type),
139		       encoder->possible_crtcs,
140		       encoder->possible_clones);
141		drmModeFreeEncoder(encoder);
142	}
143	printf("\n");
144}
145
146void dump_mode(drmModeModeInfo *mode)
147{
148	printf("  %s %d %d %d %d %d %d %d %d %d\n",
149	       mode->name,
150	       mode->vrefresh,
151	       mode->hdisplay,
152	       mode->hsync_start,
153	       mode->hsync_end,
154	       mode->htotal,
155	       mode->vdisplay,
156	       mode->vsync_start,
157	       mode->vsync_end,
158	       mode->vtotal);
159}
160
161static void
162dump_props(drmModeConnector *connector)
163{
164	drmModePropertyPtr props;
165	int i;
166
167	for (i = 0; i < connector->count_props; i++) {
168		props = drmModeGetProperty(fd, connector->props[i]);
169		printf("\t%s, flags %d\n", props->name, props->flags);
170		drmModeFreeProperty(props);
171	}
172}
173
174void dump_connectors(void)
175{
176	drmModeConnector *connector;
177	int i, j;
178
179	printf("Connectors:\n");
180	printf("id\tencoder\tstatus\t\ttype\tsize (mm)\tmodes\tencoders\n");
181	for (i = 0; i < resources->count_connectors; i++) {
182		connector = drmModeGetConnector(fd, resources->connectors[i]);
183
184		if (!connector) {
185			fprintf(stderr, "could not get connector %i: %s\n",
186				resources->connectors[i], strerror(errno));
187			continue;
188		}
189
190		printf("%d\t%d\t%s\t%s\t%dx%d\t\t%d\t",
191		       connector->connector_id,
192		       connector->encoder_id,
193		       connector_status_str(connector->connection),
194		       connector_type_str(connector->connector_type),
195		       connector->mmWidth, connector->mmHeight,
196		       connector->count_modes);
197
198		for (j = 0; j < connector->count_encoders; j++)
199			printf("%s%d", j > 0 ? ", " : "", connector->encoders[j]);
200		printf("\n");
201
202		if (!connector->count_modes)
203			continue;
204
205		printf("  modes:\n");
206		printf("  name refresh (Hz) hdisp hss hse htot vdisp "
207		       "vss vse vtot)\n");
208		for (j = 0; j < connector->count_modes; j++)
209			dump_mode(&connector->modes[j]);
210
211		printf("  props:\n");
212		dump_props(connector);
213
214		drmModeFreeConnector(connector);
215	}
216	printf("\n");
217}
218
219void dump_crtcs(void)
220{
221	drmModeCrtc *crtc;
222	int i;
223
224	printf("CRTCs:\n");
225	printf("id\tfb\tpos\tsize\n");
226	for (i = 0; i < resources->count_crtcs; i++) {
227		crtc = drmModeGetCrtc(fd, resources->crtcs[i]);
228
229		if (!crtc) {
230			fprintf(stderr, "could not get crtc %i: %s\n",
231				resources->crtcs[i], strerror(errno));
232			continue;
233		}
234		printf("%d\t%d\t(%d,%d)\t(%dx%d)\n",
235		       crtc->crtc_id,
236		       crtc->buffer_id,
237		       crtc->x, crtc->y,
238		       crtc->width, crtc->height);
239		dump_mode(&crtc->mode);
240
241		drmModeFreeCrtc(crtc);
242	}
243	printf("\n");
244}
245
246void dump_framebuffers(void)
247{
248	drmModeFB *fb;
249	int i;
250
251	printf("Frame buffers:\n");
252	printf("id\tsize\tpitch\n");
253	for (i = 0; i < resources->count_fbs; i++) {
254		fb = drmModeGetFB(fd, resources->fbs[i]);
255
256		if (!fb) {
257			fprintf(stderr, "could not get fb %i: %s\n",
258				resources->fbs[i], strerror(errno));
259			continue;
260		}
261		printf("%u\t(%ux%u)\t%u\n",
262		       fb->fb_id,
263		       fb->width, fb->height,
264		       fb->pitch);
265
266		drmModeFreeFB(fb);
267	}
268	printf("\n");
269}
270
271/*
272 * Mode setting with the kernel interfaces is a bit of a chore.
273 * First you have to find the connector in question and make sure the
274 * requested mode is available.
275 * Then you need to find the encoder attached to that connector so you
276 * can bind it with a free crtc.
277 */
278struct connector {
279	uint32_t id;
280	char mode_str[64];
281	drmModeModeInfo *mode;
282	drmModeEncoder *encoder;
283	int crtc;
284	unsigned int fb_id[2], current_fb_id;
285	struct timeval start;
286
287	int swap_count;
288};
289
290static void
291connector_find_mode(struct connector *c)
292{
293	drmModeConnector *connector;
294	int i, j;
295
296	/* First, find the connector & mode */
297	c->mode = NULL;
298	for (i = 0; i < resources->count_connectors; i++) {
299		connector = drmModeGetConnector(fd, resources->connectors[i]);
300
301		if (!connector) {
302			fprintf(stderr, "could not get connector %i: %s\n",
303				resources->connectors[i], strerror(errno));
304			drmModeFreeConnector(connector);
305			continue;
306		}
307
308		if (!connector->count_modes) {
309			drmModeFreeConnector(connector);
310			continue;
311		}
312
313		if (connector->connector_id != c->id) {
314			drmModeFreeConnector(connector);
315			continue;
316		}
317
318		for (j = 0; j < connector->count_modes; j++) {
319			c->mode = &connector->modes[j];
320			if (!strcmp(c->mode->name, c->mode_str))
321				break;
322		}
323
324		/* Found it, break out */
325		if (c->mode)
326			break;
327
328		drmModeFreeConnector(connector);
329	}
330
331	if (!c->mode) {
332		fprintf(stderr, "failed to find mode \"%s\"\n", c->mode_str);
333		return;
334	}
335
336	/* Now get the encoder */
337	for (i = 0; i < resources->count_encoders; i++) {
338		c->encoder = drmModeGetEncoder(fd, resources->encoders[i]);
339
340		if (!c->encoder) {
341			fprintf(stderr, "could not get encoder %i: %s\n",
342				resources->encoders[i], strerror(errno));
343			drmModeFreeEncoder(c->encoder);
344			continue;
345		}
346
347		if (c->encoder->encoder_id  == connector->encoder_id)
348			break;
349
350		drmModeFreeEncoder(c->encoder);
351	}
352
353	if (c->crtc == -1)
354		c->crtc = c->encoder->crtc_id;
355}
356
357static drm_intel_bo *
358allocate_buffer(drm_intel_bufmgr *bufmgr,
359		int width, int height, int *stride)
360{
361	int size;
362
363	/* Scan-out has a 64 byte alignment restriction */
364	size = (width + 63) & -64;
365	*stride = size;
366	size *= height;
367
368	return drm_intel_bo_alloc(bufmgr, "frontbuffer", size, 0);
369}
370
371static void
372make_pwetty(drm_intel_bo *bo, int width, int height, int stride)
373{
374#ifdef HAVE_CAIRO
375	cairo_surface_t *surface;
376	cairo_t *cr;
377	int x, y;
378
379	surface = cairo_image_surface_create_for_data(bo->virtual,
380						      CAIRO_FORMAT_ARGB32,
381						      width, height,
382						      stride);
383	cr = cairo_create(surface);
384	cairo_surface_destroy(surface);
385
386	cairo_set_line_cap(cr, CAIRO_LINE_CAP_SQUARE);
387	for (x = 0; x < width; x += 250)
388		for (y = 0; y < height; y += 250) {
389			char buf[64];
390
391			cairo_move_to(cr, x, y - 20);
392			cairo_line_to(cr, x, y + 20);
393			cairo_move_to(cr, x - 20, y);
394			cairo_line_to(cr, x + 20, y);
395			cairo_new_sub_path(cr);
396			cairo_arc(cr, x, y, 10, 0, M_PI * 2);
397			cairo_set_line_width(cr, 4);
398			cairo_set_source_rgb(cr, 0, 0, 0);
399			cairo_stroke_preserve(cr);
400			cairo_set_source_rgb(cr, 1, 1, 1);
401			cairo_set_line_width(cr, 2);
402			cairo_stroke(cr);
403
404			snprintf(buf, sizeof buf, "%d, %d", x, y);
405			cairo_move_to(cr, x + 20, y + 20);
406			cairo_text_path(cr, buf);
407			cairo_set_source_rgb(cr, 0, 0, 0);
408			cairo_stroke_preserve(cr);
409			cairo_set_source_rgb(cr, 1, 1, 1);
410			cairo_fill(cr);
411		}
412
413	cairo_destroy(cr);
414#endif
415}
416
417static int
418create_test_buffer(drm_intel_bufmgr *bufmgr,
419		   int width, int height, int *stride_out, drm_intel_bo **bo_out)
420{
421	drm_intel_bo *bo;
422	int ret, i, j, stride;
423
424	bo = allocate_buffer(bufmgr, width, height, &stride);
425	if (!bo) {
426		fprintf(stderr, "failed to alloc buffer: %s\n",
427			strerror(errno));
428		return -1;
429	}
430
431	ret = drm_intel_gem_bo_map_gtt(bo);
432	if (ret) {
433		fprintf(stderr, "failed to GTT map buffer: %s\n",
434			strerror(errno));
435		return -1;
436	}
437
438	/* paint the buffer with colored tiles */
439	for (j = 0; j < height; j++) {
440		uint32_t *fb_ptr = (uint32_t*)((char*)bo->virtual + j * stride);
441		for (i = 0; i < width; i++) {
442			div_t d = div(i, width);
443			fb_ptr[i] =
444				0x00130502 * (d.quot >> 6) +
445				0x000a1120 * (d.rem >> 6);
446		}
447	}
448
449	make_pwetty(bo, width, height, stride);
450
451	drm_intel_gem_bo_unmap_gtt(bo);
452
453	*bo_out = bo;
454	*stride_out = stride;
455	return 0;
456}
457
458static int
459create_grey_buffer(drm_intel_bufmgr *bufmgr,
460		   int width, int height, int *stride_out, drm_intel_bo **bo_out)
461{
462	drm_intel_bo *bo;
463	int size, ret, stride;
464
465	/* Mode size at 32 bpp */
466	stride = width * 4;
467	size = stride * height;
468
469	bo = drm_intel_bo_alloc(bufmgr, "frontbuffer", size, 4096);
470	if (!bo) {
471		fprintf(stderr, "failed to alloc buffer: %s\n",
472			strerror(errno));
473		return -1;
474	}
475
476	ret = drm_intel_gem_bo_map_gtt(bo);
477	if (ret) {
478		fprintf(stderr, "failed to GTT map buffer: %s\n",
479			strerror(errno));
480		return -1;
481	}
482
483	memset(bo->virtual, 0x77, size);
484	drm_intel_gem_bo_unmap_gtt(bo);
485
486	*bo_out = bo;
487	*stride_out = stride;
488
489	return 0;
490}
491
492void
493page_flip_handler(int fd, unsigned int frame,
494		  unsigned int sec, unsigned int usec, void *data)
495{
496	struct connector *c;
497	unsigned int new_fb_id;
498	struct timeval end;
499	double t;
500
501	c = data;
502	if (c->current_fb_id == c->fb_id[0])
503		new_fb_id = c->fb_id[1];
504	else
505		new_fb_id = c->fb_id[0];
506
507	drmModePageFlip(fd, c->crtc, new_fb_id,
508			DRM_MODE_PAGE_FLIP_EVENT, c);
509	c->current_fb_id = new_fb_id;
510	c->swap_count++;
511	if (c->swap_count == 60) {
512		gettimeofday(&end, NULL);
513		t = end.tv_sec + end.tv_usec * 1e-6 -
514			(c->start.tv_sec + c->start.tv_usec * 1e-6);
515		fprintf(stderr, "freq: %.02fHz\n", c->swap_count / t);
516		c->swap_count = 0;
517		c->start = end;
518	}
519}
520
521static void
522set_mode(struct connector *c, int count, int page_flip)
523{
524	drm_intel_bufmgr *bufmgr;
525	drm_intel_bo *bo, *other_bo;
526	unsigned int fb_id, other_fb_id;
527	int i, ret, width, height, x, stride;
528	drmEventContext evctx;
529
530	width = 0;
531	height = 0;
532	for (i = 0; i < count; i++) {
533		connector_find_mode(&c[i]);
534		if (c[i].mode == NULL)
535			continue;
536		width += c[i].mode->hdisplay;
537		if (height < c[i].mode->vdisplay)
538			height = c[i].mode->vdisplay;
539	}
540
541	bufmgr = drm_intel_bufmgr_gem_init(fd, 2<<20);
542	if (!bufmgr) {
543		fprintf(stderr, "failed to init bufmgr: %s\n", strerror(errno));
544		return;
545	}
546
547	if (create_test_buffer(bufmgr, width, height, &stride, &bo))
548		return;
549
550	ret = drmModeAddFB(fd, width, height, 32, 32, stride, bo->handle,
551			   &fb_id);
552	if (ret) {
553		fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
554		return;
555	}
556
557	x = 0;
558	for (i = 0; i < count; i++) {
559		if (c[i].mode == NULL)
560			continue;
561
562		printf("setting mode %s on connector %d, crtc %d\n",
563		       c[i].mode_str, c[i].id, c[i].crtc);
564
565		ret = drmModeSetCrtc(fd, c[i].crtc, fb_id, x, 0,
566				     &c[i].id, 1, c[i].mode);
567		x += c[i].mode->hdisplay;
568
569		if (ret) {
570			fprintf(stderr, "failed to set mode: %s\n", strerror(errno));
571			return;
572		}
573	}
574
575	if (!page_flip)
576		return;
577
578	if (create_grey_buffer(bufmgr, width, height, &stride, &other_bo))
579		return;
580
581	ret = drmModeAddFB(fd, width, height, 32, 32, stride, other_bo->handle,
582			   &other_fb_id);
583	if (ret) {
584		fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
585		return;
586	}
587
588	for (i = 0; i < count; i++) {
589		if (c[i].mode == NULL)
590			continue;
591
592		drmModePageFlip(fd, c[i].crtc, other_fb_id,
593				DRM_MODE_PAGE_FLIP_EVENT, &c[i]);
594		gettimeofday(&c[i].start, NULL);
595		c[i].swap_count = 0;
596		c[i].fb_id[0] = fb_id;
597		c[i].fb_id[1] = other_fb_id;
598		c[i].current_fb_id = fb_id;
599	}
600
601	memset(&evctx, 0, sizeof evctx);
602	evctx.version = DRM_EVENT_CONTEXT_VERSION;
603	evctx.vblank_handler = NULL;
604	evctx.page_flip_handler = page_flip_handler;
605
606	while (1) {
607#if 0
608		struct pollfd pfd[2];
609
610		pfd[0].fd = 0;
611		pfd[0].events = POLLIN;
612		pfd[1].fd = fd;
613		pfd[1].events = POLLIN;
614
615		if (poll(pfd, 2, -1) < 0) {
616			fprintf(stderr, "poll error\n");
617			break;
618		}
619
620		if (pfd[0].revents)
621			break;
622#else
623		struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 };
624		fd_set fds;
625		int ret;
626
627		FD_ZERO(&fds);
628		FD_SET(0, &fds);
629		FD_SET(fd, &fds);
630		ret = select(fd + 1, &fds, NULL, NULL, &timeout);
631
632		if (ret <= 0) {
633			fprintf(stderr, "select timed out or error (ret %d)\n",
634				ret);
635			continue;
636		} else if (FD_ISSET(0, &fds)) {
637			break;
638		}
639#endif
640
641		drmHandleEvent(fd, &evctx);
642	}
643}
644
645extern char *optarg;
646extern int optind, opterr, optopt;
647static char optstr[] = "ecpmfs:v";
648
649void usage(char *name)
650{
651	fprintf(stderr, "usage: %s [-ecpmf]\n", name);
652	fprintf(stderr, "\t-e\tlist encoders\n");
653	fprintf(stderr, "\t-c\tlist connectors\n");
654	fprintf(stderr, "\t-p\tlist CRTCs (pipes)\n");
655	fprintf(stderr, "\t-m\tlist modes\n");
656	fprintf(stderr, "\t-f\tlist framebuffers\n");
657	fprintf(stderr, "\t-v\ttest vsynced page flipping\n");
658	fprintf(stderr, "\t-s <connector_id>:<mode>\tset a mode\n");
659	fprintf(stderr, "\t-s <connector_id>@<crtc_id>:<mode>\tset a mode\n");
660	fprintf(stderr, "\n\tDefault is to dump all info.\n");
661	exit(0);
662}
663
664#define dump_resource(res) if (res) dump_##res()
665
666static int page_flipping_supported(int fd)
667{
668	int ret, value;
669	struct drm_i915_getparam gp;
670
671	gp.param = I915_PARAM_HAS_PAGEFLIPPING;
672	gp.value = &value;
673
674	ret = drmCommandWriteRead(fd, DRM_I915_GETPARAM, &gp, sizeof(gp));
675	if (ret) {
676		fprintf(stderr, "drm_i915_getparam: %m\n");
677		return 0;
678	}
679
680	return *gp.value;
681}
682
683int main(int argc, char **argv)
684{
685	int c;
686	int encoders = 0, connectors = 0, crtcs = 0, framebuffers = 0;
687	int test_vsync = 0;
688	char *modules[] = { "i915", "radeon", "nouveau" };
689	char *modeset = NULL;
690	int i, count = 0;
691	struct connector con_args[2];
692
693	opterr = 0;
694	while ((c = getopt(argc, argv, optstr)) != -1) {
695		switch (c) {
696		case 'e':
697			encoders = 1;
698			break;
699		case 'c':
700			connectors = 1;
701			break;
702		case 'p':
703			crtcs = 1;
704			break;
705		case 'm':
706			modes = 1;
707			break;
708		case 'f':
709			framebuffers = 1;
710			break;
711		case 'v':
712			test_vsync = 1;
713			break;
714		case 's':
715			modeset = strdup(optarg);
716			con_args[count].crtc = -1;
717			if (sscanf(optarg, "%d:%64s",
718				   &con_args[count].id,
719				   con_args[count].mode_str) != 2 &&
720			    sscanf(optarg, "%d@%d:%64s",
721				   &con_args[count].id,
722				   &con_args[count].crtc,
723				   con_args[count].mode_str) != 3)
724				usage(argv[0]);
725			count++;
726			break;
727		default:
728			usage(argv[0]);
729			break;
730		}
731	}
732
733	if (argc == 1)
734		encoders = connectors = crtcs = modes = framebuffers = 1;
735
736	for (i = 0; i < ARRAY_SIZE(modules); i++) {
737		printf("trying to load module %s...", modules[i]);
738		fd = drmOpen(modules[i], NULL);
739		if (fd < 0) {
740			printf("failed.\n");
741		} else {
742			printf("success.\n");
743			break;
744		}
745	}
746
747	if (test_vsync && !page_flipping_supported(fd)) {
748		fprintf(stderr, "page flipping not supported by drm.\n");
749		return -1;
750	}
751
752	if (i == ARRAY_SIZE(modules)) {
753		fprintf(stderr, "failed to load any modules, aborting.\n");
754		return -1;
755	}
756
757	resources = drmModeGetResources(fd);
758	if (!resources) {
759		fprintf(stderr, "drmModeGetResources failed: %s\n",
760			strerror(errno));
761		drmClose(fd);
762		return 1;
763	}
764
765	dump_resource(encoders);
766	dump_resource(connectors);
767	dump_resource(crtcs);
768	dump_resource(framebuffers);
769
770	if (count > 0) {
771		set_mode(con_args, count, test_vsync);
772		getchar();
773	}
774
775	drmModeFreeResources(resources);
776
777	return 0;
778}
779