modetest.c revision 694ef59532253727176ed0ce9077ae3ec41dd457
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};
115
116type_name_fn(connector_type)
117
118void dump_encoders(void)
119{
120	drmModeEncoder *encoder;
121	int i;
122
123	printf("Encoders:\n");
124	printf("id\tcrtc\ttype\tpossible crtcs\tpossible clones\t\n");
125	for (i = 0; i < resources->count_encoders; i++) {
126		encoder = drmModeGetEncoder(fd, resources->encoders[i]);
127
128		if (!encoder) {
129			fprintf(stderr, "could not get encoder %i: %s\n",
130				resources->encoders[i], strerror(errno));
131			continue;
132		}
133		printf("%d\t%d\t%s\t0x%08x\t0x%08x\n",
134		       encoder->encoder_id,
135		       encoder->crtc_id,
136		       encoder_type_str(encoder->encoder_type),
137		       encoder->possible_crtcs,
138		       encoder->possible_clones);
139		drmModeFreeEncoder(encoder);
140	}
141	printf("\n");
142}
143
144void dump_mode(drmModeModeInfo *mode)
145{
146	printf("  %s %d %d %d %d %d %d %d %d %d\n",
147	       mode->name,
148	       mode->vrefresh,
149	       mode->hdisplay,
150	       mode->hsync_start,
151	       mode->hsync_end,
152	       mode->htotal,
153	       mode->vdisplay,
154	       mode->vsync_start,
155	       mode->vsync_end,
156	       mode->vtotal);
157}
158
159static void
160dump_props(drmModeConnector *connector)
161{
162	drmModePropertyPtr props;
163	int i;
164
165	for (i = 0; i < connector->count_props; i++) {
166		props = drmModeGetProperty(fd, connector->props[i]);
167		printf("\t%s, flags %d\n", props->name, props->flags);
168		drmModeFreeProperty(props);
169	}
170}
171
172void dump_connectors(void)
173{
174	drmModeConnector *connector;
175	int i, j;
176
177	printf("Connectors:\n");
178	printf("id\tencoder\tstatus\t\ttype\tsize (mm)\tmodes\tencoders\n");
179	for (i = 0; i < resources->count_connectors; i++) {
180		connector = drmModeGetConnector(fd, resources->connectors[i]);
181
182		if (!connector) {
183			fprintf(stderr, "could not get connector %i: %s\n",
184				resources->connectors[i], strerror(errno));
185			continue;
186		}
187
188		printf("%d\t%d\t%s\t%s\t%dx%d\t\t%d\t",
189		       connector->connector_id,
190		       connector->encoder_id,
191		       connector_status_str(connector->connection),
192		       connector_type_str(connector->connector_type),
193		       connector->mmWidth, connector->mmHeight,
194		       connector->count_modes);
195
196		for (j = 0; j < connector->count_encoders; j++)
197			printf("%s%d", j > 0 ? ", " : "", connector->encoders[j]);
198		printf("\n");
199
200		if (!connector->count_modes)
201			continue;
202
203		printf("  modes:\n");
204		printf("  name refresh (Hz) hdisp hss hse htot vdisp "
205		       "vss vse vtot)\n");
206		for (j = 0; j < connector->count_modes; j++)
207			dump_mode(&connector->modes[j]);
208
209		drmModeFreeConnector(connector);
210
211		printf("  props:\n");
212		dump_props(connector);
213	}
214	printf("\n");
215}
216
217void dump_crtcs(void)
218{
219	drmModeCrtc *crtc;
220	int i;
221
222	printf("CRTCs:\n");
223	printf("id\tfb\tpos\tsize\n");
224	for (i = 0; i < resources->count_crtcs; i++) {
225		crtc = drmModeGetCrtc(fd, resources->crtcs[i]);
226
227		if (!crtc) {
228			fprintf(stderr, "could not get crtc %i: %s\n",
229				resources->crtcs[i], strerror(errno));
230			continue;
231		}
232		printf("%d\t%d\t(%d,%d)\t(%dx%d)\n",
233		       crtc->crtc_id,
234		       crtc->buffer_id,
235		       crtc->x, crtc->y,
236		       crtc->width, crtc->height);
237		dump_mode(&crtc->mode);
238
239		drmModeFreeCrtc(crtc);
240	}
241	printf("\n");
242}
243
244void dump_framebuffers(void)
245{
246	drmModeFB *fb;
247	int i;
248
249	printf("Frame buffers:\n");
250	printf("id\tsize\tpitch\n");
251	for (i = 0; i < resources->count_fbs; i++) {
252		fb = drmModeGetFB(fd, resources->fbs[i]);
253
254		if (!fb) {
255			fprintf(stderr, "could not get fb %i: %s\n",
256				resources->fbs[i], strerror(errno));
257			continue;
258		}
259		printf("%u\t(%ux%u)\t%u\n",
260		       fb->fb_id,
261		       fb->width, fb->height,
262		       fb->pitch);
263
264		drmModeFreeFB(fb);
265	}
266	printf("\n");
267}
268
269/*
270 * Mode setting with the kernel interfaces is a bit of a chore.
271 * First you have to find the connector in question and make sure the
272 * requested mode is available.
273 * Then you need to find the encoder attached to that connector so you
274 * can bind it with a free crtc.
275 */
276struct connector {
277	uint32_t id;
278	char mode_str[64];
279	drmModeModeInfo *mode;
280	drmModeEncoder *encoder;
281	int crtc;
282	unsigned int fb_id[2], current_fb_id;
283	struct timeval start;
284
285	int swap_count;
286};
287
288static void
289connector_find_mode(struct connector *c)
290{
291	drmModeConnector *connector;
292	int i, j;
293
294	/* First, find the connector & mode */
295	c->mode = NULL;
296	for (i = 0; i < resources->count_connectors; i++) {
297		connector = drmModeGetConnector(fd, resources->connectors[i]);
298
299		if (!connector) {
300			fprintf(stderr, "could not get connector %i: %s\n",
301				resources->connectors[i], strerror(errno));
302			drmModeFreeConnector(connector);
303			continue;
304		}
305
306		if (!connector->count_modes) {
307			drmModeFreeConnector(connector);
308			continue;
309		}
310
311		if (connector->connector_id != c->id) {
312			drmModeFreeConnector(connector);
313			continue;
314		}
315
316		for (j = 0; j < connector->count_modes; j++) {
317			c->mode = &connector->modes[j];
318			if (!strcmp(c->mode->name, c->mode_str))
319				break;
320		}
321
322		/* Found it, break out */
323		if (c->mode)
324			break;
325
326		drmModeFreeConnector(connector);
327	}
328
329	if (!c->mode) {
330		fprintf(stderr, "failed to find mode \"%s\"\n", c->mode_str);
331		return;
332	}
333
334	/* Now get the encoder */
335	for (i = 0; i < resources->count_encoders; i++) {
336		c->encoder = drmModeGetEncoder(fd, resources->encoders[i]);
337
338		if (!c->encoder) {
339			fprintf(stderr, "could not get encoder %i: %s\n",
340				resources->encoders[i], strerror(errno));
341			drmModeFreeEncoder(c->encoder);
342			continue;
343		}
344
345		if (c->encoder->encoder_id  == connector->encoder_id)
346			break;
347
348		drmModeFreeEncoder(c->encoder);
349	}
350
351	if (c->crtc == -1)
352		c->crtc = c->encoder->crtc_id;
353}
354
355#ifdef HAVE_CAIRO
356
357static int
358create_test_buffer(drm_intel_bufmgr *bufmgr,
359		   int width, int height, int *stride_out, drm_intel_bo **bo_out)
360{
361	drm_intel_bo *bo;
362	unsigned int *fb_ptr;
363	int size, i, stride;
364	div_t d;
365	cairo_surface_t *surface;
366	cairo_t *cr;
367	char buf[64];
368	int x, y;
369
370	surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
371	stride = cairo_image_surface_get_stride(surface);
372	size = stride * height;
373	fb_ptr = (unsigned int *) cairo_image_surface_get_data(surface);
374
375	/* paint the buffer with colored tiles */
376	for (i = 0; i < width * height; i++) {
377		d = div(i, width);
378		fb_ptr[i] = 0x00130502 * (d.quot >> 6) + 0x000a1120 * (d.rem >> 6);
379	}
380
381	cr = cairo_create(surface);
382	cairo_set_line_cap(cr, CAIRO_LINE_CAP_SQUARE);
383	for (x = 0; x < width; x += 250)
384		for (y = 0; y < height; y += 250) {
385			cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
386			cairo_move_to(cr, x, y - 20);
387			cairo_line_to(cr, x, y + 20);
388			cairo_move_to(cr, x - 20, y);
389			cairo_line_to(cr, x + 20, y);
390			cairo_new_sub_path(cr);
391			cairo_arc(cr, x, y, 10, 0, M_PI * 2);
392			cairo_set_line_width(cr, 4);
393			cairo_set_source_rgb(cr, 0, 0, 0);
394			cairo_stroke_preserve(cr);
395			cairo_set_source_rgb(cr, 1, 1, 1);
396			cairo_set_line_width(cr, 2);
397			cairo_stroke(cr);
398			snprintf(buf, sizeof buf, "%d, %d", x, y);
399			cairo_move_to(cr, x + 20, y + 20);
400			cairo_text_path(cr, buf);
401			cairo_set_source_rgb(cr, 0, 0, 0);
402			cairo_stroke_preserve(cr);
403			cairo_set_source_rgb(cr, 1, 1, 1);
404			cairo_fill(cr);
405		}
406
407	cairo_destroy(cr);
408
409	bo = drm_intel_bo_alloc(bufmgr, "frontbuffer", size, 4096);
410	if (!bo) {
411		fprintf(stderr, "failed to alloc buffer: %s\n",
412			strerror(errno));
413		return -1;
414	}
415
416	drm_intel_bo_subdata(bo, 0, size, fb_ptr);
417
418	cairo_surface_destroy(surface);
419
420	*bo_out = bo;
421	*stride_out = stride;
422
423	return 0;
424}
425
426#else
427
428static int
429create_test_buffer(drm_intel_bufmgr *bufmgr,
430		   int width, int height, int *stride_out, drm_intel_bo **bo_out)
431{
432	drm_intel_bo *bo;
433	unsigned int *fb_ptr;
434	int size, ret, i, stride;
435	div_t d;
436
437	/* Mode size at 32 bpp */
438	stride = width * 4;
439	size = stride * height;
440
441	bo = drm_intel_bo_alloc(bufmgr, "frontbuffer", size, 4096);
442	if (!bo) {
443		fprintf(stderr, "failed to alloc buffer: %s\n",
444			strerror(errno));
445		return -1;
446	}
447
448	ret = drm_intel_gem_bo_map_gtt(bo);
449	if (ret) {
450		fprintf(stderr, "failed to GTT map buffer: %s\n",
451			strerror(errno));
452		return -1;
453	}
454
455	fb_ptr = bo->virtual;
456
457	/* paint the buffer with colored tiles */
458	for (i = 0; i < width * height; i++) {
459		d = div(i, width);
460		fb_ptr[i] = 0x00130502 * (d.quot >> 6) + 0x000a1120 * (d.rem >> 6);
461	}
462	drm_intel_gem_bo_unmap_gtt(bo);
463
464	*bo_out = bo;
465	*stride_out = stride;
466
467	return 0;
468}
469
470#endif
471
472static int
473create_grey_buffer(drm_intel_bufmgr *bufmgr,
474		   int width, int height, int *stride_out, drm_intel_bo **bo_out)
475{
476	drm_intel_bo *bo;
477	int size, ret, stride;
478
479	/* Mode size at 32 bpp */
480	stride = width * 4;
481	size = stride * height;
482
483	bo = drm_intel_bo_alloc(bufmgr, "frontbuffer", size, 4096);
484	if (!bo) {
485		fprintf(stderr, "failed to alloc buffer: %s\n",
486			strerror(errno));
487		return -1;
488	}
489
490	ret = drm_intel_gem_bo_map_gtt(bo);
491	if (ret) {
492		fprintf(stderr, "failed to GTT map buffer: %s\n",
493			strerror(errno));
494		return -1;
495	}
496
497	memset(bo->virtual, 0x77, size);
498	drm_intel_gem_bo_unmap_gtt(bo);
499
500	*bo_out = bo;
501	*stride_out = stride;
502
503	return 0;
504}
505
506void
507page_flip_handler(int fd, unsigned int frame,
508		  unsigned int sec, unsigned int usec, void *data)
509{
510	struct connector *c;
511	unsigned int new_fb_id;
512	struct timeval end;
513	double t;
514
515	c = data;
516	if (c->current_fb_id == c->fb_id[0])
517		new_fb_id = c->fb_id[1];
518	else
519		new_fb_id = c->fb_id[0];
520
521	drmModePageFlip(fd, c->crtc, new_fb_id,
522			DRM_MODE_PAGE_FLIP_EVENT, c);
523	c->current_fb_id = new_fb_id;
524	c->swap_count++;
525	if (c->swap_count == 60) {
526		gettimeofday(&end, NULL);
527		t = end.tv_sec + end.tv_usec * 1e-6 -
528			(c->start.tv_sec + c->start.tv_usec * 1e-6);
529		fprintf(stderr, "freq: %.02fHz\n", c->swap_count / t);
530		c->swap_count = 0;
531		c->start = end;
532	}
533}
534
535static void
536set_mode(struct connector *c, int count, int page_flip)
537{
538	drm_intel_bufmgr *bufmgr;
539	drm_intel_bo *bo, *other_bo;
540	unsigned int fb_id, other_fb_id;
541	int i, ret, width, height, x, stride;
542	drmEventContext evctx;
543
544	width = 0;
545	height = 0;
546	for (i = 0; i < count; i++) {
547		connector_find_mode(&c[i]);
548		if (c[i].mode == NULL)
549			continue;
550		width += c[i].mode->hdisplay;
551		if (height < c[i].mode->vdisplay)
552			height = c[i].mode->vdisplay;
553	}
554
555	bufmgr = drm_intel_bufmgr_gem_init(fd, 2<<20);
556	if (!bufmgr) {
557		fprintf(stderr, "failed to init bufmgr: %s\n", strerror(errno));
558		return;
559	}
560
561	if (create_test_buffer(bufmgr, width, height, &stride, &bo))
562		return;
563
564	ret = drmModeAddFB(fd, width, height, 32, 32, stride, bo->handle,
565			   &fb_id);
566	if (ret) {
567		fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
568		return;
569	}
570
571	x = 0;
572	for (i = 0; i < count; i++) {
573		if (c[i].mode == NULL)
574			continue;
575
576		printf("setting mode %s on connector %d, crtc %d\n",
577		       c[i].mode_str, c[i].id, c[i].crtc);
578
579		ret = drmModeSetCrtc(fd, c[i].crtc, fb_id, x, 0,
580				     &c[i].id, 1, c[i].mode);
581		x += c[i].mode->hdisplay;
582
583		if (ret) {
584			fprintf(stderr, "failed to set mode: %s\n", strerror(errno));
585			return;
586		}
587	}
588
589	if (!page_flip)
590		return;
591
592	if (create_grey_buffer(bufmgr, width, height, &stride, &other_bo))
593		return;
594
595	ret = drmModeAddFB(fd, width, height, 32, 32, stride, other_bo->handle,
596			   &other_fb_id);
597	if (ret) {
598		fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
599		return;
600	}
601
602	for (i = 0; i < count; i++) {
603		if (c[i].mode == NULL)
604			continue;
605
606		drmModePageFlip(fd, c[i].crtc, other_fb_id,
607				DRM_MODE_PAGE_FLIP_EVENT, &c[i]);
608		gettimeofday(&c[i].start, NULL);
609		c[i].swap_count = 0;
610		c[i].fb_id[0] = fb_id;
611		c[i].fb_id[1] = other_fb_id;
612		c[i].current_fb_id = fb_id;
613	}
614
615	memset(&evctx, 0, sizeof evctx);
616	evctx.version = DRM_EVENT_CONTEXT_VERSION;
617	evctx.vblank_handler = NULL;
618	evctx.page_flip_handler = page_flip_handler;
619
620	while (1) {
621		struct pollfd pfd[2];
622
623		pfd[0].fd = 0;
624		pfd[0].events = POLLIN;
625		pfd[1].fd = fd;
626		pfd[1].events = POLLIN;
627
628		if (poll(pfd, 2, -1) < 0) {
629			fprintf(stderr, "poll error\n");
630			break;
631		}
632
633		if (pfd[0].revents)
634			break;
635
636		drmHandleEvent(fd, &evctx);
637	}
638}
639
640extern char *optarg;
641extern int optind, opterr, optopt;
642static char optstr[] = "ecpmfs:v";
643
644void usage(char *name)
645{
646	fprintf(stderr, "usage: %s [-ecpmf]\n", name);
647	fprintf(stderr, "\t-e\tlist encoders\n");
648	fprintf(stderr, "\t-c\tlist connectors\n");
649	fprintf(stderr, "\t-p\tlist CRTCs (pipes)\n");
650	fprintf(stderr, "\t-m\tlist modes\n");
651	fprintf(stderr, "\t-f\tlist framebuffers\n");
652	fprintf(stderr, "\t-v\ttest vsynced page flipping\n");
653	fprintf(stderr, "\t-s <connector_id>:<mode>\tset a mode\n");
654	fprintf(stderr, "\t-s <connector_id>@<crtc_id>:<mode>\tset a mode\n");
655	fprintf(stderr, "\n\tDefault is to dump all info.\n");
656	exit(0);
657}
658
659#define dump_resource(res) if (res) dump_##res()
660
661static int page_flipping_supported(int fd)
662{
663	int ret, value;
664	struct drm_i915_getparam gp;
665
666	gp.param = I915_PARAM_HAS_PAGEFLIPPING;
667	gp.value = &value;
668
669	ret = drmCommandWriteRead(fd, DRM_I915_GETPARAM, &gp, sizeof(gp));
670	if (ret) {
671		fprintf(stderr, "drm_i915_getparam: %m\n");
672		return 0;
673	}
674
675	return *gp.value;
676}
677
678int main(int argc, char **argv)
679{
680	int c;
681	int encoders = 0, connectors = 0, crtcs = 0, framebuffers = 0;
682	int test_vsync = 0;
683	char *modules[] = { "i915", "radeon", "nouveau" };
684	char *modeset = NULL;
685	int i, count = 0;
686	struct connector con_args[2];
687
688	opterr = 0;
689	while ((c = getopt(argc, argv, optstr)) != -1) {
690		switch (c) {
691		case 'e':
692			encoders = 1;
693			break;
694		case 'c':
695			connectors = 1;
696			break;
697		case 'p':
698			crtcs = 1;
699			break;
700		case 'm':
701			modes = 1;
702			break;
703		case 'f':
704			framebuffers = 1;
705			break;
706		case 'v':
707			test_vsync = 1;
708			break;
709		case 's':
710			modeset = strdup(optarg);
711			con_args[count].crtc = -1;
712			if (sscanf(optarg, "%d:%64s",
713				   &con_args[count].id,
714				   con_args[count].mode_str) != 2 &&
715			    sscanf(optarg, "%d@%d:%64s",
716				   &con_args[count].id,
717				   &con_args[count].crtc,
718				   con_args[count].mode_str) != 3)
719				usage(argv[0]);
720			count++;
721			break;
722		default:
723			usage(argv[0]);
724			break;
725		}
726	}
727
728	if (argc == 1)
729		encoders = connectors = crtcs = modes = framebuffers = 1;
730
731	for (i = 0; i < ARRAY_SIZE(modules); i++) {
732		printf("trying to load module %s...", modules[i]);
733		fd = drmOpen(modules[i], NULL);
734		if (fd < 0) {
735			printf("failed.\n");
736		} else {
737			printf("success.\n");
738			break;
739		}
740	}
741
742	if (test_vsync && !page_flipping_supported(fd)) {
743		fprintf(stderr, "page flipping not supported by drm.\n");
744		return -1;
745	}
746
747	if (i == ARRAY_SIZE(modules)) {
748		fprintf(stderr, "failed to load any modules, aborting.\n");
749		return -1;
750	}
751
752	resources = drmModeGetResources(fd);
753	if (!resources) {
754		fprintf(stderr, "drmModeGetResources failed: %s\n",
755			strerror(errno));
756		drmClose(fd);
757		return 1;
758	}
759
760	dump_resource(encoders);
761	dump_resource(connectors);
762	dump_resource(crtcs);
763	dump_resource(framebuffers);
764
765	if (count > 0) {
766		set_mode(con_args, count, test_vsync);
767		getchar();
768	}
769
770	drmModeFreeResources(resources);
771
772	return 0;
773}
774