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