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