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