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