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