modetest.c revision 7d13a5d0
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 <ctype.h>
44#include <stdbool.h>
45#include <stdio.h>
46#include <stdlib.h>
47#include <stdint.h>
48#include <inttypes.h>
49#include <unistd.h>
50#include <string.h>
51#include <errno.h>
52#include <sys/poll.h>
53#include <sys/time.h>
54
55#include "xf86drm.h"
56#include "xf86drmMode.h"
57#include "drm_fourcc.h"
58#include "libkms.h"
59
60#include "buffers.h"
61
62struct crtc {
63	drmModeCrtc *crtc;
64	drmModeObjectProperties *props;
65	drmModePropertyRes **props_info;
66	drmModeModeInfo *mode;
67};
68
69struct encoder {
70	drmModeEncoder *encoder;
71};
72
73struct connector {
74	drmModeConnector *connector;
75	drmModeObjectProperties *props;
76	drmModePropertyRes **props_info;
77};
78
79struct fb {
80	drmModeFB *fb;
81};
82
83struct plane {
84	drmModePlane *plane;
85	drmModeObjectProperties *props;
86	drmModePropertyRes **props_info;
87};
88
89struct resources {
90	drmModeRes *res;
91	drmModePlaneRes *plane_res;
92
93	struct crtc *crtcs;
94	struct encoder *encoders;
95	struct connector *connectors;
96	struct fb *fbs;
97	struct plane *planes;
98};
99
100struct device {
101	int fd;
102
103	struct resources *resources;
104	struct kms_driver *kms;
105
106	struct {
107		unsigned int width;
108		unsigned int height;
109
110		unsigned int fb_id;
111		struct kms_bo *bo;
112	} mode;
113};
114
115#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
116
117struct type_name {
118	int type;
119	const char *name;
120};
121
122#define type_name_fn(res) \
123const char * res##_str(int type) {			\
124	unsigned int i;					\
125	for (i = 0; i < ARRAY_SIZE(res##_names); i++) { \
126		if (res##_names[i].type == type)	\
127			return res##_names[i].name;	\
128	}						\
129	return "(invalid)";				\
130}
131
132struct type_name encoder_type_names[] = {
133	{ DRM_MODE_ENCODER_NONE, "none" },
134	{ DRM_MODE_ENCODER_DAC, "DAC" },
135	{ DRM_MODE_ENCODER_TMDS, "TMDS" },
136	{ DRM_MODE_ENCODER_LVDS, "LVDS" },
137	{ DRM_MODE_ENCODER_TVDAC, "TVDAC" },
138};
139
140static type_name_fn(encoder_type)
141
142struct type_name connector_status_names[] = {
143	{ DRM_MODE_CONNECTED, "connected" },
144	{ DRM_MODE_DISCONNECTED, "disconnected" },
145	{ DRM_MODE_UNKNOWNCONNECTION, "unknown" },
146};
147
148static type_name_fn(connector_status)
149
150struct type_name connector_type_names[] = {
151	{ DRM_MODE_CONNECTOR_Unknown, "unknown" },
152	{ DRM_MODE_CONNECTOR_VGA, "VGA" },
153	{ DRM_MODE_CONNECTOR_DVII, "DVI-I" },
154	{ DRM_MODE_CONNECTOR_DVID, "DVI-D" },
155	{ DRM_MODE_CONNECTOR_DVIA, "DVI-A" },
156	{ DRM_MODE_CONNECTOR_Composite, "composite" },
157	{ DRM_MODE_CONNECTOR_SVIDEO, "s-video" },
158	{ DRM_MODE_CONNECTOR_LVDS, "LVDS" },
159	{ DRM_MODE_CONNECTOR_Component, "component" },
160	{ DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN" },
161	{ DRM_MODE_CONNECTOR_DisplayPort, "DP" },
162	{ DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" },
163	{ DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" },
164	{ DRM_MODE_CONNECTOR_TV, "TV" },
165	{ DRM_MODE_CONNECTOR_eDP, "eDP" },
166};
167
168static type_name_fn(connector_type)
169
170#define bit_name_fn(res)					\
171const char * res##_str(int type) {				\
172	unsigned int i;						\
173	const char *sep = "";					\
174	for (i = 0; i < ARRAY_SIZE(res##_names); i++) {		\
175		if (type & (1 << i)) {				\
176			printf("%s%s", sep, res##_names[i]);	\
177			sep = ", ";				\
178		}						\
179	}							\
180	return NULL;						\
181}
182
183static const char *mode_type_names[] = {
184	"builtin",
185	"clock_c",
186	"crtc_c",
187	"preferred",
188	"default",
189	"userdef",
190	"driver",
191};
192
193static bit_name_fn(mode_type)
194
195static const char *mode_flag_names[] = {
196	"phsync",
197	"nhsync",
198	"pvsync",
199	"nvsync",
200	"interlace",
201	"dblscan",
202	"csync",
203	"pcsync",
204	"ncsync",
205	"hskew",
206	"bcast",
207	"pixmux",
208	"dblclk",
209	"clkdiv2"
210};
211
212static bit_name_fn(mode_flag)
213
214static void dump_encoders(struct device *dev)
215{
216	drmModeEncoder *encoder;
217	int i;
218
219	printf("Encoders:\n");
220	printf("id\tcrtc\ttype\tpossible crtcs\tpossible clones\t\n");
221	for (i = 0; i < dev->resources->res->count_encoders; i++) {
222		encoder = dev->resources->encoders[i].encoder;
223		if (!encoder)
224			continue;
225
226		printf("%d\t%d\t%s\t0x%08x\t0x%08x\n",
227		       encoder->encoder_id,
228		       encoder->crtc_id,
229		       encoder_type_str(encoder->encoder_type),
230		       encoder->possible_crtcs,
231		       encoder->possible_clones);
232	}
233	printf("\n");
234}
235
236static void dump_mode(drmModeModeInfo *mode)
237{
238	printf("  %s %d %d %d %d %d %d %d %d %d",
239	       mode->name,
240	       mode->vrefresh,
241	       mode->hdisplay,
242	       mode->hsync_start,
243	       mode->hsync_end,
244	       mode->htotal,
245	       mode->vdisplay,
246	       mode->vsync_start,
247	       mode->vsync_end,
248	       mode->vtotal);
249
250	printf(" flags: ");
251	mode_flag_str(mode->flags);
252	printf("; type: ");
253	mode_type_str(mode->type);
254	printf("\n");
255}
256
257static void dump_blob(struct device *dev, uint32_t blob_id)
258{
259	uint32_t i;
260	unsigned char *blob_data;
261	drmModePropertyBlobPtr blob;
262
263	blob = drmModeGetPropertyBlob(dev->fd, blob_id);
264	if (!blob)
265		return;
266
267	blob_data = blob->data;
268
269	for (i = 0; i < blob->length; i++) {
270		if (i % 16 == 0)
271			printf("\n\t\t\t");
272		printf("%.2hhx", blob_data[i]);
273	}
274	printf("\n");
275
276	drmModeFreePropertyBlob(blob);
277}
278
279static void dump_prop(struct device *dev, drmModePropertyPtr prop,
280		      uint32_t prop_id, uint64_t value)
281{
282	int i;
283	printf("\t%d", prop_id);
284	if (!prop) {
285		printf("\n");
286		return;
287	}
288
289	printf(" %s:\n", prop->name);
290
291	printf("\t\tflags:");
292	if (prop->flags & DRM_MODE_PROP_PENDING)
293		printf(" pending");
294	if (prop->flags & DRM_MODE_PROP_RANGE)
295		printf(" range");
296	if (prop->flags & DRM_MODE_PROP_IMMUTABLE)
297		printf(" immutable");
298	if (prop->flags & DRM_MODE_PROP_ENUM)
299		printf(" enum");
300	if (prop->flags & DRM_MODE_PROP_BITMASK)
301		printf(" bitmask");
302	if (prop->flags & DRM_MODE_PROP_BLOB)
303		printf(" blob");
304	printf("\n");
305
306	if (prop->flags & DRM_MODE_PROP_RANGE) {
307		printf("\t\tvalues:");
308		for (i = 0; i < prop->count_values; i++)
309			printf(" %"PRIu64, prop->values[i]);
310		printf("\n");
311	}
312
313	if (prop->flags & DRM_MODE_PROP_ENUM) {
314		printf("\t\tenums:");
315		for (i = 0; i < prop->count_enums; i++)
316			printf(" %s=%llu", prop->enums[i].name,
317			       prop->enums[i].value);
318		printf("\n");
319	} else if (prop->flags & DRM_MODE_PROP_BITMASK) {
320		printf("\t\tvalues:");
321		for (i = 0; i < prop->count_enums; i++)
322			printf(" %s=0x%llx", prop->enums[i].name,
323			       (1LL << prop->enums[i].value));
324		printf("\n");
325	} else {
326		assert(prop->count_enums == 0);
327	}
328
329	if (prop->flags & DRM_MODE_PROP_BLOB) {
330		printf("\t\tblobs:\n");
331		for (i = 0; i < prop->count_blobs; i++)
332			dump_blob(dev, prop->blob_ids[i]);
333		printf("\n");
334	} else {
335		assert(prop->count_blobs == 0);
336	}
337
338	printf("\t\tvalue:");
339	if (prop->flags & DRM_MODE_PROP_BLOB)
340		dump_blob(dev, value);
341	else
342		printf(" %"PRIu64"\n", value);
343}
344
345static void dump_connectors(struct device *dev)
346{
347	int i, j;
348
349	printf("Connectors:\n");
350	printf("id\tencoder\tstatus\t\ttype\tsize (mm)\tmodes\tencoders\n");
351	for (i = 0; i < dev->resources->res->count_connectors; i++) {
352		struct connector *_connector = &dev->resources->connectors[i];
353		drmModeConnector *connector = _connector->connector;
354		if (!connector)
355			continue;
356
357		printf("%d\t%d\t%s\t%s\t%dx%d\t\t%d\t",
358		       connector->connector_id,
359		       connector->encoder_id,
360		       connector_status_str(connector->connection),
361		       connector_type_str(connector->connector_type),
362		       connector->mmWidth, connector->mmHeight,
363		       connector->count_modes);
364
365		for (j = 0; j < connector->count_encoders; j++)
366			printf("%s%d", j > 0 ? ", " : "", connector->encoders[j]);
367		printf("\n");
368
369		if (connector->count_modes) {
370			printf("  modes:\n");
371			printf("\tname refresh (Hz) hdisp hss hse htot vdisp "
372			       "vss vse vtot)\n");
373			for (j = 0; j < connector->count_modes; j++)
374				dump_mode(&connector->modes[j]);
375		}
376
377		if (_connector->props) {
378			printf("  props:\n");
379			for (j = 0; j < (int)_connector->props->count_props; j++)
380				dump_prop(dev, _connector->props_info[j],
381					  _connector->props->props[j],
382					  _connector->props->prop_values[j]);
383		}
384	}
385	printf("\n");
386}
387
388static void dump_crtcs(struct device *dev)
389{
390	int i;
391	uint32_t j;
392
393	printf("CRTCs:\n");
394	printf("id\tfb\tpos\tsize\n");
395	for (i = 0; i < dev->resources->res->count_crtcs; i++) {
396		struct crtc *_crtc = &dev->resources->crtcs[i];
397		drmModeCrtc *crtc = _crtc->crtc;
398		if (!crtc)
399			continue;
400
401		printf("%d\t%d\t(%d,%d)\t(%dx%d)\n",
402		       crtc->crtc_id,
403		       crtc->buffer_id,
404		       crtc->x, crtc->y,
405		       crtc->width, crtc->height);
406		dump_mode(&crtc->mode);
407
408		if (_crtc->props) {
409			printf("  props:\n");
410			for (j = 0; j < _crtc->props->count_props; j++)
411				dump_prop(dev, _crtc->props_info[j],
412					  _crtc->props->props[j],
413					  _crtc->props->prop_values[j]);
414		} else {
415			printf("  no properties found\n");
416		}
417	}
418	printf("\n");
419}
420
421static void dump_framebuffers(struct device *dev)
422{
423	drmModeFB *fb;
424	int i;
425
426	printf("Frame buffers:\n");
427	printf("id\tsize\tpitch\n");
428	for (i = 0; i < dev->resources->res->count_fbs; i++) {
429		fb = dev->resources->fbs[i].fb;
430		if (!fb)
431			continue;
432
433		printf("%u\t(%ux%u)\t%u\n",
434		       fb->fb_id,
435		       fb->width, fb->height,
436		       fb->pitch);
437	}
438	printf("\n");
439}
440
441static void dump_planes(struct device *dev)
442{
443	unsigned int i, j;
444
445	printf("Planes:\n");
446	printf("id\tcrtc\tfb\tCRTC x,y\tx,y\tgamma size\tpossible crtcs\n");
447
448	if (!dev->resources->plane_res)
449		return;
450
451	for (i = 0; i < dev->resources->plane_res->count_planes; i++) {
452		struct plane *plane = &dev->resources->planes[i];
453		drmModePlane *ovr = plane->plane;
454		if (!ovr)
455			continue;
456
457		printf("%d\t%d\t%d\t%d,%d\t\t%d,%d\t%-8d\t0x%08x\n",
458		       ovr->plane_id, ovr->crtc_id, ovr->fb_id,
459		       ovr->crtc_x, ovr->crtc_y, ovr->x, ovr->y,
460		       ovr->gamma_size, ovr->possible_crtcs);
461
462		if (!ovr->count_formats)
463			continue;
464
465		printf("  formats:");
466		for (j = 0; j < ovr->count_formats; j++)
467			printf(" %4.4s", (char *)&ovr->formats[j]);
468		printf("\n");
469
470		if (plane->props) {
471			printf("  props:\n");
472			for (j = 0; j < plane->props->count_props; j++)
473				dump_prop(dev, plane->props_info[j],
474					  plane->props->props[j],
475					  plane->props->prop_values[j]);
476		} else {
477			printf("  no properties found\n");
478		}
479	}
480	printf("\n");
481
482	return;
483}
484
485static void free_resources(struct resources *res)
486{
487	if (!res)
488		return;
489
490#define free_resource(_res, __res, type, Type)					\
491	do {									\
492		int i;								\
493		if (!(_res)->type##s)						\
494			break;							\
495		for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) {	\
496			if (!(_res)->type##s[i].type)				\
497				break;						\
498			drmModeFree##Type((_res)->type##s[i].type);		\
499		}								\
500		free((_res)->type##s);						\
501	} while (0)
502
503#define free_properties(_res, __res, type)					\
504	do {									\
505		int i;								\
506		for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) {	\
507			drmModeFreeObjectProperties(res->type##s[i].props);	\
508			free(res->type##s[i].props_info);			\
509		}								\
510	} while (0)
511
512	if (res->res) {
513		free_properties(res, res, crtc);
514
515		free_resource(res, res, crtc, Crtc);
516		free_resource(res, res, encoder, Encoder);
517		free_resource(res, res, connector, Connector);
518		free_resource(res, res, fb, FB);
519
520		drmModeFreeResources(res->res);
521	}
522
523	if (res->plane_res) {
524		free_properties(res, plane_res, plane);
525
526		free_resource(res, plane_res, plane, Plane);
527
528		drmModeFreePlaneResources(res->plane_res);
529	}
530
531	free(res);
532}
533
534static struct resources *get_resources(struct device *dev)
535{
536	struct resources *res;
537	int i;
538
539	res = malloc(sizeof *res);
540	if (res == 0)
541		return NULL;
542
543	memset(res, 0, sizeof *res);
544
545	res->res = drmModeGetResources(dev->fd);
546	if (!res->res) {
547		fprintf(stderr, "drmModeGetResources failed: %s\n",
548			strerror(errno));
549		goto error;
550	}
551
552	res->crtcs = malloc(res->res->count_crtcs * sizeof *res->crtcs);
553	res->encoders = malloc(res->res->count_encoders * sizeof *res->encoders);
554	res->connectors = malloc(res->res->count_connectors * sizeof *res->connectors);
555	res->fbs = malloc(res->res->count_fbs * sizeof *res->fbs);
556
557	if (!res->crtcs || !res->encoders || !res->connectors || !res->fbs)
558		goto error;
559
560	memset(res->crtcs , 0, res->res->count_crtcs * sizeof *res->crtcs);
561	memset(res->encoders, 0, res->res->count_encoders * sizeof *res->encoders);
562	memset(res->connectors, 0, res->res->count_connectors * sizeof *res->connectors);
563	memset(res->fbs, 0, res->res->count_fbs * sizeof *res->fbs);
564
565#define get_resource(_res, __res, type, Type)					\
566	do {									\
567		int i;								\
568		for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) {	\
569			(_res)->type##s[i].type =				\
570				drmModeGet##Type(dev->fd, (_res)->__res->type##s[i]); \
571			if (!(_res)->type##s[i].type)				\
572				fprintf(stderr, "could not get %s %i: %s\n",	\
573					#type, (_res)->__res->type##s[i],	\
574					strerror(errno));			\
575		}								\
576	} while (0)
577
578	get_resource(res, res, crtc, Crtc);
579	get_resource(res, res, encoder, Encoder);
580	get_resource(res, res, connector, Connector);
581	get_resource(res, res, fb, FB);
582
583#define get_properties(_res, __res, type, Type)					\
584	do {									\
585		int i;								\
586		for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) {	\
587			struct type *obj = &res->type##s[i];			\
588			unsigned int j;						\
589			obj->props =						\
590				drmModeObjectGetProperties(dev->fd, obj->type->type##_id, \
591							   DRM_MODE_OBJECT_##Type); \
592			if (!obj->props) {					\
593				fprintf(stderr,					\
594					"could not get %s %i properties: %s\n", \
595					#type, obj->type->type##_id,		\
596					strerror(errno));			\
597				continue;					\
598			}							\
599			obj->props_info = malloc(obj->props->count_props *	\
600						 sizeof *obj->props_info);	\
601			if (!obj->props_info)					\
602				continue;					\
603			for (j = 0; j < obj->props->count_props; ++j)		\
604				obj->props_info[j] =				\
605					drmModeGetProperty(dev->fd, obj->props->props[j]); \
606		}								\
607	} while (0)
608
609	get_properties(res, res, crtc, CRTC);
610	get_properties(res, res, connector, CONNECTOR);
611
612	for (i = 0; i < res->res->count_crtcs; ++i)
613		res->crtcs[i].mode = &res->crtcs[i].crtc->mode;
614
615	res->plane_res = drmModeGetPlaneResources(dev->fd);
616	if (!res->plane_res) {
617		fprintf(stderr, "drmModeGetPlaneResources failed: %s\n",
618			strerror(errno));
619		return res;
620	}
621
622	res->planes = malloc(res->plane_res->count_planes * sizeof *res->planes);
623	if (!res->planes)
624		goto error;
625
626	memset(res->planes, 0, res->plane_res->count_planes * sizeof *res->planes);
627
628	get_resource(res, plane_res, plane, Plane);
629	get_properties(res, plane_res, plane, PLANE);
630
631	return res;
632
633error:
634	free_resources(res);
635	return NULL;
636}
637
638static int get_crtc_index(struct device *dev, uint32_t id)
639{
640	int i;
641
642	for (i = 0; i < dev->resources->res->count_crtcs; ++i) {
643		drmModeCrtc *crtc = dev->resources->crtcs[i].crtc;
644		if (crtc && crtc->crtc_id == id)
645			return i;
646	}
647
648	return -1;
649}
650
651static drmModeConnector *get_connector_by_id(struct device *dev, uint32_t id)
652{
653	drmModeConnector *connector;
654	int i;
655
656	for (i = 0; i < dev->resources->res->count_connectors; i++) {
657		connector = dev->resources->connectors[i].connector;
658		if (connector && connector->connector_id == id)
659			return connector;
660	}
661
662	return NULL;
663}
664
665static drmModeEncoder *get_encoder_by_id(struct device *dev, uint32_t id)
666{
667	drmModeEncoder *encoder;
668	int i;
669
670	for (i = 0; i < dev->resources->res->count_encoders; i++) {
671		encoder = dev->resources->encoders[i].encoder;
672		if (encoder && encoder->encoder_id == id)
673			return encoder;
674	}
675
676	return NULL;
677}
678
679/* -----------------------------------------------------------------------------
680 * Pipes and planes
681 */
682
683/*
684 * Mode setting with the kernel interfaces is a bit of a chore.
685 * First you have to find the connector in question and make sure the
686 * requested mode is available.
687 * Then you need to find the encoder attached to that connector so you
688 * can bind it with a free crtc.
689 */
690struct pipe_arg {
691	uint32_t *con_ids;
692	unsigned int num_cons;
693	uint32_t crtc_id;
694	char mode_str[64];
695	char format_str[5];
696	unsigned int vrefresh;
697	unsigned int fourcc;
698	drmModeModeInfo *mode;
699	struct crtc *crtc;
700	unsigned int fb_id[2], current_fb_id;
701	struct timeval start;
702
703	int swap_count;
704};
705
706struct plane_arg {
707	uint32_t crtc_id;  /* the id of CRTC to bind to */
708	bool has_position;
709	int32_t x, y;
710	uint32_t w, h;
711	double scale;
712	unsigned int fb_id;
713	char format_str[5]; /* need to leave room for terminating \0 */
714	unsigned int fourcc;
715};
716
717static drmModeModeInfo *
718connector_find_mode(struct device *dev, uint32_t con_id, const char *mode_str,
719        const unsigned int vrefresh)
720{
721	drmModeConnector *connector;
722	drmModeModeInfo *mode;
723	int i;
724
725	connector = get_connector_by_id(dev, con_id);
726	if (!connector || !connector->count_modes)
727		return NULL;
728
729	for (i = 0; i < connector->count_modes; i++) {
730		mode = &connector->modes[i];
731		if (!strcmp(mode->name, mode_str)) {
732			/* If the vertical refresh frequency is not specified then return the
733			 * first mode that match with the name. Else, return the mode that match
734			 * the name and the specified vertical refresh frequency.
735			 */
736			if (vrefresh == 0)
737				return mode;
738			else if (mode->vrefresh == vrefresh)
739				return mode;
740		}
741	}
742
743	return NULL;
744}
745
746static struct crtc *pipe_find_crtc(struct device *dev, struct pipe_arg *pipe)
747{
748	uint32_t possible_crtcs = ~0;
749	uint32_t active_crtcs = 0;
750	unsigned int crtc_idx;
751	unsigned int i;
752	int j;
753
754	for (i = 0; i < pipe->num_cons; ++i) {
755		uint32_t crtcs_for_connector = 0;
756		drmModeConnector *connector;
757		drmModeEncoder *encoder;
758		int idx;
759
760		connector = get_connector_by_id(dev, pipe->con_ids[i]);
761		if (!connector)
762			return NULL;
763
764		for (j = 0; j < connector->count_encoders; ++j) {
765			encoder = get_encoder_by_id(dev, connector->encoders[j]);
766			if (!encoder)
767				continue;
768
769			crtcs_for_connector |= encoder->possible_crtcs;
770
771			idx = get_crtc_index(dev, encoder->crtc_id);
772			if (idx >= 0)
773				active_crtcs |= 1 << idx;
774		}
775
776		possible_crtcs &= crtcs_for_connector;
777	}
778
779	if (!possible_crtcs)
780		return NULL;
781
782	/* Return the first possible and active CRTC if one exists, or the first
783	 * possible CRTC otherwise.
784	 */
785	if (possible_crtcs & active_crtcs)
786		crtc_idx = ffs(possible_crtcs & active_crtcs);
787	else
788		crtc_idx = ffs(possible_crtcs);
789
790	return &dev->resources->crtcs[crtc_idx - 1];
791}
792
793static int pipe_find_crtc_and_mode(struct device *dev, struct pipe_arg *pipe)
794{
795	drmModeModeInfo *mode = NULL;
796	int i;
797
798	pipe->mode = NULL;
799
800	for (i = 0; i < (int)pipe->num_cons; i++) {
801		mode = connector_find_mode(dev, pipe->con_ids[i],
802					   pipe->mode_str, pipe->vrefresh);
803		if (mode == NULL) {
804			fprintf(stderr,
805				"failed to find mode \"%s\" for connector %u\n",
806				pipe->mode_str, pipe->con_ids[i]);
807			return -EINVAL;
808		}
809	}
810
811	/* If the CRTC ID was specified, get the corresponding CRTC. Otherwise
812	 * locate a CRTC that can be attached to all the connectors.
813	 */
814	if (pipe->crtc_id != (uint32_t)-1) {
815		for (i = 0; i < dev->resources->res->count_crtcs; i++) {
816			struct crtc *crtc = &dev->resources->crtcs[i];
817
818			if (pipe->crtc_id == crtc->crtc->crtc_id) {
819				pipe->crtc = crtc;
820				break;
821			}
822		}
823	} else {
824		pipe->crtc = pipe_find_crtc(dev, pipe);
825	}
826
827	if (!pipe->crtc) {
828		fprintf(stderr, "failed to find CRTC for pipe\n");
829		return -EINVAL;
830	}
831
832	pipe->mode = mode;
833	pipe->crtc->mode = mode;
834
835	return 0;
836}
837
838/* -----------------------------------------------------------------------------
839 * Properties
840 */
841
842struct property_arg {
843	uint32_t obj_id;
844	uint32_t obj_type;
845	char name[DRM_PROP_NAME_LEN+1];
846	uint32_t prop_id;
847	uint64_t value;
848};
849
850static void set_property(struct device *dev, struct property_arg *p)
851{
852	drmModeObjectProperties *props = NULL;
853	drmModePropertyRes **props_info = NULL;
854	const char *obj_type;
855	int ret;
856	int i;
857
858	p->obj_type = 0;
859	p->prop_id = 0;
860
861#define find_object(_res, __res, type, Type)					\
862	do {									\
863		for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) {	\
864			struct type *obj = &(_res)->type##s[i];			\
865			if (obj->type->type##_id != p->obj_id)			\
866				continue;					\
867			p->obj_type = DRM_MODE_OBJECT_##Type;			\
868			obj_type = #Type;					\
869			props = obj->props;					\
870			props_info = obj->props_info;				\
871		}								\
872	} while(0)								\
873
874	find_object(dev->resources, res, crtc, CRTC);
875	if (p->obj_type == 0)
876		find_object(dev->resources, res, connector, CONNECTOR);
877	if (p->obj_type == 0)
878		find_object(dev->resources, plane_res, plane, PLANE);
879	if (p->obj_type == 0) {
880		fprintf(stderr, "Object %i not found, can't set property\n",
881			p->obj_id);
882			return;
883	}
884
885	if (!props) {
886		fprintf(stderr, "%s %i has no properties\n",
887			obj_type, p->obj_id);
888		return;
889	}
890
891	for (i = 0; i < (int)props->count_props; ++i) {
892		if (!props_info[i])
893			continue;
894		if (strcmp(props_info[i]->name, p->name) == 0)
895			break;
896	}
897
898	if (i == (int)props->count_props) {
899		fprintf(stderr, "%s %i has no %s property\n",
900			obj_type, p->obj_id, p->name);
901		return;
902	}
903
904	p->prop_id = props->props[i];
905
906	ret = drmModeObjectSetProperty(dev->fd, p->obj_id, p->obj_type,
907				       p->prop_id, p->value);
908	if (ret < 0)
909		fprintf(stderr, "failed to set %s %i property %s to %" PRIu64 ": %s\n",
910			obj_type, p->obj_id, p->name, p->value, strerror(errno));
911}
912
913/* -------------------------------------------------------------------------- */
914
915static void
916page_flip_handler(int fd, unsigned int frame,
917		  unsigned int sec, unsigned int usec, void *data)
918{
919	struct pipe_arg *pipe;
920	unsigned int new_fb_id;
921	struct timeval end;
922	double t;
923
924	pipe = data;
925	if (pipe->current_fb_id == pipe->fb_id[0])
926		new_fb_id = pipe->fb_id[1];
927	else
928		new_fb_id = pipe->fb_id[0];
929
930	drmModePageFlip(fd, pipe->crtc->crtc->crtc_id, new_fb_id,
931			DRM_MODE_PAGE_FLIP_EVENT, pipe);
932	pipe->current_fb_id = new_fb_id;
933	pipe->swap_count++;
934	if (pipe->swap_count == 60) {
935		gettimeofday(&end, NULL);
936		t = end.tv_sec + end.tv_usec * 1e-6 -
937			(pipe->start.tv_sec + pipe->start.tv_usec * 1e-6);
938		fprintf(stderr, "freq: %.02fHz\n", pipe->swap_count / t);
939		pipe->swap_count = 0;
940		pipe->start = end;
941	}
942}
943
944static int set_plane(struct device *dev, struct plane_arg *p)
945{
946	drmModePlane *ovr;
947	uint32_t handles[4], pitches[4], offsets[4] = {0}; /* we only use [0] */
948	uint32_t plane_id = 0;
949	struct kms_bo *plane_bo;
950	uint32_t plane_flags = 0;
951	int crtc_x, crtc_y, crtc_w, crtc_h;
952	struct crtc *crtc = NULL;
953	unsigned int pipe;
954	unsigned int i;
955
956	/* Find an unused plane which can be connected to our CRTC. Find the
957	 * CRTC index first, then iterate over available planes.
958	 */
959	for (i = 0; i < (unsigned int)dev->resources->res->count_crtcs; i++) {
960		if (p->crtc_id == dev->resources->res->crtcs[i]) {
961			crtc = &dev->resources->crtcs[i];
962			pipe = i;
963			break;
964		}
965	}
966
967	if (!crtc) {
968		fprintf(stderr, "CRTC %u not found\n", p->crtc_id);
969		return -1;
970	}
971
972	for (i = 0; i < dev->resources->plane_res->count_planes && !plane_id; i++) {
973		ovr = dev->resources->planes[i].plane;
974		if (!ovr)
975			continue;
976
977		if ((ovr->possible_crtcs & (1 << pipe)) && !ovr->crtc_id)
978			plane_id = ovr->plane_id;
979	}
980
981	if (!plane_id) {
982		fprintf(stderr, "no unused plane available for CRTC %u\n",
983			crtc->crtc->crtc_id);
984		return -1;
985	}
986
987	fprintf(stderr, "testing %dx%d@%s overlay plane %u\n",
988		p->w, p->h, p->format_str, plane_id);
989
990	plane_bo = create_test_buffer(dev->kms, p->fourcc, p->w, p->h, handles,
991				      pitches, offsets, PATTERN_TILES);
992	if (plane_bo == NULL)
993		return -1;
994
995	/* just use single plane format for now.. */
996	if (drmModeAddFB2(dev->fd, p->w, p->h, p->fourcc,
997			handles, pitches, offsets, &p->fb_id, plane_flags)) {
998		fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
999		return -1;
1000	}
1001
1002	crtc_w = p->w * p->scale;
1003	crtc_h = p->h * p->scale;
1004	if (!p->has_position) {
1005		/* Default to the middle of the screen */
1006		crtc_x = (crtc->mode->hdisplay - crtc_w) / 2;
1007		crtc_y = (crtc->mode->vdisplay - crtc_h) / 2;
1008	} else {
1009		crtc_x = p->x;
1010		crtc_y = p->y;
1011	}
1012
1013	/* note src coords (last 4 args) are in Q16 format */
1014	if (drmModeSetPlane(dev->fd, plane_id, crtc->crtc->crtc_id, p->fb_id,
1015			    plane_flags, crtc_x, crtc_y, crtc_w, crtc_h,
1016			    0, 0, p->w << 16, p->h << 16)) {
1017		fprintf(stderr, "failed to enable plane: %s\n",
1018			strerror(errno));
1019		return -1;
1020	}
1021
1022	ovr->crtc_id = crtc->crtc->crtc_id;
1023
1024	return 0;
1025}
1026
1027static void set_mode(struct device *dev, struct pipe_arg *pipes, unsigned int count)
1028{
1029	uint32_t handles[4], pitches[4], offsets[4] = {0}; /* we only use [0] */
1030	unsigned int fb_id;
1031	struct kms_bo *bo;
1032	unsigned int i;
1033	unsigned int j;
1034	int ret, x;
1035
1036	dev->mode.width = 0;
1037	dev->mode.height = 0;
1038
1039	for (i = 0; i < count; i++) {
1040		struct pipe_arg *pipe = &pipes[i];
1041
1042		ret = pipe_find_crtc_and_mode(dev, pipe);
1043		if (ret < 0)
1044			continue;
1045
1046		dev->mode.width += pipe->mode->hdisplay;
1047		if (dev->mode.height < pipe->mode->vdisplay)
1048			dev->mode.height = pipe->mode->vdisplay;
1049	}
1050
1051	bo = create_test_buffer(dev->kms, pipes[0].fourcc,
1052				dev->mode.width, dev->mode.height,
1053				handles, pitches, offsets, PATTERN_SMPTE);
1054	if (bo == NULL)
1055		return;
1056
1057	ret = drmModeAddFB2(dev->fd, dev->mode.width, dev->mode.height,
1058			    pipes[0].fourcc, handles, pitches, offsets, &fb_id, 0);
1059	if (ret) {
1060		fprintf(stderr, "failed to add fb (%ux%u): %s\n",
1061			dev->mode.width, dev->mode.height, strerror(errno));
1062		return;
1063	}
1064
1065	x = 0;
1066	for (i = 0; i < count; i++) {
1067		struct pipe_arg *pipe = &pipes[i];
1068
1069		if (pipe->mode == NULL)
1070			continue;
1071
1072		printf("setting mode %s-%dHz@%s on connectors ",
1073		       pipe->mode_str, pipe->mode->vrefresh, pipe->format_str);
1074		for (j = 0; j < pipe->num_cons; ++j)
1075			printf("%u, ", pipe->con_ids[j]);
1076		printf("crtc %d\n", pipe->crtc->crtc->crtc_id);
1077
1078		ret = drmModeSetCrtc(dev->fd, pipe->crtc->crtc->crtc_id, fb_id,
1079				     x, 0, pipe->con_ids, pipe->num_cons,
1080				     pipe->mode);
1081
1082		/* XXX: Actually check if this is needed */
1083		drmModeDirtyFB(dev->fd, fb_id, NULL, 0);
1084
1085		x += pipe->mode->hdisplay;
1086
1087		if (ret) {
1088			fprintf(stderr, "failed to set mode: %s\n", strerror(errno));
1089			return;
1090		}
1091	}
1092
1093	dev->mode.bo = bo;
1094	dev->mode.fb_id = fb_id;
1095}
1096
1097static void set_planes(struct device *dev, struct plane_arg *p, unsigned int count)
1098{
1099	unsigned int i;
1100
1101	/* set up planes/overlays */
1102	for (i = 0; i < count; i++)
1103		if (set_plane(dev, &p[i]))
1104			return;
1105}
1106
1107static void test_page_flip(struct device *dev, struct pipe_arg *pipes, unsigned int count)
1108{
1109	uint32_t handles[4], pitches[4], offsets[4] = {0}; /* we only use [0] */
1110	unsigned int other_fb_id;
1111	struct kms_bo *other_bo;
1112	drmEventContext evctx;
1113	unsigned int i;
1114	int ret;
1115
1116	other_bo = create_test_buffer(dev->kms, pipes[0].fourcc,
1117				      dev->mode.width, dev->mode.height,
1118				      handles, pitches, offsets, PATTERN_PLAIN);
1119	if (other_bo == NULL)
1120		return;
1121
1122	ret = drmModeAddFB2(dev->fd, dev->mode.width, dev->mode.height,
1123			    pipes[0].fourcc, handles, pitches, offsets,
1124			    &other_fb_id, 0);
1125	if (ret) {
1126		fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
1127		return;
1128	}
1129
1130	for (i = 0; i < count; i++) {
1131		struct pipe_arg *pipe = &pipes[i];
1132
1133		if (pipe->mode == NULL)
1134			continue;
1135
1136		ret = drmModePageFlip(dev->fd, pipe->crtc->crtc->crtc_id,
1137				      other_fb_id, DRM_MODE_PAGE_FLIP_EVENT,
1138				      pipe);
1139		if (ret) {
1140			fprintf(stderr, "failed to page flip: %s\n", strerror(errno));
1141			return;
1142		}
1143		gettimeofday(&pipe->start, NULL);
1144		pipe->swap_count = 0;
1145		pipe->fb_id[0] = dev->mode.fb_id;
1146		pipe->fb_id[1] = other_fb_id;
1147		pipe->current_fb_id = other_fb_id;
1148	}
1149
1150	memset(&evctx, 0, sizeof evctx);
1151	evctx.version = DRM_EVENT_CONTEXT_VERSION;
1152	evctx.vblank_handler = NULL;
1153	evctx.page_flip_handler = page_flip_handler;
1154
1155	while (1) {
1156#if 0
1157		struct pollfd pfd[2];
1158
1159		pfd[0].fd = 0;
1160		pfd[0].events = POLLIN;
1161		pfd[1].fd = fd;
1162		pfd[1].events = POLLIN;
1163
1164		if (poll(pfd, 2, -1) < 0) {
1165			fprintf(stderr, "poll error\n");
1166			break;
1167		}
1168
1169		if (pfd[0].revents)
1170			break;
1171#else
1172		struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 };
1173		fd_set fds;
1174		int ret;
1175
1176		FD_ZERO(&fds);
1177		FD_SET(0, &fds);
1178		FD_SET(dev->fd, &fds);
1179		ret = select(dev->fd + 1, &fds, NULL, NULL, &timeout);
1180
1181		if (ret <= 0) {
1182			fprintf(stderr, "select timed out or error (ret %d)\n",
1183				ret);
1184			continue;
1185		} else if (FD_ISSET(0, &fds)) {
1186			break;
1187		}
1188#endif
1189
1190		drmHandleEvent(dev->fd, &evctx);
1191	}
1192
1193	kms_bo_destroy(&other_bo);
1194}
1195
1196#define min(a, b)	((a) < (b) ? (a) : (b))
1197
1198/* XXX local definition of GNUism for expedience */
1199static char *
1200strchrnul(const char *s, int c)
1201{
1202
1203	while ((*s != c) && (*s != '\0'))
1204		s++;
1205
1206	return __UNCONST(s);
1207}
1208
1209static int parse_connector(struct pipe_arg *pipe, const char *arg)
1210{
1211	unsigned int len;
1212	unsigned int i;
1213	const char *p;
1214	char *endp;
1215
1216	pipe->vrefresh = 0;
1217	pipe->crtc_id = (uint32_t)-1;
1218	strcpy(pipe->format_str, "XR24");
1219
1220	/* Count the number of connectors and allocate them. */
1221	pipe->num_cons = 1;
1222	for (p = arg; isdigit(*p) || *p == ','; ++p) {
1223		if (*p == ',')
1224			pipe->num_cons++;
1225	}
1226
1227	pipe->con_ids = malloc(pipe->num_cons * sizeof *pipe->con_ids);
1228	if (pipe->con_ids == NULL)
1229		return -1;
1230
1231	/* Parse the connectors. */
1232	for (i = 0, p = arg; i < pipe->num_cons; ++i, p = endp + 1) {
1233		pipe->con_ids[i] = strtoul(p, &endp, 10);
1234		if (*endp != ',')
1235			break;
1236	}
1237
1238	if (i != pipe->num_cons - 1)
1239		return -1;
1240
1241	/* Parse the remaining parameters. */
1242	if (*endp == '@') {
1243		arg = endp + 1;
1244		pipe->crtc_id = strtoul(arg, &endp, 10);
1245	}
1246	if (*endp != ':')
1247		return -1;
1248
1249	arg = endp + 1;
1250
1251	/* Search for the vertical refresh or the format. */
1252	p = strpbrk(arg, "-@");
1253	if (p == NULL)
1254		p = arg + strlen(arg);
1255	len = min(sizeof pipe->mode_str - 1, (unsigned int)(p - arg));
1256	strncpy(pipe->mode_str, arg, len);
1257	pipe->mode_str[len] = '\0';
1258
1259	if (*p == '-') {
1260		pipe->vrefresh = strtoul(p + 1, &endp, 10);
1261		p = endp;
1262	}
1263
1264	if (*p == '@') {
1265		strncpy(pipe->format_str, p + 1, 4);
1266		pipe->format_str[4] = '\0';
1267	}
1268
1269	pipe->fourcc = format_fourcc(pipe->format_str);
1270	if (pipe->fourcc == 0)  {
1271		fprintf(stderr, "unknown format %s\n", pipe->format_str);
1272		return -1;
1273	}
1274
1275	return 0;
1276}
1277
1278static int parse_plane(struct plane_arg *plane, const char *p)
1279{
1280	char *end;
1281
1282	memset(plane, 0, sizeof *plane);
1283
1284	plane->crtc_id = strtoul(p, &end, 10);
1285	if (*end != ':')
1286		return -EINVAL;
1287
1288	p = end + 1;
1289	plane->w = strtoul(p, &end, 10);
1290	if (*end != 'x')
1291		return -EINVAL;
1292
1293	p = end + 1;
1294	plane->h = strtoul(p, &end, 10);
1295
1296	if (*end == '+' || *end == '-') {
1297		plane->x = strtol(end, &end, 10);
1298		if (*end != '+' && *end != '-')
1299			return -EINVAL;
1300		plane->y = strtol(end, &end, 10);
1301
1302		plane->has_position = true;
1303	}
1304
1305	if (*end == '*') {
1306		p = end + 1;
1307		plane->scale = strtod(p, &end);
1308		if (plane->scale <= 0.0)
1309			return -EINVAL;
1310	} else {
1311		plane->scale = 1.0;
1312	}
1313
1314	if (*end == '@') {
1315		p = end + 1;
1316		if (strlen(p) != 4)
1317			return -EINVAL;
1318
1319		strcpy(plane->format_str, p);
1320	} else {
1321		strcpy(plane->format_str, "XR24");
1322	}
1323
1324	plane->fourcc = format_fourcc(plane->format_str);
1325	if (plane->fourcc == 0) {
1326		fprintf(stderr, "unknown format %s\n", plane->format_str);
1327		return -EINVAL;
1328	}
1329
1330	return 0;
1331}
1332
1333static int parse_property(struct property_arg *p, const char *arg)
1334{
1335	if (sscanf(arg, "%d:%32[^:]:%" SCNu64, &p->obj_id, p->name, &p->value) != 3)
1336		return -1;
1337
1338	p->obj_type = 0;
1339	p->name[DRM_PROP_NAME_LEN] = '\0';
1340
1341	return 0;
1342}
1343
1344static void usage(char *name)
1345{
1346	fprintf(stderr, "usage: %s [-cDdefMPpsvw]\n", name);
1347
1348	fprintf(stderr, "\n Query options:\n\n");
1349	fprintf(stderr, "\t-c\tlist connectors\n");
1350	fprintf(stderr, "\t-e\tlist encoders\n");
1351	fprintf(stderr, "\t-f\tlist framebuffers\n");
1352	fprintf(stderr, "\t-p\tlist CRTCs and planes (pipes)\n");
1353
1354	fprintf(stderr, "\n Test options:\n\n");
1355	fprintf(stderr, "\t-P <crtc_id>:<w>x<h>[+<x>+<y>][*<scale>][@<format>]\tset a plane\n");
1356	fprintf(stderr, "\t-s <connector_id>[,<connector_id>][@<crtc_id>]:<mode>[-<vrefresh>][@<format>]\tset a mode\n");
1357	fprintf(stderr, "\t-v\ttest vsynced page flipping\n");
1358	fprintf(stderr, "\t-w <obj_id>:<prop_name>:<value>\tset property\n");
1359
1360	fprintf(stderr, "\n Generic options:\n\n");
1361	fprintf(stderr, "\t-d\tdrop master after mode set\n");
1362	fprintf(stderr, "\t-M module\tuse the given driver\n");
1363	fprintf(stderr, "\t-D device\tuse the given device\n");
1364
1365	fprintf(stderr, "\n\tDefault is to dump all info.\n");
1366	exit(0);
1367}
1368
1369static int page_flipping_supported(void)
1370{
1371	/*FIXME: generic ioctl needed? */
1372	return 1;
1373#if 0
1374	int ret, value;
1375	struct drm_i915_getparam gp;
1376
1377	gp.param = I915_PARAM_HAS_PAGEFLIPPING;
1378	gp.value = &value;
1379
1380	ret = drmCommandWriteRead(fd, DRM_I915_GETPARAM, &gp, sizeof(gp));
1381	if (ret) {
1382		fprintf(stderr, "drm_i915_getparam: %m\n");
1383		return 0;
1384	}
1385
1386	return *gp.value;
1387#endif
1388}
1389
1390static char optstr[] = "cdD:efM:P:ps:vw:";
1391
1392int main(int argc, char **argv)
1393{
1394	struct device dev;
1395
1396	int c;
1397	int encoders = 0, connectors = 0, crtcs = 0, planes = 0, framebuffers = 0;
1398	int drop_master = 0;
1399	int test_vsync = 0;
1400	const char *modules[] = { "i915", "radeon", "nouveau", "vmwgfx", "omapdrm", "exynos", "tilcdc", "msm" };
1401	char *device = NULL;
1402	char *module = NULL;
1403	unsigned int i;
1404	int count = 0, plane_count = 0;
1405	unsigned int prop_count = 0;
1406	struct pipe_arg *pipe_args = NULL;
1407	struct plane_arg *plane_args = NULL;
1408	struct property_arg *prop_args = NULL;
1409	unsigned int args = 0;
1410	int ret;
1411
1412	memset(&dev, 0, sizeof dev);
1413
1414	opterr = 0;
1415	while ((c = getopt(argc, argv, optstr)) != -1) {
1416		args++;
1417
1418		switch (c) {
1419		case 'c':
1420			connectors = 1;
1421			break;
1422		case 'D':
1423			device = optarg;
1424			args--;
1425			break;
1426		case 'd':
1427			drop_master = 1;
1428			break;
1429		case 'e':
1430			encoders = 1;
1431			break;
1432		case 'f':
1433			framebuffers = 1;
1434			break;
1435		case 'M':
1436			module = optarg;
1437			/* Preserve the default behaviour of dumping all information. */
1438			args--;
1439			break;
1440		case 'P':
1441			plane_args = realloc(plane_args,
1442					     (plane_count + 1) * sizeof *plane_args);
1443			if (plane_args == NULL) {
1444				fprintf(stderr, "memory allocation failed\n");
1445				return 1;
1446			}
1447
1448			if (parse_plane(&plane_args[plane_count], optarg) < 0)
1449				usage(argv[0]);
1450
1451			plane_count++;
1452			break;
1453		case 'p':
1454			crtcs = 1;
1455			planes = 1;
1456			break;
1457		case 's':
1458			pipe_args = realloc(pipe_args,
1459					    (count + 1) * sizeof *pipe_args);
1460			if (pipe_args == NULL) {
1461				fprintf(stderr, "memory allocation failed\n");
1462				return 1;
1463			}
1464
1465			if (parse_connector(&pipe_args[count], optarg) < 0)
1466				usage(argv[0]);
1467
1468			count++;
1469			break;
1470		case 'v':
1471			test_vsync = 1;
1472			break;
1473		case 'w':
1474			prop_args = realloc(prop_args,
1475					   (prop_count + 1) * sizeof *prop_args);
1476			if (prop_args == NULL) {
1477				fprintf(stderr, "memory allocation failed\n");
1478				return 1;
1479			}
1480
1481			if (parse_property(&prop_args[prop_count], optarg) < 0)
1482				usage(argv[0]);
1483
1484			prop_count++;
1485			break;
1486		default:
1487			usage(argv[0]);
1488			break;
1489		}
1490	}
1491
1492	if (!args)
1493		encoders = connectors = crtcs = planes = framebuffers = 1;
1494
1495	if (module) {
1496		dev.fd = drmOpen(module, device);
1497		if (dev.fd < 0) {
1498			fprintf(stderr, "failed to open device '%s'.\n", module);
1499			return 1;
1500		}
1501	} else {
1502		for (i = 0; i < ARRAY_SIZE(modules); i++) {
1503			printf("trying to open device '%s'...", modules[i]);
1504			dev.fd = drmOpen(modules[i], device);
1505			if (dev.fd < 0) {
1506				printf("failed.\n");
1507			} else {
1508				printf("success.\n");
1509				break;
1510			}
1511		}
1512
1513		if (dev.fd < 0) {
1514			fprintf(stderr, "no device found.\n");
1515			return 1;
1516		}
1517	}
1518
1519	if (test_vsync && !page_flipping_supported()) {
1520		fprintf(stderr, "page flipping not supported by drm.\n");
1521		return -1;
1522	}
1523
1524	if (test_vsync && !count) {
1525		fprintf(stderr, "page flipping requires at least one -s option.\n");
1526		return -1;
1527	}
1528
1529	dev.resources = get_resources(&dev);
1530	if (!dev.resources) {
1531		drmClose(dev.fd);
1532		return 1;
1533	}
1534
1535#define dump_resource(dev, res) if (res) dump_##res(dev)
1536
1537	dump_resource(&dev, encoders);
1538	dump_resource(&dev, connectors);
1539	dump_resource(&dev, crtcs);
1540	dump_resource(&dev, planes);
1541	dump_resource(&dev, framebuffers);
1542
1543	for (i = 0; i < prop_count; ++i)
1544		set_property(&dev, &prop_args[i]);
1545
1546	if (count || plane_count) {
1547		ret = kms_create(dev.fd, &dev.kms);
1548		if (ret) {
1549			fprintf(stderr, "failed to create kms driver: %s\n",
1550				strerror(-ret));
1551			return 1;
1552		}
1553
1554		if (count)
1555			set_mode(&dev, pipe_args, count);
1556
1557		if (plane_count)
1558			set_planes(&dev, plane_args, plane_count);
1559
1560		if (test_vsync)
1561			test_page_flip(&dev, pipe_args, count);
1562
1563		if (drop_master)
1564			drmDropMaster(dev.fd);
1565
1566		kms_bo_destroy(&dev.mode.bo);
1567		kms_destroy(&dev.kms);
1568
1569		getchar();
1570	}
1571
1572	free_resources(dev.resources);
1573
1574	return 0;
1575}
1576