modetest.c revision 2c26a106fcfb692badef4c42faaed46508a3d1d3
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 <inttypes.h>
47#include <unistd.h>
48#include <string.h>
49#include <errno.h>
50#include <sys/poll.h>
51#include <sys/time.h>
52
53#include "xf86drm.h"
54#include "xf86drmMode.h"
55#include "drm_fourcc.h"
56#include "libkms.h"
57
58#include "buffers.h"
59
60drmModeRes *resources;
61int fd, modes;
62
63#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
64
65struct type_name {
66	int type;
67	char *name;
68};
69
70#define type_name_fn(res) \
71char * res##_str(int type) {			\
72	unsigned int i;					\
73	for (i = 0; i < ARRAY_SIZE(res##_names); i++) { \
74		if (res##_names[i].type == type)	\
75			return res##_names[i].name;	\
76	}						\
77	return "(invalid)";				\
78}
79
80struct type_name encoder_type_names[] = {
81	{ DRM_MODE_ENCODER_NONE, "none" },
82	{ DRM_MODE_ENCODER_DAC, "DAC" },
83	{ DRM_MODE_ENCODER_TMDS, "TMDS" },
84	{ DRM_MODE_ENCODER_LVDS, "LVDS" },
85	{ DRM_MODE_ENCODER_TVDAC, "TVDAC" },
86};
87
88type_name_fn(encoder_type)
89
90struct type_name connector_status_names[] = {
91	{ DRM_MODE_CONNECTED, "connected" },
92	{ DRM_MODE_DISCONNECTED, "disconnected" },
93	{ DRM_MODE_UNKNOWNCONNECTION, "unknown" },
94};
95
96type_name_fn(connector_status)
97
98struct type_name connector_type_names[] = {
99	{ DRM_MODE_CONNECTOR_Unknown, "unknown" },
100	{ DRM_MODE_CONNECTOR_VGA, "VGA" },
101	{ DRM_MODE_CONNECTOR_DVII, "DVI-I" },
102	{ DRM_MODE_CONNECTOR_DVID, "DVI-D" },
103	{ DRM_MODE_CONNECTOR_DVIA, "DVI-A" },
104	{ DRM_MODE_CONNECTOR_Composite, "composite" },
105	{ DRM_MODE_CONNECTOR_SVIDEO, "s-video" },
106	{ DRM_MODE_CONNECTOR_LVDS, "LVDS" },
107	{ DRM_MODE_CONNECTOR_Component, "component" },
108	{ DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN" },
109	{ DRM_MODE_CONNECTOR_DisplayPort, "DP" },
110	{ DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" },
111	{ DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" },
112	{ DRM_MODE_CONNECTOR_TV, "TV" },
113	{ DRM_MODE_CONNECTOR_eDP, "eDP" },
114};
115
116type_name_fn(connector_type)
117
118#define bit_name_fn(res)					\
119char * res##_str(int type) {					\
120	int i;							\
121	const char *sep = "";					\
122	for (i = 0; i < ARRAY_SIZE(res##_names); i++) {		\
123		if (type & (1 << i)) {				\
124			printf("%s%s", sep, res##_names[i]);	\
125			sep = ", ";				\
126		}						\
127	}							\
128	return NULL;						\
129}
130
131static const char *mode_type_names[] = {
132	"builtin",
133	"clock_c",
134	"crtc_c",
135	"preferred",
136	"default",
137	"userdef",
138	"driver",
139};
140
141bit_name_fn(mode_type)
142
143static const char *mode_flag_names[] = {
144	"phsync",
145	"nhsync",
146	"pvsync",
147	"nvsync",
148	"interlace",
149	"dblscan",
150	"csync",
151	"pcsync",
152	"ncsync",
153	"hskew",
154	"bcast",
155	"pixmux",
156	"dblclk",
157	"clkdiv2"
158};
159
160bit_name_fn(mode_flag)
161
162void dump_encoders(void)
163{
164	drmModeEncoder *encoder;
165	int i;
166
167	printf("Encoders:\n");
168	printf("id\tcrtc\ttype\tpossible crtcs\tpossible clones\t\n");
169	for (i = 0; i < resources->count_encoders; i++) {
170		encoder = drmModeGetEncoder(fd, resources->encoders[i]);
171
172		if (!encoder) {
173			fprintf(stderr, "could not get encoder %i: %s\n",
174				resources->encoders[i], strerror(errno));
175			continue;
176		}
177		printf("%d\t%d\t%s\t0x%08x\t0x%08x\n",
178		       encoder->encoder_id,
179		       encoder->crtc_id,
180		       encoder_type_str(encoder->encoder_type),
181		       encoder->possible_crtcs,
182		       encoder->possible_clones);
183		drmModeFreeEncoder(encoder);
184	}
185	printf("\n");
186}
187
188void dump_mode(drmModeModeInfo *mode)
189{
190	printf("  %s %d %d %d %d %d %d %d %d %d",
191	       mode->name,
192	       mode->vrefresh,
193	       mode->hdisplay,
194	       mode->hsync_start,
195	       mode->hsync_end,
196	       mode->htotal,
197	       mode->vdisplay,
198	       mode->vsync_start,
199	       mode->vsync_end,
200	       mode->vtotal);
201
202	printf(" flags: ");
203	mode_flag_str(mode->flags);
204	printf("; type: ");
205	mode_type_str(mode->type);
206	printf("\n");
207}
208
209static void
210dump_blob(uint32_t blob_id)
211{
212	uint32_t i;
213	unsigned char *blob_data;
214	drmModePropertyBlobPtr blob;
215
216	blob = drmModeGetPropertyBlob(fd, blob_id);
217	if (!blob)
218		return;
219
220	blob_data = blob->data;
221
222	for (i = 0; i < blob->length; i++) {
223		if (i % 16 == 0)
224			printf("\n\t\t\t");
225		printf("%.2hhx", blob_data[i]);
226	}
227	printf("\n");
228
229	drmModeFreePropertyBlob(blob);
230}
231
232static void
233dump_prop(uint32_t prop_id, uint64_t value)
234{
235	int i;
236	drmModePropertyPtr prop;
237
238	prop = drmModeGetProperty(fd, prop_id);
239
240	printf("\t%d", prop_id);
241	if (!prop) {
242		printf("\n");
243		return;
244	}
245
246	printf(" %s:\n", prop->name);
247
248	printf("\t\tflags:");
249	if (prop->flags & DRM_MODE_PROP_PENDING)
250		printf(" pending");
251	if (prop->flags & DRM_MODE_PROP_RANGE)
252		printf(" range");
253	if (prop->flags & DRM_MODE_PROP_IMMUTABLE)
254		printf(" immutable");
255	if (prop->flags & DRM_MODE_PROP_ENUM)
256		printf(" enum");
257	if (prop->flags & DRM_MODE_PROP_BITMASK)
258		printf(" bitmask");
259	if (prop->flags & DRM_MODE_PROP_BLOB)
260		printf(" blob");
261	printf("\n");
262
263	if (prop->flags & DRM_MODE_PROP_RANGE) {
264		printf("\t\tvalues:");
265		for (i = 0; i < prop->count_values; i++)
266			printf(" %"PRIu64, prop->values[i]);
267		printf("\n");
268	}
269
270	if (prop->flags & DRM_MODE_PROP_ENUM) {
271		printf("\t\tenums:");
272		for (i = 0; i < prop->count_enums; i++)
273			printf(" %s=%llu", prop->enums[i].name,
274			       prop->enums[i].value);
275		printf("\n");
276	} else if (prop->flags & DRM_MODE_PROP_BITMASK) {
277		printf("\t\tvalues:");
278		for (i = 0; i < prop->count_enums; i++)
279			printf(" %s=0x%llx", prop->enums[i].name,
280			       (1LL << prop->enums[i].value));
281		printf("\n");
282	} else {
283		assert(prop->count_enums == 0);
284	}
285
286	if (prop->flags & DRM_MODE_PROP_BLOB) {
287		printf("\t\tblobs:\n");
288		for (i = 0; i < prop->count_blobs; i++)
289			dump_blob(prop->blob_ids[i]);
290		printf("\n");
291	} else {
292		assert(prop->count_blobs == 0);
293	}
294
295	printf("\t\tvalue:");
296	if (prop->flags & DRM_MODE_PROP_BLOB)
297		dump_blob(value);
298	else
299		printf(" %"PRIu64"\n", value);
300
301	drmModeFreeProperty(prop);
302}
303
304void dump_connectors(void)
305{
306	drmModeConnector *connector;
307	int i, j;
308
309	printf("Connectors:\n");
310	printf("id\tencoder\tstatus\t\ttype\tsize (mm)\tmodes\tencoders\n");
311	for (i = 0; i < resources->count_connectors; i++) {
312		connector = drmModeGetConnector(fd, resources->connectors[i]);
313
314		if (!connector) {
315			fprintf(stderr, "could not get connector %i: %s\n",
316				resources->connectors[i], strerror(errno));
317			continue;
318		}
319
320		printf("%d\t%d\t%s\t%s\t%dx%d\t\t%d\t",
321		       connector->connector_id,
322		       connector->encoder_id,
323		       connector_status_str(connector->connection),
324		       connector_type_str(connector->connector_type),
325		       connector->mmWidth, connector->mmHeight,
326		       connector->count_modes);
327
328		for (j = 0; j < connector->count_encoders; j++)
329			printf("%s%d", j > 0 ? ", " : "", connector->encoders[j]);
330		printf("\n");
331
332		if (connector->count_modes) {
333			printf("  modes:\n");
334			printf("\tname refresh (Hz) hdisp hss hse htot vdisp "
335			       "vss vse vtot)\n");
336			for (j = 0; j < connector->count_modes; j++)
337				dump_mode(&connector->modes[j]);
338
339			printf("  props:\n");
340			for (j = 0; j < connector->count_props; j++)
341				dump_prop(connector->props[j],
342					  connector->prop_values[j]);
343		}
344
345		drmModeFreeConnector(connector);
346	}
347	printf("\n");
348}
349
350void dump_crtcs(void)
351{
352	drmModeCrtc *crtc;
353	drmModeObjectPropertiesPtr props;
354	int i;
355	uint32_t j;
356
357	printf("CRTCs:\n");
358	printf("id\tfb\tpos\tsize\n");
359	for (i = 0; i < resources->count_crtcs; i++) {
360		crtc = drmModeGetCrtc(fd, resources->crtcs[i]);
361
362		if (!crtc) {
363			fprintf(stderr, "could not get crtc %i: %s\n",
364				resources->crtcs[i], strerror(errno));
365			continue;
366		}
367		printf("%d\t%d\t(%d,%d)\t(%dx%d)\n",
368		       crtc->crtc_id,
369		       crtc->buffer_id,
370		       crtc->x, crtc->y,
371		       crtc->width, crtc->height);
372		dump_mode(&crtc->mode);
373
374		printf("  props:\n");
375		props = drmModeObjectGetProperties(fd, crtc->crtc_id,
376						   DRM_MODE_OBJECT_CRTC);
377		if (props) {
378			for (j = 0; j < props->count_props; j++)
379				dump_prop(props->props[j],
380					  props->prop_values[j]);
381			drmModeFreeObjectProperties(props);
382		} else {
383			printf("\tcould not get crtc properties: %s\n",
384			       strerror(errno));
385		}
386
387		drmModeFreeCrtc(crtc);
388	}
389	printf("\n");
390}
391
392void dump_framebuffers(void)
393{
394	drmModeFB *fb;
395	int i;
396
397	printf("Frame buffers:\n");
398	printf("id\tsize\tpitch\n");
399	for (i = 0; i < resources->count_fbs; i++) {
400		fb = drmModeGetFB(fd, resources->fbs[i]);
401
402		if (!fb) {
403			fprintf(stderr, "could not get fb %i: %s\n",
404				resources->fbs[i], strerror(errno));
405			continue;
406		}
407		printf("%u\t(%ux%u)\t%u\n",
408		       fb->fb_id,
409		       fb->width, fb->height,
410		       fb->pitch);
411
412		drmModeFreeFB(fb);
413	}
414	printf("\n");
415}
416
417static void dump_planes(void)
418{
419	drmModeObjectPropertiesPtr props;
420	drmModePlaneRes *plane_resources;
421	drmModePlane *ovr;
422	unsigned int i, j;
423
424	plane_resources = drmModeGetPlaneResources(fd);
425	if (!plane_resources) {
426		fprintf(stderr, "drmModeGetPlaneResources failed: %s\n",
427			strerror(errno));
428		return;
429	}
430
431	printf("Planes:\n");
432	printf("id\tcrtc\tfb\tCRTC x,y\tx,y\tgamma size\tpossible crtcs\n");
433	for (i = 0; i < plane_resources->count_planes; i++) {
434		ovr = drmModeGetPlane(fd, plane_resources->planes[i]);
435		if (!ovr) {
436			fprintf(stderr, "drmModeGetPlane failed: %s\n",
437				strerror(errno));
438			continue;
439		}
440
441		printf("%d\t%d\t%d\t%d,%d\t\t%d,%d\t%-8d\t0x%08x\n",
442		       ovr->plane_id, ovr->crtc_id, ovr->fb_id,
443		       ovr->crtc_x, ovr->crtc_y, ovr->x, ovr->y,
444		       ovr->gamma_size, ovr->possible_crtcs);
445
446		if (!ovr->count_formats)
447			continue;
448
449		printf("  formats:");
450		for (j = 0; j < ovr->count_formats; j++)
451			printf(" %4.4s", (char *)&ovr->formats[j]);
452		printf("\n");
453
454		printf("  props:\n");
455		props = drmModeObjectGetProperties(fd, ovr->plane_id,
456						   DRM_MODE_OBJECT_PLANE);
457		if (props) {
458			for (j = 0; j < props->count_props; j++)
459				dump_prop(props->props[j],
460					  props->prop_values[j]);
461			drmModeFreeObjectProperties(props);
462		} else {
463			printf("\tcould not get plane properties: %s\n",
464			       strerror(errno));
465		}
466
467		drmModeFreePlane(ovr);
468	}
469	printf("\n");
470
471	drmModeFreePlaneResources(plane_resources);
472	return;
473}
474
475/* -----------------------------------------------------------------------------
476 * Connectors and planes
477 */
478
479/*
480 * Mode setting with the kernel interfaces is a bit of a chore.
481 * First you have to find the connector in question and make sure the
482 * requested mode is available.
483 * Then you need to find the encoder attached to that connector so you
484 * can bind it with a free crtc.
485 */
486struct connector {
487	uint32_t id;
488	char mode_str[64];
489	char format_str[5];
490	unsigned int fourcc;
491	drmModeModeInfo *mode;
492	drmModeEncoder *encoder;
493	int crtc;
494	int pipe;
495	unsigned int fb_id[2], current_fb_id;
496	struct timeval start;
497
498	int swap_count;
499};
500
501struct plane {
502	uint32_t con_id;  /* the id of connector to bind to */
503	uint32_t w, h;
504	unsigned int fb_id;
505	char format_str[5]; /* need to leave room for terminating \0 */
506	unsigned int fourcc;
507};
508
509static void
510connector_find_mode(struct connector *c)
511{
512	drmModeConnector *connector;
513	int i, j;
514
515	/* First, find the connector & mode */
516	c->mode = NULL;
517	for (i = 0; i < resources->count_connectors; i++) {
518		connector = drmModeGetConnector(fd, resources->connectors[i]);
519
520		if (!connector) {
521			fprintf(stderr, "could not get connector %i: %s\n",
522				resources->connectors[i], strerror(errno));
523			drmModeFreeConnector(connector);
524			continue;
525		}
526
527		if (!connector->count_modes) {
528			drmModeFreeConnector(connector);
529			continue;
530		}
531
532		if (connector->connector_id != c->id) {
533			drmModeFreeConnector(connector);
534			continue;
535		}
536
537		for (j = 0; j < connector->count_modes; j++) {
538			c->mode = &connector->modes[j];
539			if (!strcmp(c->mode->name, c->mode_str))
540				break;
541		}
542
543		/* Found it, break out */
544		if (c->mode)
545			break;
546
547		drmModeFreeConnector(connector);
548	}
549
550	if (!c->mode) {
551		fprintf(stderr, "failed to find mode \"%s\"\n", c->mode_str);
552		return;
553	}
554
555	/* Now get the encoder */
556	for (i = 0; i < resources->count_encoders; i++) {
557		c->encoder = drmModeGetEncoder(fd, resources->encoders[i]);
558
559		if (!c->encoder) {
560			fprintf(stderr, "could not get encoder %i: %s\n",
561				resources->encoders[i], strerror(errno));
562			drmModeFreeEncoder(c->encoder);
563			continue;
564		}
565
566		if (c->encoder->encoder_id  == connector->encoder_id)
567			break;
568
569		drmModeFreeEncoder(c->encoder);
570	}
571
572	if (c->crtc == -1)
573		c->crtc = c->encoder->crtc_id;
574
575	/* and figure out which crtc index it is: */
576	for (i = 0; i < resources->count_crtcs; i++) {
577		if (c->crtc == resources->crtcs[i]) {
578			c->pipe = i;
579			break;
580		}
581	}
582
583}
584
585/* -------------------------------------------------------------------------- */
586
587void
588page_flip_handler(int fd, unsigned int frame,
589		  unsigned int sec, unsigned int usec, void *data)
590{
591	struct connector *c;
592	unsigned int new_fb_id;
593	struct timeval end;
594	double t;
595
596	c = data;
597	if (c->current_fb_id == c->fb_id[0])
598		new_fb_id = c->fb_id[1];
599	else
600		new_fb_id = c->fb_id[0];
601
602	drmModePageFlip(fd, c->crtc, new_fb_id,
603			DRM_MODE_PAGE_FLIP_EVENT, c);
604	c->current_fb_id = new_fb_id;
605	c->swap_count++;
606	if (c->swap_count == 60) {
607		gettimeofday(&end, NULL);
608		t = end.tv_sec + end.tv_usec * 1e-6 -
609			(c->start.tv_sec + c->start.tv_usec * 1e-6);
610		fprintf(stderr, "freq: %.02fHz\n", c->swap_count / t);
611		c->swap_count = 0;
612		c->start = end;
613	}
614}
615
616static int
617set_plane(struct kms_driver *kms, struct connector *c, struct plane *p)
618{
619	drmModePlaneRes *plane_resources;
620	drmModePlane *ovr;
621	uint32_t handles[4], pitches[4], offsets[4] = {0}; /* we only use [0] */
622	uint32_t plane_id = 0;
623	struct kms_bo *plane_bo;
624	uint32_t plane_flags = 0;
625	int ret, crtc_x, crtc_y, crtc_w, crtc_h;
626	unsigned int i;
627
628	/* find an unused plane which can be connected to our crtc */
629	plane_resources = drmModeGetPlaneResources(fd);
630	if (!plane_resources) {
631		fprintf(stderr, "drmModeGetPlaneResources failed: %s\n",
632			strerror(errno));
633		return -1;
634	}
635
636	for (i = 0; i < plane_resources->count_planes && !plane_id; i++) {
637		ovr = drmModeGetPlane(fd, plane_resources->planes[i]);
638		if (!ovr) {
639			fprintf(stderr, "drmModeGetPlane failed: %s\n",
640				strerror(errno));
641			return -1;
642		}
643
644		if ((ovr->possible_crtcs & (1 << c->pipe)) && !ovr->crtc_id)
645			plane_id = ovr->plane_id;
646
647		drmModeFreePlane(ovr);
648	}
649
650	fprintf(stderr, "testing %dx%d@%s overlay plane\n",
651			p->w, p->h, p->format_str);
652
653	if (!plane_id) {
654		fprintf(stderr, "failed to find plane!\n");
655		return -1;
656	}
657
658	plane_bo = create_test_buffer(kms, p->fourcc, p->w, p->h, handles,
659				      pitches, offsets, PATTERN_TILES);
660	if (plane_bo == NULL)
661		return -1;
662
663	/* just use single plane format for now.. */
664	if (drmModeAddFB2(fd, p->w, p->h, p->fourcc,
665			handles, pitches, offsets, &p->fb_id, plane_flags)) {
666		fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
667		return -1;
668	}
669
670	/* ok, boring.. but for now put in middle of screen: */
671	crtc_x = c->mode->hdisplay / 3;
672	crtc_y = c->mode->vdisplay / 3;
673	crtc_w = crtc_x;
674	crtc_h = crtc_y;
675
676	/* note src coords (last 4 args) are in Q16 format */
677	if (drmModeSetPlane(fd, plane_id, c->crtc, p->fb_id,
678			    plane_flags, crtc_x, crtc_y, crtc_w, crtc_h,
679			    0, 0, p->w << 16, p->h << 16)) {
680		fprintf(stderr, "failed to enable plane: %s\n",
681			strerror(errno));
682		return -1;
683	}
684
685	return 0;
686}
687
688static void
689set_mode(struct connector *c, int count, struct plane *p, int plane_count,
690		int page_flip)
691{
692	struct kms_driver *kms;
693	struct kms_bo *bo, *other_bo;
694	unsigned int fb_id, other_fb_id;
695	int i, j, ret, width, height, x;
696	uint32_t handles[4], pitches[4], offsets[4] = {0}; /* we only use [0] */
697	drmEventContext evctx;
698
699	width = 0;
700	height = 0;
701	for (i = 0; i < count; i++) {
702		connector_find_mode(&c[i]);
703		if (c[i].mode == NULL)
704			continue;
705		width += c[i].mode->hdisplay;
706		if (height < c[i].mode->vdisplay)
707			height = c[i].mode->vdisplay;
708	}
709
710	ret = kms_create(fd, &kms);
711	if (ret) {
712		fprintf(stderr, "failed to create kms driver: %s\n",
713			strerror(-ret));
714		return;
715	}
716
717	bo = create_test_buffer(kms, c->fourcc, width, height, handles,
718				pitches, offsets, PATTERN_SMPTE);
719	if (bo == NULL)
720		return;
721
722	ret = drmModeAddFB2(fd, width, height, c->fourcc,
723			    handles, pitches, offsets, &fb_id, 0);
724	if (ret) {
725		fprintf(stderr, "failed to add fb (%ux%u): %s\n",
726			width, height, strerror(errno));
727		return;
728	}
729
730	x = 0;
731	for (i = 0; i < count; i++) {
732		if (c[i].mode == NULL)
733			continue;
734
735		printf("setting mode %s@%s on connector %d, crtc %d\n",
736		       c[i].mode_str, c[i].format_str, c[i].id, c[i].crtc);
737
738		ret = drmModeSetCrtc(fd, c[i].crtc, fb_id, x, 0,
739				     &c[i].id, 1, c[i].mode);
740
741		/* XXX: Actually check if this is needed */
742		drmModeDirtyFB(fd, fb_id, NULL, 0);
743
744		x += c[i].mode->hdisplay;
745
746		if (ret) {
747			fprintf(stderr, "failed to set mode: %s\n", strerror(errno));
748			return;
749		}
750
751		/* if we have a plane/overlay to show, set that up now: */
752		for (j = 0; j < plane_count; j++)
753			if (p[j].con_id == c[i].id)
754				if (set_plane(kms, &c[i], &p[j]))
755					return;
756	}
757
758	if (!page_flip)
759		return;
760
761	other_bo = create_test_buffer(kms, c->fourcc, width, height, handles,
762				      pitches, offsets, PATTERN_PLAIN);
763	if (other_bo == NULL)
764		return;
765
766	ret = drmModeAddFB2(fd, width, height, c->fourcc, handles, pitches, offsets,
767			    &other_fb_id, 0);
768	if (ret) {
769		fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
770		return;
771	}
772
773	for (i = 0; i < count; i++) {
774		if (c[i].mode == NULL)
775			continue;
776
777		ret = drmModePageFlip(fd, c[i].crtc, other_fb_id,
778				      DRM_MODE_PAGE_FLIP_EVENT, &c[i]);
779		if (ret) {
780			fprintf(stderr, "failed to page flip: %s\n", strerror(errno));
781			return;
782		}
783		gettimeofday(&c[i].start, NULL);
784		c[i].swap_count = 0;
785		c[i].fb_id[0] = fb_id;
786		c[i].fb_id[1] = other_fb_id;
787		c[i].current_fb_id = other_fb_id;
788	}
789
790	memset(&evctx, 0, sizeof evctx);
791	evctx.version = DRM_EVENT_CONTEXT_VERSION;
792	evctx.vblank_handler = NULL;
793	evctx.page_flip_handler = page_flip_handler;
794
795	while (1) {
796#if 0
797		struct pollfd pfd[2];
798
799		pfd[0].fd = 0;
800		pfd[0].events = POLLIN;
801		pfd[1].fd = fd;
802		pfd[1].events = POLLIN;
803
804		if (poll(pfd, 2, -1) < 0) {
805			fprintf(stderr, "poll error\n");
806			break;
807		}
808
809		if (pfd[0].revents)
810			break;
811#else
812		struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 };
813		fd_set fds;
814		int ret;
815
816		FD_ZERO(&fds);
817		FD_SET(0, &fds);
818		FD_SET(fd, &fds);
819		ret = select(fd + 1, &fds, NULL, NULL, &timeout);
820
821		if (ret <= 0) {
822			fprintf(stderr, "select timed out or error (ret %d)\n",
823				ret);
824			continue;
825		} else if (FD_ISSET(0, &fds)) {
826			break;
827		}
828#endif
829
830		drmHandleEvent(fd, &evctx);
831	}
832
833	kms_bo_destroy(&bo);
834	kms_bo_destroy(&other_bo);
835	kms_destroy(&kms);
836}
837
838extern char *optarg;
839extern int optind, opterr, optopt;
840static char optstr[] = "ecpmfs:P:v";
841
842#define min(a, b)	((a) < (b) ? (a) : (b))
843
844static int parse_connector(struct connector *c, const char *arg)
845{
846	unsigned int len;
847	const char *p;
848	char *endp;
849
850	c->crtc = -1;
851	strcpy(c->format_str, "XR24");
852
853	c->id = strtoul(arg, &endp, 10);
854	if (*endp == '@') {
855		arg = endp + 1;
856		c->crtc = strtoul(arg, &endp, 10);
857	}
858	if (*endp != ':')
859		return -1;
860
861	arg = endp + 1;
862
863	p = strchrnul(arg, '@');
864	len = min(sizeof c->mode_str - 1, p - arg);
865	strncpy(c->mode_str, arg, len);
866	c->mode_str[len] = '\0';
867
868	if (*p == '@') {
869		strncpy(c->format_str, p + 1, 4);
870		c->format_str[4] = '\0';
871	}
872
873	c->fourcc = format_fourcc(c->format_str);
874	if (c->fourcc == 0)  {
875		fprintf(stderr, "unknown format %s\n", c->format_str);
876		return -1;
877	}
878
879	return 0;
880}
881
882static int parse_plane(struct plane *p, const char *arg)
883{
884	strcpy(p->format_str, "XR24");
885
886	if (sscanf(arg, "%d:%dx%d@%4s", &p->con_id, &p->w, &p->h, &p->format_str) != 4 &&
887	    sscanf(arg, "%d:%dx%d", &p->con_id, &p->w, &p->h) != 3)
888		return -1;
889
890	p->fourcc = format_fourcc(p->format_str);
891	if (p->fourcc == 0) {
892		fprintf(stderr, "unknown format %s\n", p->format_str);
893		return -1;
894	}
895
896	return 0;
897}
898
899void usage(char *name)
900{
901	fprintf(stderr, "usage: %s [-ecpmf]\n", name);
902	fprintf(stderr, "\t-e\tlist encoders\n");
903	fprintf(stderr, "\t-c\tlist connectors\n");
904	fprintf(stderr, "\t-p\tlist CRTCs and planes (pipes)\n");
905	fprintf(stderr, "\t-m\tlist modes\n");
906	fprintf(stderr, "\t-f\tlist framebuffers\n");
907	fprintf(stderr, "\t-v\ttest vsynced page flipping\n");
908	fprintf(stderr, "\t-s <connector_id>[@<crtc_id>]:<mode>[@<format>]\tset a mode\n");
909	fprintf(stderr, "\t-P <connector_id>:<w>x<h>[@<format>]\tset a plane\n");
910	fprintf(stderr, "\n\tDefault is to dump all info.\n");
911	exit(0);
912}
913
914#define dump_resource(res) if (res) dump_##res()
915
916static int page_flipping_supported(void)
917{
918	/*FIXME: generic ioctl needed? */
919	return 1;
920#if 0
921	int ret, value;
922	struct drm_i915_getparam gp;
923
924	gp.param = I915_PARAM_HAS_PAGEFLIPPING;
925	gp.value = &value;
926
927	ret = drmCommandWriteRead(fd, DRM_I915_GETPARAM, &gp, sizeof(gp));
928	if (ret) {
929		fprintf(stderr, "drm_i915_getparam: %m\n");
930		return 0;
931	}
932
933	return *gp.value;
934#endif
935}
936
937int main(int argc, char **argv)
938{
939	int c;
940	int encoders = 0, connectors = 0, crtcs = 0, planes = 0, framebuffers = 0;
941	int test_vsync = 0;
942	char *modules[] = { "i915", "radeon", "nouveau", "vmwgfx", "omapdrm", "exynos" };
943	unsigned int i;
944	int count = 0, plane_count = 0;
945	struct connector con_args[2];
946	struct plane plane_args[2] = {0};
947
948	opterr = 0;
949	while ((c = getopt(argc, argv, optstr)) != -1) {
950		switch (c) {
951		case 'e':
952			encoders = 1;
953			break;
954		case 'c':
955			connectors = 1;
956			break;
957		case 'p':
958			crtcs = 1;
959			planes = 1;
960			break;
961		case 'm':
962			modes = 1;
963			break;
964		case 'f':
965			framebuffers = 1;
966			break;
967		case 'v':
968			test_vsync = 1;
969			break;
970		case 's':
971			if (parse_connector(&con_args[count], optarg) < 0)
972				usage(argv[0]);
973			count++;
974			break;
975		case 'P':
976			if (parse_plane(&plane_args[plane_count], optarg) < 0)
977				usage(argv[0]);
978			plane_count++;
979			break;
980		default:
981			usage(argv[0]);
982			break;
983		}
984	}
985
986	if (argc == 1)
987		encoders = connectors = crtcs = planes = modes = framebuffers = 1;
988
989	for (i = 0; i < ARRAY_SIZE(modules); i++) {
990		printf("trying to load module %s...", modules[i]);
991		fd = drmOpen(modules[i], NULL);
992		if (fd < 0) {
993			printf("failed.\n");
994		} else {
995			printf("success.\n");
996			break;
997		}
998	}
999
1000	if (test_vsync && !page_flipping_supported()) {
1001		fprintf(stderr, "page flipping not supported by drm.\n");
1002		return -1;
1003	}
1004
1005	if (i == ARRAY_SIZE(modules)) {
1006		fprintf(stderr, "failed to load any modules, aborting.\n");
1007		return -1;
1008	}
1009
1010	resources = drmModeGetResources(fd);
1011	if (!resources) {
1012		fprintf(stderr, "drmModeGetResources failed: %s\n",
1013			strerror(errno));
1014		drmClose(fd);
1015		return 1;
1016	}
1017
1018	dump_resource(encoders);
1019	dump_resource(connectors);
1020	dump_resource(crtcs);
1021	dump_resource(planes);
1022	dump_resource(framebuffers);
1023
1024	if (count > 0) {
1025		set_mode(con_args, count, plane_args, plane_count, test_vsync);
1026		getchar();
1027	}
1028
1029	drmModeFreeResources(resources);
1030
1031	return 0;
1032}
1033