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