modetest.c revision 87bf8e7c
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
41#include <assert.h>
42#include <ctype.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 <strings.h>
51#include <errno.h>
52#include <poll.h>
53#include <sys/time.h>
54#if HAVE_SYS_SELECT_H
55#include <sys/select.h>
56#endif
57#include <math.h>
58
59#include "xf86drm.h"
60#include "xf86drmMode.h"
61#include "drm_fourcc.h"
62
63#include "util/common.h"
64#include "util/format.h"
65#include "util/kms.h"
66#include "util/pattern.h"
67
68#include "buffers.h"
69#include "cursor.h"
70
71static enum util_fill_pattern primary_fill = UTIL_PATTERN_SMPTE;
72static enum util_fill_pattern secondary_fill = UTIL_PATTERN_TILES;
73
74struct crtc {
75	drmModeCrtc *crtc;
76	drmModeObjectProperties *props;
77	drmModePropertyRes **props_info;
78	drmModeModeInfo *mode;
79};
80
81struct encoder {
82	drmModeEncoder *encoder;
83};
84
85struct connector {
86	drmModeConnector *connector;
87	drmModeObjectProperties *props;
88	drmModePropertyRes **props_info;
89	char *name;
90};
91
92struct fb {
93	drmModeFB *fb;
94};
95
96struct plane {
97	drmModePlane *plane;
98	drmModeObjectProperties *props;
99	drmModePropertyRes **props_info;
100};
101
102struct resources {
103	struct crtc *crtcs;
104	int count_crtcs;
105	struct encoder *encoders;
106	int count_encoders;
107	struct connector *connectors;
108	int count_connectors;
109	struct fb *fbs;
110	int count_fbs;
111	struct plane *planes;
112	uint32_t count_planes;
113};
114
115struct device {
116	int fd;
117
118	struct resources *resources;
119
120	struct {
121		unsigned int width;
122		unsigned int height;
123
124		unsigned int fb_id;
125		struct bo *bo;
126		struct bo *cursor_bo;
127	} mode;
128
129	int use_atomic;
130	drmModeAtomicReq *req;
131};
132
133static inline int64_t U642I64(uint64_t val)
134{
135	return (int64_t)*((int64_t *)&val);
136}
137
138static float mode_vrefresh(drmModeModeInfo *mode)
139{
140	return  mode->clock * 1000.00
141			/ (mode->htotal * mode->vtotal);
142}
143
144#define bit_name_fn(res)					\
145const char * res##_str(int type) {				\
146	unsigned int i;						\
147	const char *sep = "";					\
148	for (i = 0; i < ARRAY_SIZE(res##_names); i++) {		\
149		if (type & (1 << i)) {				\
150			printf("%s%s", sep, res##_names[i]);	\
151			sep = ", ";				\
152		}						\
153	}							\
154	return NULL;						\
155}
156
157static const char *mode_type_names[] = {
158	"builtin",
159	"clock_c",
160	"crtc_c",
161	"preferred",
162	"default",
163	"userdef",
164	"driver",
165};
166
167static bit_name_fn(mode_type)
168
169static const char *mode_flag_names[] = {
170	"phsync",
171	"nhsync",
172	"pvsync",
173	"nvsync",
174	"interlace",
175	"dblscan",
176	"csync",
177	"pcsync",
178	"ncsync",
179	"hskew",
180	"bcast",
181	"pixmux",
182	"dblclk",
183	"clkdiv2"
184};
185
186static bit_name_fn(mode_flag)
187
188static void dump_fourcc(uint32_t fourcc)
189{
190	printf(" %c%c%c%c",
191		fourcc,
192		fourcc >> 8,
193		fourcc >> 16,
194		fourcc >> 24);
195}
196
197static void dump_encoders(struct device *dev)
198{
199	drmModeEncoder *encoder;
200	int i;
201
202	printf("Encoders:\n");
203	printf("id\tcrtc\ttype\tpossible crtcs\tpossible clones\t\n");
204	for (i = 0; i < dev->resources->count_encoders; i++) {
205		encoder = dev->resources->encoders[i].encoder;
206		if (!encoder)
207			continue;
208
209		printf("%d\t%d\t%s\t0x%08x\t0x%08x\n",
210		       encoder->encoder_id,
211		       encoder->crtc_id,
212		       util_lookup_encoder_type_name(encoder->encoder_type),
213		       encoder->possible_crtcs,
214		       encoder->possible_clones);
215	}
216	printf("\n");
217}
218
219static void dump_mode(drmModeModeInfo *mode, int index)
220{
221	printf("  #%i %s %.2f %d %d %d %d %d %d %d %d %d",
222	       index,
223	       mode->name,
224	       mode_vrefresh(mode),
225	       mode->hdisplay,
226	       mode->hsync_start,
227	       mode->hsync_end,
228	       mode->htotal,
229	       mode->vdisplay,
230	       mode->vsync_start,
231	       mode->vsync_end,
232	       mode->vtotal,
233	       mode->clock);
234
235	printf(" flags: ");
236	mode_flag_str(mode->flags);
237	printf("; type: ");
238	mode_type_str(mode->type);
239	printf("\n");
240}
241
242static void dump_blob(struct device *dev, uint32_t blob_id)
243{
244	uint32_t i;
245	unsigned char *blob_data;
246	drmModePropertyBlobPtr blob;
247
248	blob = drmModeGetPropertyBlob(dev->fd, blob_id);
249	if (!blob) {
250		printf("\n");
251		return;
252	}
253
254	blob_data = blob->data;
255
256	for (i = 0; i < blob->length; i++) {
257		if (i % 16 == 0)
258			printf("\n\t\t\t");
259		printf("%.2hhx", blob_data[i]);
260	}
261	printf("\n");
262
263	drmModeFreePropertyBlob(blob);
264}
265
266static const char *modifier_to_string(uint64_t modifier)
267{
268	switch (modifier) {
269	case DRM_FORMAT_MOD_INVALID:
270		return "INVALID";
271	case DRM_FORMAT_MOD_LINEAR:
272		return "LINEAR";
273	case I915_FORMAT_MOD_X_TILED:
274		return "X_TILED";
275	case I915_FORMAT_MOD_Y_TILED:
276		return "Y_TILED";
277	case I915_FORMAT_MOD_Yf_TILED:
278		return "Yf_TILED";
279	case I915_FORMAT_MOD_Y_TILED_CCS:
280		return "Y_TILED_CCS";
281	case I915_FORMAT_MOD_Yf_TILED_CCS:
282		return "Yf_TILED_CCS";
283	case DRM_FORMAT_MOD_SAMSUNG_64_32_TILE:
284		return "SAMSUNG_64_32_TILE";
285	case DRM_FORMAT_MOD_VIVANTE_TILED:
286		return "VIVANTE_TILED";
287	case DRM_FORMAT_MOD_VIVANTE_SUPER_TILED:
288		return "VIVANTE_SUPER_TILED";
289	case DRM_FORMAT_MOD_VIVANTE_SPLIT_TILED:
290		return "VIVANTE_SPLIT_TILED";
291	case DRM_FORMAT_MOD_VIVANTE_SPLIT_SUPER_TILED:
292		return "VIVANTE_SPLIT_SUPER_TILED";
293	case DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED:
294		return "NVIDIA_TEGRA_TILED";
295	case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0):
296		return "NVIDIA_16BX2_BLOCK(0)";
297	case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1):
298		return "NVIDIA_16BX2_BLOCK(1)";
299	case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2):
300		return "NVIDIA_16BX2_BLOCK(2)";
301	case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3):
302		return "NVIDIA_16BX2_BLOCK(3)";
303	case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4):
304		return "NVIDIA_16BX2_BLOCK(4)";
305	case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5):
306		return "NVIDIA_16BX2_BLOCK(5)";
307	case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED:
308		return "MOD_BROADCOM_VC4_T_TILED";
309	case DRM_FORMAT_MOD_QCOM_COMPRESSED:
310		return "QCOM_COMPRESSED";
311	default:
312		return "(UNKNOWN MODIFIER)";
313	}
314}
315
316static void dump_in_formats(struct device *dev, uint32_t blob_id)
317{
318	uint32_t i, j;
319	drmModePropertyBlobPtr blob;
320	struct drm_format_modifier_blob *header;
321	uint32_t *formats;
322	struct drm_format_modifier *modifiers;
323
324	printf("\t\tin_formats blob decoded:\n");
325	blob = drmModeGetPropertyBlob(dev->fd, blob_id);
326	if (!blob) {
327		printf("\n");
328		return;
329	}
330
331	header = blob->data;
332	formats = (uint32_t *) ((char *) header + header->formats_offset);
333	modifiers = (struct drm_format_modifier *)
334		((char *) header + header->modifiers_offset);
335
336	for (i = 0; i < header->count_formats; i++) {
337		printf("\t\t\t");
338		dump_fourcc(formats[i]);
339		printf(": ");
340		for (j = 0; j < header->count_modifiers; j++) {
341			uint64_t mask = 1ULL << i;
342			if (modifiers[j].formats & mask)
343				printf(" %s", modifier_to_string(modifiers[j].modifier));
344		}
345		printf("\n");
346	}
347
348	drmModeFreePropertyBlob(blob);
349}
350
351static void dump_prop(struct device *dev, drmModePropertyPtr prop,
352		      uint32_t prop_id, uint64_t value)
353{
354	int i;
355	printf("\t%d", prop_id);
356	if (!prop) {
357		printf("\n");
358		return;
359	}
360
361	printf(" %s:\n", prop->name);
362
363	printf("\t\tflags:");
364	if (prop->flags & DRM_MODE_PROP_PENDING)
365		printf(" pending");
366	if (prop->flags & DRM_MODE_PROP_IMMUTABLE)
367		printf(" immutable");
368	if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE))
369		printf(" signed range");
370	if (drm_property_type_is(prop, DRM_MODE_PROP_RANGE))
371		printf(" range");
372	if (drm_property_type_is(prop, DRM_MODE_PROP_ENUM))
373		printf(" enum");
374	if (drm_property_type_is(prop, DRM_MODE_PROP_BITMASK))
375		printf(" bitmask");
376	if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB))
377		printf(" blob");
378	if (drm_property_type_is(prop, DRM_MODE_PROP_OBJECT))
379		printf(" object");
380	printf("\n");
381
382	if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE)) {
383		printf("\t\tvalues:");
384		for (i = 0; i < prop->count_values; i++)
385			printf(" %"PRId64, U642I64(prop->values[i]));
386		printf("\n");
387	}
388
389	if (drm_property_type_is(prop, DRM_MODE_PROP_RANGE)) {
390		printf("\t\tvalues:");
391		for (i = 0; i < prop->count_values; i++)
392			printf(" %"PRIu64, prop->values[i]);
393		printf("\n");
394	}
395
396	if (drm_property_type_is(prop, DRM_MODE_PROP_ENUM)) {
397		printf("\t\tenums:");
398		for (i = 0; i < prop->count_enums; i++)
399			printf(" %s=%llu", prop->enums[i].name,
400			       prop->enums[i].value);
401		printf("\n");
402	} else if (drm_property_type_is(prop, DRM_MODE_PROP_BITMASK)) {
403		printf("\t\tvalues:");
404		for (i = 0; i < prop->count_enums; i++)
405			printf(" %s=0x%llx", prop->enums[i].name,
406			       (1LL << prop->enums[i].value));
407		printf("\n");
408	} else {
409		assert(prop->count_enums == 0);
410	}
411
412	if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB)) {
413		printf("\t\tblobs:\n");
414		for (i = 0; i < prop->count_blobs; i++)
415			dump_blob(dev, prop->blob_ids[i]);
416		printf("\n");
417	} else {
418		assert(prop->count_blobs == 0);
419	}
420
421	printf("\t\tvalue:");
422	if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB))
423		dump_blob(dev, value);
424	else if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE))
425		printf(" %"PRId64"\n", value);
426	else
427		printf(" %"PRIu64"\n", value);
428
429	if (strcmp(prop->name, "IN_FORMATS") == 0)
430		dump_in_formats(dev, value);
431}
432
433static void dump_connectors(struct device *dev)
434{
435	int i, j;
436
437	printf("Connectors:\n");
438	printf("id\tencoder\tstatus\t\tname\t\tsize (mm)\tmodes\tencoders\n");
439	for (i = 0; i < dev->resources->count_connectors; i++) {
440		struct connector *_connector = &dev->resources->connectors[i];
441		drmModeConnector *connector = _connector->connector;
442		if (!connector)
443			continue;
444
445		printf("%d\t%d\t%s\t%-15s\t%dx%d\t\t%d\t",
446		       connector->connector_id,
447		       connector->encoder_id,
448		       util_lookup_connector_status_name(connector->connection),
449		       _connector->name,
450		       connector->mmWidth, connector->mmHeight,
451		       connector->count_modes);
452
453		for (j = 0; j < connector->count_encoders; j++)
454			printf("%s%d", j > 0 ? ", " : "", connector->encoders[j]);
455		printf("\n");
456
457		if (connector->count_modes) {
458			printf("  modes:\n");
459			printf("\tindex name refresh (Hz) hdisp hss hse htot vdisp "
460			       "vss vse vtot)\n");
461			for (j = 0; j < connector->count_modes; j++)
462				dump_mode(&connector->modes[j], j);
463		}
464
465		if (_connector->props) {
466			printf("  props:\n");
467			for (j = 0; j < (int)_connector->props->count_props; j++)
468				dump_prop(dev, _connector->props_info[j],
469					  _connector->props->props[j],
470					  _connector->props->prop_values[j]);
471		}
472	}
473	printf("\n");
474}
475
476static void dump_crtcs(struct device *dev)
477{
478	int i;
479	uint32_t j;
480
481	printf("CRTCs:\n");
482	printf("id\tfb\tpos\tsize\n");
483	for (i = 0; i < dev->resources->count_crtcs; i++) {
484		struct crtc *_crtc = &dev->resources->crtcs[i];
485		drmModeCrtc *crtc = _crtc->crtc;
486		if (!crtc)
487			continue;
488
489		printf("%d\t%d\t(%d,%d)\t(%dx%d)\n",
490		       crtc->crtc_id,
491		       crtc->buffer_id,
492		       crtc->x, crtc->y,
493		       crtc->width, crtc->height);
494		dump_mode(&crtc->mode, 0);
495
496		if (_crtc->props) {
497			printf("  props:\n");
498			for (j = 0; j < _crtc->props->count_props; j++)
499				dump_prop(dev, _crtc->props_info[j],
500					  _crtc->props->props[j],
501					  _crtc->props->prop_values[j]);
502		} else {
503			printf("  no properties found\n");
504		}
505	}
506	printf("\n");
507}
508
509static void dump_framebuffers(struct device *dev)
510{
511	drmModeFB *fb;
512	int i;
513
514	printf("Frame buffers:\n");
515	printf("id\tsize\tpitch\n");
516	for (i = 0; i < dev->resources->count_fbs; i++) {
517		fb = dev->resources->fbs[i].fb;
518		if (!fb)
519			continue;
520
521		printf("%u\t(%ux%u)\t%u\n",
522		       fb->fb_id,
523		       fb->width, fb->height,
524		       fb->pitch);
525	}
526	printf("\n");
527}
528
529static void dump_planes(struct device *dev)
530{
531	unsigned int i, j;
532
533	printf("Planes:\n");
534	printf("id\tcrtc\tfb\tCRTC x,y\tx,y\tgamma size\tpossible crtcs\n");
535
536	for (i = 0; i < dev->resources->count_planes; i++) {
537		struct plane *plane = &dev->resources->planes[i];
538		drmModePlane *ovr = plane->plane;
539		if (!ovr)
540			continue;
541
542		printf("%d\t%d\t%d\t%d,%d\t\t%d,%d\t%-8d\t0x%08x\n",
543		       ovr->plane_id, ovr->crtc_id, ovr->fb_id,
544		       ovr->crtc_x, ovr->crtc_y, ovr->x, ovr->y,
545		       ovr->gamma_size, ovr->possible_crtcs);
546
547		if (!ovr->count_formats)
548			continue;
549
550		printf("  formats:");
551		for (j = 0; j < ovr->count_formats; j++)
552			dump_fourcc(ovr->formats[j]);
553		printf("\n");
554
555		if (plane->props) {
556			printf("  props:\n");
557			for (j = 0; j < plane->props->count_props; j++)
558				dump_prop(dev, plane->props_info[j],
559					  plane->props->props[j],
560					  plane->props->prop_values[j]);
561		} else {
562			printf("  no properties found\n");
563		}
564	}
565	printf("\n");
566
567	return;
568}
569
570static void free_resources(struct resources *res)
571{
572	int i;
573
574	if (!res)
575		return;
576
577#define free_resource(_res, type, Type)					\
578	do {									\
579		if (!(_res)->type##s)						\
580			break;							\
581		for (i = 0; i < (int)(_res)->count_##type##s; ++i) {	\
582			if (!(_res)->type##s[i].type)				\
583				break;						\
584			drmModeFree##Type((_res)->type##s[i].type);		\
585		}								\
586		free((_res)->type##s);						\
587	} while (0)
588
589#define free_properties(_res, type)					\
590	do {									\
591		for (i = 0; i < (int)(_res)->count_##type##s; ++i) {	\
592			unsigned int j;										\
593			for (j = 0; j < res->type##s[i].props->count_props; ++j)\
594				drmModeFreeProperty(res->type##s[i].props_info[j]);\
595			free(res->type##s[i].props_info);			\
596			drmModeFreeObjectProperties(res->type##s[i].props);	\
597		}								\
598	} while (0)
599
600	free_properties(res, plane);
601	free_resource(res, plane, Plane);
602
603	free_properties(res, connector);
604	free_properties(res, crtc);
605
606	for (i = 0; i < res->count_connectors; i++)
607		free(res->connectors[i].name);
608
609	free_resource(res, fb, FB);
610	free_resource(res, connector, Connector);
611	free_resource(res, encoder, Encoder);
612	free_resource(res, crtc, Crtc);
613
614	free(res);
615}
616
617static struct resources *get_resources(struct device *dev)
618{
619	drmModeRes *_res;
620	drmModePlaneRes *plane_res;
621	struct resources *res;
622	int i;
623
624	res = calloc(1, sizeof(*res));
625	if (res == 0)
626		return NULL;
627
628	drmSetClientCap(dev->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
629
630	_res = drmModeGetResources(dev->fd);
631	if (!_res) {
632		fprintf(stderr, "drmModeGetResources failed: %s\n",
633			strerror(errno));
634		free(res);
635		return NULL;
636	}
637
638	res->count_crtcs = _res->count_crtcs;
639	res->count_encoders = _res->count_encoders;
640	res->count_connectors = _res->count_connectors;
641	res->count_fbs = _res->count_fbs;
642
643	res->crtcs = calloc(res->count_crtcs, sizeof(*res->crtcs));
644	res->encoders = calloc(res->count_encoders, sizeof(*res->encoders));
645	res->connectors = calloc(res->count_connectors, sizeof(*res->connectors));
646	res->fbs = calloc(res->count_fbs, sizeof(*res->fbs));
647
648	if (!res->crtcs || !res->encoders || !res->connectors || !res->fbs) {
649	    drmModeFreeResources(_res);
650		goto error;
651    }
652
653#define get_resource(_res, __res, type, Type)					\
654	do {									\
655		for (i = 0; i < (int)(_res)->count_##type##s; ++i) {	\
656			uint32_t type##id = (__res)->type##s[i];			\
657			(_res)->type##s[i].type =							\
658				drmModeGet##Type(dev->fd, type##id);			\
659			if (!(_res)->type##s[i].type)						\
660				fprintf(stderr, "could not get %s %i: %s\n",	\
661					#type, type##id,							\
662					strerror(errno));			\
663		}								\
664	} while (0)
665
666	get_resource(res, _res, crtc, Crtc);
667	get_resource(res, _res, encoder, Encoder);
668	get_resource(res, _res, connector, Connector);
669	get_resource(res, _res, fb, FB);
670
671	drmModeFreeResources(_res);
672
673	/* Set the name of all connectors based on the type name and the per-type ID. */
674	for (i = 0; i < res->count_connectors; i++) {
675		struct connector *connector = &res->connectors[i];
676		drmModeConnector *conn = connector->connector;
677		int num;
678
679		num = asprintf(&connector->name, "%s-%u",
680			 util_lookup_connector_type_name(conn->connector_type),
681			 conn->connector_type_id);
682		if (num < 0)
683			goto error;
684	}
685
686#define get_properties(_res, type, Type)					\
687	do {									\
688		for (i = 0; i < (int)(_res)->count_##type##s; ++i) {	\
689			struct type *obj = &res->type##s[i];			\
690			unsigned int j;						\
691			obj->props =						\
692				drmModeObjectGetProperties(dev->fd, obj->type->type##_id, \
693							   DRM_MODE_OBJECT_##Type); \
694			if (!obj->props) {					\
695				fprintf(stderr,					\
696					"could not get %s %i properties: %s\n", \
697					#type, obj->type->type##_id,		\
698					strerror(errno));			\
699				continue;					\
700			}							\
701			obj->props_info = calloc(obj->props->count_props,	\
702						 sizeof(*obj->props_info));	\
703			if (!obj->props_info)					\
704				continue;					\
705			for (j = 0; j < obj->props->count_props; ++j)		\
706				obj->props_info[j] =				\
707					drmModeGetProperty(dev->fd, obj->props->props[j]); \
708		}								\
709	} while (0)
710
711	get_properties(res, crtc, CRTC);
712	get_properties(res, connector, CONNECTOR);
713
714	for (i = 0; i < res->count_crtcs; ++i)
715		res->crtcs[i].mode = &res->crtcs[i].crtc->mode;
716
717	plane_res = drmModeGetPlaneResources(dev->fd);
718	if (!plane_res) {
719		fprintf(stderr, "drmModeGetPlaneResources failed: %s\n",
720			strerror(errno));
721		return res;
722	}
723
724	res->count_planes = plane_res->count_planes;
725
726	res->planes = calloc(res->count_planes, sizeof(*res->planes));
727	if (!res->planes) {
728		drmModeFreePlaneResources(plane_res);
729		goto error;
730	}
731
732	get_resource(res, plane_res, plane, Plane);
733	drmModeFreePlaneResources(plane_res);
734	get_properties(res, plane, PLANE);
735
736	return res;
737
738error:
739	free_resources(res);
740	return NULL;
741}
742
743static struct crtc *get_crtc_by_id(struct device *dev, uint32_t id)
744{
745	int i;
746
747	for (i = 0; i < dev->resources->count_crtcs; ++i) {
748		drmModeCrtc *crtc = dev->resources->crtcs[i].crtc;
749		if (crtc && crtc->crtc_id == id)
750			return &dev->resources->crtcs[i];
751	}
752
753	return NULL;
754}
755
756static uint32_t get_crtc_mask(struct device *dev, struct crtc *crtc)
757{
758	unsigned int i;
759
760	for (i = 0; i < (unsigned int)dev->resources->count_crtcs; i++) {
761		if (crtc->crtc->crtc_id == dev->resources->crtcs[i].crtc->crtc_id)
762			return 1 << i;
763	}
764    /* Unreachable: crtc->crtc is one of resources->crtcs[] */
765    /* Don't return zero or static analysers will complain */
766	abort();
767	return 0;
768}
769
770static drmModeConnector *get_connector_by_name(struct device *dev, const char *name)
771{
772	struct connector *connector;
773	int i;
774
775	for (i = 0; i < dev->resources->count_connectors; i++) {
776		connector = &dev->resources->connectors[i];
777
778		if (strcmp(connector->name, name) == 0)
779			return connector->connector;
780	}
781
782	return NULL;
783}
784
785static drmModeConnector *get_connector_by_id(struct device *dev, uint32_t id)
786{
787	drmModeConnector *connector;
788	int i;
789
790	for (i = 0; i < dev->resources->count_connectors; i++) {
791		connector = dev->resources->connectors[i].connector;
792		if (connector && connector->connector_id == id)
793			return connector;
794	}
795
796	return NULL;
797}
798
799static drmModeEncoder *get_encoder_by_id(struct device *dev, uint32_t id)
800{
801	drmModeEncoder *encoder;
802	int i;
803
804	for (i = 0; i < dev->resources->count_encoders; i++) {
805		encoder = dev->resources->encoders[i].encoder;
806		if (encoder && encoder->encoder_id == id)
807			return encoder;
808	}
809
810	return NULL;
811}
812
813/* -----------------------------------------------------------------------------
814 * Pipes and planes
815 */
816
817/*
818 * Mode setting with the kernel interfaces is a bit of a chore.
819 * First you have to find the connector in question and make sure the
820 * requested mode is available.
821 * Then you need to find the encoder attached to that connector so you
822 * can bind it with a free crtc.
823 */
824struct pipe_arg {
825	const char **cons;
826	uint32_t *con_ids;
827	unsigned int num_cons;
828	uint32_t crtc_id;
829	char mode_str[64];
830	char format_str[5];
831	float vrefresh;
832	unsigned int fourcc;
833	drmModeModeInfo *mode;
834	struct crtc *crtc;
835	unsigned int fb_id[2], current_fb_id;
836	struct timeval start;
837
838	int swap_count;
839};
840
841struct plane_arg {
842	uint32_t plane_id;  /* the id of plane to use */
843	uint32_t crtc_id;  /* the id of CRTC to bind to */
844	bool has_position;
845	int32_t x, y;
846	uint32_t w, h;
847	double scale;
848	unsigned int fb_id;
849	unsigned int old_fb_id;
850	struct bo *bo;
851	struct bo *old_bo;
852	char format_str[5]; /* need to leave room for terminating \0 */
853	unsigned int fourcc;
854};
855
856static drmModeModeInfo *
857connector_find_mode(struct device *dev, uint32_t con_id, const char *mode_str,
858	const float vrefresh)
859{
860	drmModeConnector *connector;
861	drmModeModeInfo *mode;
862	int i;
863
864	connector = get_connector_by_id(dev, con_id);
865	if (!connector || !connector->count_modes)
866		return NULL;
867
868	/* Pick by Index */
869	if (mode_str[0] == '#') {
870		int index = atoi(mode_str + 1);
871
872		if (index >= connector->count_modes || index < 0)
873			return NULL;
874		return &connector->modes[index];
875	}
876
877	/* Pick by Name */
878	for (i = 0; i < connector->count_modes; i++) {
879		mode = &connector->modes[i];
880		if (!strcmp(mode->name, mode_str)) {
881			/* If the vertical refresh frequency is not specified
882			 * then return the first mode that match with the name.
883			 * Else, return the mode that match the name and
884			 * the specified vertical refresh frequency.
885			 */
886			if (vrefresh == 0)
887				return mode;
888			else if (fabs(mode_vrefresh(mode) - vrefresh) < 0.005)
889				return mode;
890		}
891	}
892
893	return NULL;
894}
895
896static struct crtc *pipe_find_crtc(struct device *dev, struct pipe_arg *pipe)
897{
898	uint32_t possible_crtcs = ~0;
899	uint32_t active_crtcs = 0;
900	unsigned int crtc_idx;
901	unsigned int i;
902	int j;
903
904	for (i = 0; i < pipe->num_cons; ++i) {
905		uint32_t crtcs_for_connector = 0;
906		drmModeConnector *connector;
907		drmModeEncoder *encoder;
908		struct crtc *crtc;
909
910		connector = get_connector_by_id(dev, pipe->con_ids[i]);
911		if (!connector)
912			return NULL;
913
914		for (j = 0; j < connector->count_encoders; ++j) {
915			encoder = get_encoder_by_id(dev, connector->encoders[j]);
916			if (!encoder)
917				continue;
918
919			crtcs_for_connector |= encoder->possible_crtcs;
920			crtc = get_crtc_by_id(dev, encoder->crtc_id);
921			if (!crtc)
922				continue;
923			active_crtcs |= get_crtc_mask(dev, crtc);
924		}
925
926		possible_crtcs &= crtcs_for_connector;
927	}
928
929	if (!possible_crtcs)
930		return NULL;
931
932	/* Return the first possible and active CRTC if one exists, or the first
933	 * possible CRTC otherwise.
934	 */
935	if (possible_crtcs & active_crtcs)
936		crtc_idx = ffs(possible_crtcs & active_crtcs);
937	else
938		crtc_idx = ffs(possible_crtcs);
939
940	return &dev->resources->crtcs[crtc_idx - 1];
941}
942
943static int pipe_find_crtc_and_mode(struct device *dev, struct pipe_arg *pipe)
944{
945	drmModeModeInfo *mode = NULL;
946	int i;
947
948	pipe->mode = NULL;
949
950	for (i = 0; i < (int)pipe->num_cons; i++) {
951		mode = connector_find_mode(dev, pipe->con_ids[i],
952					   pipe->mode_str, pipe->vrefresh);
953		if (mode == NULL) {
954			if (pipe->vrefresh)
955				fprintf(stderr,
956				"failed to find mode "
957				"\"%s-%.2fHz\" for connector %s\n",
958				pipe->mode_str, pipe->vrefresh, pipe->cons[i]);
959			else
960				fprintf(stderr,
961				"failed to find mode \"%s\" for connector %s\n",
962				pipe->mode_str, pipe->cons[i]);
963			return -EINVAL;
964		}
965	}
966
967	/* If the CRTC ID was specified, get the corresponding CRTC. Otherwise
968	 * locate a CRTC that can be attached to all the connectors.
969	 */
970	if (pipe->crtc_id != (uint32_t)-1) {
971		pipe->crtc = get_crtc_by_id(dev, pipe->crtc_id);
972	} else {
973		pipe->crtc = pipe_find_crtc(dev, pipe);
974		pipe->crtc_id = pipe->crtc->crtc->crtc_id;
975	}
976
977	if (!pipe->crtc) {
978		fprintf(stderr, "failed to find CRTC for pipe\n");
979		return -EINVAL;
980	}
981
982	pipe->mode = mode;
983	pipe->crtc->mode = mode;
984
985	return 0;
986}
987
988/* -----------------------------------------------------------------------------
989 * Properties
990 */
991
992struct property_arg {
993	uint32_t obj_id;
994	uint32_t obj_type;
995	char name[DRM_PROP_NAME_LEN+1];
996	uint32_t prop_id;
997	uint64_t value;
998	bool optional;
999};
1000
1001static bool set_property(struct device *dev, struct property_arg *p)
1002{
1003	drmModeObjectProperties *props = NULL;
1004	drmModePropertyRes **props_info = NULL;
1005	const char *obj_type;
1006	int ret;
1007	int i;
1008
1009	p->obj_type = 0;
1010	p->prop_id = 0;
1011
1012#define find_object(_res, type, Type)					\
1013	do {									\
1014		for (i = 0; i < (int)(_res)->count_##type##s; ++i) {	\
1015			struct type *obj = &(_res)->type##s[i];			\
1016			if (obj->type->type##_id != p->obj_id)			\
1017				continue;					\
1018			p->obj_type = DRM_MODE_OBJECT_##Type;			\
1019			obj_type = #Type;					\
1020			props = obj->props;					\
1021			props_info = obj->props_info;				\
1022		}								\
1023	} while(0)								\
1024
1025	find_object(dev->resources, crtc, CRTC);
1026	if (p->obj_type == 0)
1027		find_object(dev->resources, connector, CONNECTOR);
1028	if (p->obj_type == 0)
1029		find_object(dev->resources, plane, PLANE);
1030	if (p->obj_type == 0) {
1031		fprintf(stderr, "Object %i not found, can't set property\n",
1032			p->obj_id);
1033		return false;
1034	}
1035
1036	if (!props) {
1037		fprintf(stderr, "%s %i has no properties\n",
1038			obj_type, p->obj_id);
1039		return false;
1040	}
1041
1042	for (i = 0; i < (int)props->count_props; ++i) {
1043		if (!props_info[i])
1044			continue;
1045		if (strcmp(props_info[i]->name, p->name) == 0)
1046			break;
1047	}
1048
1049	if (i == (int)props->count_props) {
1050		if (!p->optional)
1051			fprintf(stderr, "%s %i has no %s property\n",
1052				obj_type, p->obj_id, p->name);
1053		return false;
1054	}
1055
1056	p->prop_id = props->props[i];
1057
1058	if (!dev->use_atomic)
1059		ret = drmModeObjectSetProperty(dev->fd, p->obj_id, p->obj_type,
1060									   p->prop_id, p->value);
1061	else
1062		ret = drmModeAtomicAddProperty(dev->req, p->obj_id, p->prop_id, p->value);
1063
1064	if (ret < 0)
1065		fprintf(stderr, "failed to set %s %i property %s to %" PRIu64 ": %s\n",
1066			obj_type, p->obj_id, p->name, p->value, strerror(errno));
1067
1068	return true;
1069}
1070
1071/* -------------------------------------------------------------------------- */
1072
1073static void
1074page_flip_handler(int fd, unsigned int frame,
1075		  unsigned int sec, unsigned int usec, void *data)
1076{
1077	struct pipe_arg *pipe;
1078	unsigned int new_fb_id;
1079	struct timeval end;
1080	double t;
1081
1082	pipe = data;
1083	if (pipe->current_fb_id == pipe->fb_id[0])
1084		new_fb_id = pipe->fb_id[1];
1085	else
1086		new_fb_id = pipe->fb_id[0];
1087
1088	drmModePageFlip(fd, pipe->crtc_id, new_fb_id,
1089			DRM_MODE_PAGE_FLIP_EVENT, pipe);
1090	pipe->current_fb_id = new_fb_id;
1091	pipe->swap_count++;
1092	if (pipe->swap_count == 60) {
1093		gettimeofday(&end, NULL);
1094		t = end.tv_sec + end.tv_usec * 1e-6 -
1095			(pipe->start.tv_sec + pipe->start.tv_usec * 1e-6);
1096		fprintf(stderr, "freq: %.02fHz\n", pipe->swap_count / t);
1097		pipe->swap_count = 0;
1098		pipe->start = end;
1099	}
1100}
1101
1102static bool format_support(const drmModePlanePtr ovr, uint32_t fmt)
1103{
1104	unsigned int i;
1105
1106	for (i = 0; i < ovr->count_formats; ++i) {
1107		if (ovr->formats[i] == fmt)
1108			return true;
1109	}
1110
1111	return false;
1112}
1113
1114static void add_property(struct device *dev, uint32_t obj_id,
1115			       const char *name, uint64_t value)
1116{
1117	struct property_arg p;
1118
1119	p.obj_id = obj_id;
1120	strcpy(p.name, name);
1121	p.value = value;
1122
1123	set_property(dev, &p);
1124}
1125
1126static bool add_property_optional(struct device *dev, uint32_t obj_id,
1127				  const char *name, uint64_t value)
1128{
1129	struct property_arg p;
1130
1131	p.obj_id = obj_id;
1132	strcpy(p.name, name);
1133	p.value = value;
1134	p.optional = true;
1135
1136	return set_property(dev, &p);
1137}
1138
1139static void set_gamma(struct device *dev, unsigned crtc_id, unsigned fourcc)
1140{
1141	unsigned blob_id = 0;
1142	/* TODO: support 1024-sized LUTs, when the use-case arises */
1143	struct drm_color_lut gamma_lut[256];
1144	int i, ret;
1145
1146	if (fourcc == DRM_FORMAT_C8) {
1147		/* TODO: Add C8 support for more patterns */
1148		util_smpte_c8_gamma(256, gamma_lut);
1149		drmModeCreatePropertyBlob(dev->fd, gamma_lut, sizeof(gamma_lut), &blob_id);
1150	} else {
1151		for (i = 0; i < 256; i++) {
1152			gamma_lut[i].red =
1153			gamma_lut[i].green =
1154			gamma_lut[i].blue = i << 8;
1155		}
1156	}
1157
1158	add_property_optional(dev, crtc_id, "DEGAMMA_LUT", 0);
1159	add_property_optional(dev, crtc_id, "CTM", 0);
1160	if (!add_property_optional(dev, crtc_id, "GAMMA_LUT", blob_id)) {
1161		uint16_t r[256], g[256], b[256];
1162
1163		for (i = 0; i < 256; i++) {
1164			r[i] = gamma_lut[i].red;
1165			g[i] = gamma_lut[i].green;
1166			b[i] = gamma_lut[i].blue;
1167		}
1168
1169		ret = drmModeCrtcSetGamma(dev->fd, crtc_id, 256, r, g, b);
1170		if (ret)
1171			fprintf(stderr, "failed to set gamma: %s\n", strerror(errno));
1172	}
1173}
1174
1175static int
1176bo_fb_create(int fd, unsigned int fourcc, const uint32_t w, const uint32_t h,
1177             enum util_fill_pattern pat, struct bo **out_bo, unsigned int *out_fb_id)
1178{
1179	uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
1180	struct bo *bo;
1181	unsigned int fb_id;
1182
1183	bo = bo_create(fd, fourcc, w, h, handles, pitches, offsets, pat);
1184
1185	if (bo == NULL)
1186		return -1;
1187
1188	if (drmModeAddFB2(fd, w, h, fourcc, handles, pitches, offsets, &fb_id, 0)) {
1189		fprintf(stderr, "failed to add fb (%ux%u): %s\n", w, h, strerror(errno));
1190		bo_destroy(bo);
1191		return -1;
1192	}
1193	*out_bo = bo;
1194	*out_fb_id = fb_id;
1195	return 0;
1196}
1197
1198static int atomic_set_plane(struct device *dev, struct plane_arg *p,
1199							int pattern, bool update)
1200{
1201	struct bo *plane_bo;
1202	int crtc_x, crtc_y, crtc_w, crtc_h;
1203	struct crtc *crtc = NULL;
1204	unsigned int old_fb_id;
1205
1206	/* Find an unused plane which can be connected to our CRTC. Find the
1207	 * CRTC index first, then iterate over available planes.
1208	 */
1209	crtc = get_crtc_by_id(dev, p->crtc_id);
1210	if (!crtc) {
1211		fprintf(stderr, "CRTC %u not found\n", p->crtc_id);
1212		return -1;
1213	}
1214
1215	if (!update)
1216		fprintf(stderr, "testing %dx%d@%s on plane %u, crtc %u\n",
1217			p->w, p->h, p->format_str, p->plane_id, p->crtc_id);
1218
1219	plane_bo = p->old_bo;
1220	p->old_bo = p->bo;
1221
1222	if (!plane_bo) {
1223		if (bo_fb_create(dev->fd, p->fourcc, p->w, p->h,
1224                         pattern, &plane_bo, &p->fb_id))
1225			return -1;
1226	}
1227
1228	p->bo = plane_bo;
1229
1230	old_fb_id = p->fb_id;
1231	p->old_fb_id = old_fb_id;
1232
1233	crtc_w = p->w * p->scale;
1234	crtc_h = p->h * p->scale;
1235	if (!p->has_position) {
1236		/* Default to the middle of the screen */
1237		crtc_x = (crtc->mode->hdisplay - crtc_w) / 2;
1238		crtc_y = (crtc->mode->vdisplay - crtc_h) / 2;
1239	} else {
1240		crtc_x = p->x;
1241		crtc_y = p->y;
1242	}
1243
1244	add_property(dev, p->plane_id, "FB_ID", p->fb_id);
1245	add_property(dev, p->plane_id, "CRTC_ID", p->crtc_id);
1246	add_property(dev, p->plane_id, "SRC_X", 0);
1247	add_property(dev, p->plane_id, "SRC_Y", 0);
1248	add_property(dev, p->plane_id, "SRC_W", p->w << 16);
1249	add_property(dev, p->plane_id, "SRC_H", p->h << 16);
1250	add_property(dev, p->plane_id, "CRTC_X", crtc_x);
1251	add_property(dev, p->plane_id, "CRTC_Y", crtc_y);
1252	add_property(dev, p->plane_id, "CRTC_W", crtc_w);
1253	add_property(dev, p->plane_id, "CRTC_H", crtc_h);
1254
1255	return 0;
1256}
1257
1258static int set_plane(struct device *dev, struct plane_arg *p)
1259{
1260	drmModePlane *ovr;
1261	uint32_t plane_id;
1262	int crtc_x, crtc_y, crtc_w, crtc_h;
1263	struct crtc *crtc = NULL;
1264	unsigned int i, crtc_mask;
1265
1266	/* Find an unused plane which can be connected to our CRTC. Find the
1267	 * CRTC index first, then iterate over available planes.
1268	 */
1269	crtc = get_crtc_by_id(dev, p->crtc_id);
1270	if (!crtc) {
1271		fprintf(stderr, "CRTC %u not found\n", p->crtc_id);
1272		return -1;
1273	}
1274	crtc_mask = get_crtc_mask(dev, crtc);
1275	plane_id = p->plane_id;
1276
1277	for (i = 0; i < dev->resources->count_planes; i++) {
1278		ovr = dev->resources->planes[i].plane;
1279		if (!ovr)
1280			continue;
1281
1282		if (plane_id && plane_id != ovr->plane_id)
1283			continue;
1284
1285		if (!format_support(ovr, p->fourcc))
1286			continue;
1287
1288		if ((ovr->possible_crtcs & crtc_mask) &&
1289		    (ovr->crtc_id == 0 || ovr->crtc_id == p->crtc_id)) {
1290			plane_id = ovr->plane_id;
1291			break;
1292		}
1293	}
1294
1295	if (i == dev->resources->count_planes) {
1296		fprintf(stderr, "no unused plane available for CRTC %u\n",
1297			p->crtc_id);
1298		return -1;
1299	}
1300
1301	fprintf(stderr, "testing %dx%d@%s overlay plane %u\n",
1302		p->w, p->h, p->format_str, plane_id);
1303
1304	/* just use single plane format for now.. */
1305	if (bo_fb_create(dev->fd, p->fourcc, p->w, p->h,
1306	                 secondary_fill, &p->bo, &p->fb_id))
1307		return -1;
1308
1309	crtc_w = p->w * p->scale;
1310	crtc_h = p->h * p->scale;
1311	if (!p->has_position) {
1312		/* Default to the middle of the screen */
1313		crtc_x = (crtc->mode->hdisplay - crtc_w) / 2;
1314		crtc_y = (crtc->mode->vdisplay - crtc_h) / 2;
1315	} else {
1316		crtc_x = p->x;
1317		crtc_y = p->y;
1318	}
1319
1320	/* note src coords (last 4 args) are in Q16 format */
1321	if (drmModeSetPlane(dev->fd, plane_id, p->crtc_id, p->fb_id,
1322			    0, crtc_x, crtc_y, crtc_w, crtc_h,
1323			    0, 0, p->w << 16, p->h << 16)) {
1324		fprintf(stderr, "failed to enable plane: %s\n",
1325			strerror(errno));
1326		return -1;
1327	}
1328
1329	ovr->crtc_id = p->crtc_id;
1330
1331	return 0;
1332}
1333
1334static void atomic_set_planes(struct device *dev, struct plane_arg *p,
1335			      unsigned int count, bool update)
1336{
1337	unsigned int i, pattern = primary_fill;
1338
1339	/* set up planes */
1340	for (i = 0; i < count; i++) {
1341		if (i > 0)
1342			pattern = secondary_fill;
1343		else
1344			set_gamma(dev, p[i].crtc_id, p[i].fourcc);
1345
1346		if (atomic_set_plane(dev, &p[i], pattern, update))
1347			return;
1348	}
1349}
1350
1351static void
1352atomic_test_page_flip(struct device *dev, struct pipe_arg *pipe_args,
1353              struct plane_arg *plane_args, unsigned int plane_count)
1354{
1355    int ret;
1356
1357	gettimeofday(&pipe_args->start, NULL);
1358	pipe_args->swap_count = 0;
1359
1360	while (true) {
1361		drmModeAtomicFree(dev->req);
1362		dev->req = drmModeAtomicAlloc();
1363		atomic_set_planes(dev, plane_args, plane_count, true);
1364
1365		ret = drmModeAtomicCommit(dev->fd, dev->req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
1366		if (ret) {
1367			fprintf(stderr, "Atomic Commit failed [2]\n");
1368			return;
1369		}
1370
1371		pipe_args->swap_count++;
1372		if (pipe_args->swap_count == 60) {
1373			struct timeval end;
1374			double t;
1375
1376			gettimeofday(&end, NULL);
1377			t = end.tv_sec + end.tv_usec * 1e-6 -
1378			    (pipe_args->start.tv_sec + pipe_args->start.tv_usec * 1e-6);
1379			fprintf(stderr, "freq: %.02fHz\n", pipe_args->swap_count / t);
1380			pipe_args->swap_count = 0;
1381			pipe_args->start = end;
1382		}
1383	}
1384}
1385
1386static void atomic_clear_planes(struct device *dev, struct plane_arg *p, unsigned int count)
1387{
1388	unsigned int i;
1389
1390	for (i = 0; i < count; i++) {
1391		add_property(dev, p[i].plane_id, "FB_ID", 0);
1392		add_property(dev, p[i].plane_id, "CRTC_ID", 0);
1393		add_property(dev, p[i].plane_id, "SRC_X", 0);
1394		add_property(dev, p[i].plane_id, "SRC_Y", 0);
1395		add_property(dev, p[i].plane_id, "SRC_W", 0);
1396		add_property(dev, p[i].plane_id, "SRC_H", 0);
1397		add_property(dev, p[i].plane_id, "CRTC_X", 0);
1398		add_property(dev, p[i].plane_id, "CRTC_Y", 0);
1399		add_property(dev, p[i].plane_id, "CRTC_W", 0);
1400		add_property(dev, p[i].plane_id, "CRTC_H", 0);
1401	}
1402}
1403
1404static void atomic_clear_FB(struct device *dev, struct plane_arg *p, unsigned int count)
1405{
1406	unsigned int i;
1407
1408	for (i = 0; i < count; i++) {
1409		if (p[i].fb_id) {
1410			drmModeRmFB(dev->fd, p[i].fb_id);
1411			p[i].fb_id = 0;
1412		}
1413		if (p[i].old_fb_id) {
1414			drmModeRmFB(dev->fd, p[i].old_fb_id);
1415			p[i].old_fb_id = 0;
1416		}
1417		if (p[i].bo) {
1418			bo_destroy(p[i].bo);
1419			p[i].bo = NULL;
1420		}
1421		if (p[i].old_bo) {
1422			bo_destroy(p[i].old_bo);
1423			p[i].old_bo = NULL;
1424		}
1425
1426	}
1427}
1428
1429static void clear_planes(struct device *dev, struct plane_arg *p, unsigned int count)
1430{
1431	unsigned int i;
1432
1433	for (i = 0; i < count; i++) {
1434		if (p[i].fb_id)
1435			drmModeRmFB(dev->fd, p[i].fb_id);
1436		if (p[i].bo)
1437			bo_destroy(p[i].bo);
1438	}
1439}
1440
1441static int pipe_resolve_connectors(struct device *dev, struct pipe_arg *pipe)
1442{
1443	drmModeConnector *connector;
1444	unsigned int i;
1445	uint32_t id;
1446	char *endp;
1447
1448	for (i = 0; i < pipe->num_cons; i++) {
1449		id = strtoul(pipe->cons[i], &endp, 10);
1450		if (endp == pipe->cons[i]) {
1451			connector = get_connector_by_name(dev, pipe->cons[i]);
1452			if (!connector) {
1453				fprintf(stderr, "no connector named '%s'\n",
1454					pipe->cons[i]);
1455				return -ENODEV;
1456			}
1457
1458			id = connector->connector_id;
1459		}
1460
1461		pipe->con_ids[i] = id;
1462	}
1463
1464	return 0;
1465}
1466
1467static int pipe_attempt_connector(struct device *dev, drmModeConnector *con,
1468		struct pipe_arg *pipe)
1469{
1470	char *con_str;
1471	int i;
1472
1473	con_str = calloc(8, sizeof(char));
1474	if (!con_str)
1475		return -1;
1476
1477	sprintf(con_str, "%d", con->connector_id);
1478	strcpy(pipe->format_str, "XR24");
1479	pipe->fourcc = util_format_fourcc(pipe->format_str);
1480	pipe->num_cons = 1;
1481	pipe->con_ids = calloc(1, sizeof(*pipe->con_ids));
1482	pipe->cons = calloc(1, sizeof(*pipe->cons));
1483
1484	if (!pipe->con_ids || !pipe->cons)
1485		goto free_con_str;
1486
1487	pipe->con_ids[0] = con->connector_id;
1488	pipe->cons[0] = (const char*)con_str;
1489
1490	pipe->crtc = pipe_find_crtc(dev, pipe);
1491	if (!pipe->crtc)
1492		goto free_all;
1493
1494	pipe->crtc_id = pipe->crtc->crtc->crtc_id;
1495
1496	/* Return the first mode if no preferred. */
1497	pipe->mode = &con->modes[0];
1498
1499	for (i = 0; i < con->count_modes; i++) {
1500		drmModeModeInfo *current_mode = &con->modes[i];
1501
1502		if (current_mode->type & DRM_MODE_TYPE_PREFERRED) {
1503			pipe->mode = current_mode;
1504			break;
1505		}
1506	}
1507
1508	sprintf(pipe->mode_str, "%dx%d", pipe->mode->hdisplay, pipe->mode->vdisplay);
1509
1510	return 0;
1511
1512free_all:
1513	free(pipe->cons);
1514	free(pipe->con_ids);
1515free_con_str:
1516	free(con_str);
1517	return -1;
1518}
1519
1520static int pipe_find_preferred(struct device *dev, struct pipe_arg **out_pipes)
1521{
1522	struct pipe_arg *pipes;
1523	struct resources *res = dev->resources;
1524	drmModeConnector *con = NULL;
1525	int i, connected = 0, attempted = 0;
1526
1527	for (i = 0; i < res->count_connectors; i++) {
1528		con = res->connectors[i].connector;
1529		if (!con || con->connection != DRM_MODE_CONNECTED)
1530			continue;
1531		connected++;
1532	}
1533	if (!connected) {
1534		printf("no connected connector!\n");
1535		return 0;
1536	}
1537
1538	pipes = calloc(connected, sizeof(struct pipe_arg));
1539	if (!pipes)
1540		return 0;
1541
1542	for (i = 0; i < res->count_connectors && attempted < connected; i++) {
1543		con = res->connectors[i].connector;
1544		if (!con || con->connection != DRM_MODE_CONNECTED)
1545			continue;
1546
1547		if (pipe_attempt_connector(dev, con, &pipes[attempted]) < 0) {
1548			printf("failed fetching preferred mode for connector\n");
1549			continue;
1550		}
1551		attempted++;
1552	}
1553
1554	*out_pipes = pipes;
1555	return attempted;
1556}
1557
1558static struct plane *get_primary_plane_by_crtc(struct device *dev, struct crtc *crtc)
1559{
1560	unsigned int i;
1561
1562	for (i = 0; i < dev->resources->count_planes; i++) {
1563		struct plane *plane = &dev->resources->planes[i];
1564		drmModePlane *ovr = plane->plane;
1565		if (!ovr)
1566			continue;
1567
1568		// XXX: add is_primary_plane and (?) format checks
1569
1570		if (ovr->possible_crtcs & get_crtc_mask(dev, crtc))
1571            return plane;
1572	}
1573	return NULL;
1574}
1575
1576static void set_mode(struct device *dev, struct pipe_arg *pipes, unsigned int count)
1577{
1578	unsigned int i, j;
1579	int ret, x = 0;
1580	int preferred = count == 0;
1581
1582	for (i = 0; i < count; i++) {
1583		struct pipe_arg *pipe = &pipes[i];
1584
1585		ret = pipe_resolve_connectors(dev, pipe);
1586		if (ret < 0)
1587			return;
1588
1589		ret = pipe_find_crtc_and_mode(dev, pipe);
1590		if (ret < 0)
1591			continue;
1592	}
1593	if (preferred) {
1594		struct pipe_arg *pipe_args;
1595
1596		count = pipe_find_preferred(dev, &pipe_args);
1597		if (!count) {
1598			fprintf(stderr, "can't find any preferred connector/mode.\n");
1599			return;
1600		}
1601		pipes = pipe_args;
1602	}
1603
1604	if (!dev->use_atomic) {
1605		for (i = 0; i < count; i++) {
1606			struct pipe_arg *pipe = &pipes[i];
1607
1608			if (pipe->mode == NULL)
1609				continue;
1610
1611			if (!preferred) {
1612				dev->mode.width += pipe->mode->hdisplay;
1613				if (dev->mode.height < pipe->mode->vdisplay)
1614					dev->mode.height = pipe->mode->vdisplay;
1615			} else {
1616				/* XXX: Use a clone mode, more like atomic. We could do per
1617				 * connector bo/fb, so we don't have the stretched image.
1618				 */
1619				if (dev->mode.width < pipe->mode->hdisplay)
1620					dev->mode.width = pipe->mode->hdisplay;
1621				if (dev->mode.height < pipe->mode->vdisplay)
1622					dev->mode.height = pipe->mode->vdisplay;
1623			}
1624		}
1625
1626		if (bo_fb_create(dev->fd, pipes[0].fourcc, dev->mode.width, dev->mode.height,
1627			             primary_fill, &dev->mode.bo, &dev->mode.fb_id))
1628			return;
1629	}
1630
1631	for (i = 0; i < count; i++) {
1632		struct pipe_arg *pipe = &pipes[i];
1633		uint32_t blob_id;
1634
1635		if (pipe->mode == NULL)
1636			continue;
1637
1638		printf("setting mode %s-%.2fHz on connectors ",
1639		       pipe->mode->name, mode_vrefresh(pipe->mode));
1640		for (j = 0; j < pipe->num_cons; ++j) {
1641			printf("%s, ", pipe->cons[j]);
1642			if (dev->use_atomic)
1643				add_property(dev, pipe->con_ids[j], "CRTC_ID", pipe->crtc_id);
1644		}
1645		printf("crtc %d\n", pipe->crtc_id);
1646
1647		if (!dev->use_atomic) {
1648			ret = drmModeSetCrtc(dev->fd, pipe->crtc_id, dev->mode.fb_id,
1649								 x, 0, pipe->con_ids, pipe->num_cons,
1650								 pipe->mode);
1651
1652			/* XXX: Actually check if this is needed */
1653			drmModeDirtyFB(dev->fd, dev->mode.fb_id, NULL, 0);
1654
1655			if (!preferred)
1656				x += pipe->mode->hdisplay;
1657
1658			if (ret) {
1659				fprintf(stderr, "failed to set mode: %s\n", strerror(errno));
1660				return;
1661			}
1662
1663			set_gamma(dev, pipe->crtc_id, pipe->fourcc);
1664		} else {
1665			drmModeCreatePropertyBlob(dev->fd, pipe->mode, sizeof(*pipe->mode), &blob_id);
1666			add_property(dev, pipe->crtc_id, "MODE_ID", blob_id);
1667			add_property(dev, pipe->crtc_id, "ACTIVE", 1);
1668
1669			/* By default atomic modeset does not set a primary plane, shrug */
1670			if (preferred) {
1671				struct plane *plane = get_primary_plane_by_crtc(dev, pipe->crtc);
1672				struct plane_arg plane_args = {
1673					.plane_id = plane->plane->plane_id,
1674					.crtc_id = pipe->crtc_id,
1675					.w = pipe->mode->hdisplay,
1676					.h = pipe->mode->vdisplay,
1677					.scale = 1.0,
1678					.format_str = "XR24",
1679					.fourcc = util_format_fourcc(pipe->format_str),
1680				};
1681
1682				atomic_set_planes(dev, &plane_args, 1, false);
1683			}
1684		}
1685	}
1686}
1687
1688static void atomic_clear_mode(struct device *dev, struct pipe_arg *pipes, unsigned int count)
1689{
1690	unsigned int i;
1691	unsigned int j;
1692
1693	for (i = 0; i < count; i++) {
1694		struct pipe_arg *pipe = &pipes[i];
1695
1696		if (pipe->mode == NULL)
1697			continue;
1698
1699		for (j = 0; j < pipe->num_cons; ++j)
1700			add_property(dev, pipe->con_ids[j], "CRTC_ID",0);
1701
1702		add_property(dev, pipe->crtc_id, "MODE_ID", 0);
1703		add_property(dev, pipe->crtc_id, "ACTIVE", 0);
1704	}
1705}
1706
1707static void clear_mode(struct device *dev)
1708{
1709	if (dev->mode.fb_id)
1710		drmModeRmFB(dev->fd, dev->mode.fb_id);
1711	if (dev->mode.bo)
1712		bo_destroy(dev->mode.bo);
1713}
1714
1715static void set_planes(struct device *dev, struct plane_arg *p, unsigned int count)
1716{
1717	unsigned int i;
1718
1719	/* set up planes/overlays */
1720	for (i = 0; i < count; i++)
1721		if (set_plane(dev, &p[i]))
1722			return;
1723}
1724
1725static void set_cursors(struct device *dev, struct pipe_arg *pipes, unsigned int count)
1726{
1727	uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
1728	struct bo *bo;
1729	unsigned int i;
1730	int ret;
1731
1732	/* maybe make cursor width/height configurable some day */
1733	uint32_t cw = 64;
1734	uint32_t ch = 64;
1735
1736	/* create cursor bo.. just using PATTERN_PLAIN as it has
1737	 * translucent alpha
1738	 */
1739	bo = bo_create(dev->fd, DRM_FORMAT_ARGB8888, cw, ch, handles, pitches,
1740		       offsets, UTIL_PATTERN_PLAIN);
1741	if (bo == NULL)
1742		return;
1743
1744	dev->mode.cursor_bo = bo;
1745
1746	for (i = 0; i < count; i++) {
1747		struct pipe_arg *pipe = &pipes[i];
1748		ret = cursor_init(dev->fd, handles[0],
1749				pipe->crtc_id,
1750				pipe->mode->hdisplay, pipe->mode->vdisplay,
1751				cw, ch);
1752		if (ret) {
1753			fprintf(stderr, "failed to init cursor for CRTC[%u]\n",
1754					pipe->crtc_id);
1755			return;
1756		}
1757	}
1758
1759	cursor_start();
1760}
1761
1762static void clear_cursors(struct device *dev)
1763{
1764	cursor_stop();
1765
1766	if (dev->mode.cursor_bo)
1767		bo_destroy(dev->mode.cursor_bo);
1768}
1769
1770static void test_page_flip(struct device *dev, struct pipe_arg *pipes, unsigned int count)
1771{
1772	unsigned int other_fb_id;
1773	struct bo *other_bo;
1774	drmEventContext evctx;
1775	unsigned int i;
1776	int ret;
1777
1778	if (bo_fb_create(dev->fd, pipes[0].fourcc, dev->mode.width, dev->mode.height,
1779	                 UTIL_PATTERN_PLAIN, &other_bo, &other_fb_id))
1780		return;
1781
1782	for (i = 0; i < count; i++) {
1783		struct pipe_arg *pipe = &pipes[i];
1784
1785		if (pipe->mode == NULL)
1786			continue;
1787
1788		ret = drmModePageFlip(dev->fd, pipe->crtc_id,
1789				      other_fb_id, DRM_MODE_PAGE_FLIP_EVENT,
1790				      pipe);
1791		if (ret) {
1792			fprintf(stderr, "failed to page flip: %s\n", strerror(errno));
1793			goto err_rmfb;
1794		}
1795		gettimeofday(&pipe->start, NULL);
1796		pipe->swap_count = 0;
1797		pipe->fb_id[0] = dev->mode.fb_id;
1798		pipe->fb_id[1] = other_fb_id;
1799		pipe->current_fb_id = other_fb_id;
1800	}
1801
1802	memset(&evctx, 0, sizeof evctx);
1803	evctx.version = DRM_EVENT_CONTEXT_VERSION;
1804	evctx.vblank_handler = NULL;
1805	evctx.page_flip_handler = page_flip_handler;
1806
1807	while (1) {
1808#if 0
1809		struct pollfd pfd[2];
1810
1811		pfd[0].fd = 0;
1812		pfd[0].events = POLLIN;
1813		pfd[1].fd = fd;
1814		pfd[1].events = POLLIN;
1815
1816		if (poll(pfd, 2, -1) < 0) {
1817			fprintf(stderr, "poll error\n");
1818			break;
1819		}
1820
1821		if (pfd[0].revents)
1822			break;
1823#else
1824		struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 };
1825		fd_set fds;
1826
1827		FD_ZERO(&fds);
1828		FD_SET(0, &fds);
1829		FD_SET(dev->fd, &fds);
1830		ret = select(dev->fd + 1, &fds, NULL, NULL, &timeout);
1831
1832		if (ret <= 0) {
1833			fprintf(stderr, "select timed out or error (ret %d)\n",
1834				ret);
1835			continue;
1836		} else if (FD_ISSET(0, &fds)) {
1837			break;
1838		}
1839#endif
1840
1841		drmHandleEvent(dev->fd, &evctx);
1842	}
1843
1844err_rmfb:
1845	drmModeRmFB(dev->fd, other_fb_id);
1846	bo_destroy(other_bo);
1847}
1848
1849#define min(a, b)	((a) < (b) ? (a) : (b))
1850
1851static int parse_connector(struct pipe_arg *pipe, const char *arg)
1852{
1853	unsigned int len;
1854	unsigned int i;
1855	const char *p;
1856	char *endp;
1857
1858	pipe->vrefresh = 0;
1859	pipe->crtc_id = (uint32_t)-1;
1860	strcpy(pipe->format_str, "XR24");
1861
1862	/* Count the number of connectors and allocate them. */
1863	pipe->num_cons = 1;
1864	for (p = arg; *p && *p != ':' && *p != '@'; ++p) {
1865		if (*p == ',')
1866			pipe->num_cons++;
1867	}
1868
1869	pipe->con_ids = calloc(pipe->num_cons, sizeof(*pipe->con_ids));
1870	pipe->cons = calloc(pipe->num_cons, sizeof(*pipe->cons));
1871	if (pipe->con_ids == NULL || pipe->cons == NULL)
1872		return -1;
1873
1874	/* Parse the connectors. */
1875	for (i = 0, p = arg; i < pipe->num_cons; ++i, p = endp + 1) {
1876		endp = strpbrk(p, ",@:");
1877		if (!endp)
1878			break;
1879
1880		pipe->cons[i] = strndup(p, endp - p);
1881
1882		if (*endp != ',')
1883			break;
1884	}
1885
1886	if (i != pipe->num_cons - 1)
1887		return -1;
1888
1889	/* Parse the remaining parameters. */
1890	if (!endp)
1891		return -1;
1892	if (*endp == '@') {
1893		arg = endp + 1;
1894		pipe->crtc_id = strtoul(arg, &endp, 10);
1895	}
1896	if (*endp != ':')
1897		return -1;
1898
1899	arg = endp + 1;
1900
1901	/* Search for the vertical refresh or the format. */
1902	p = strpbrk(arg, "-@");
1903	if (p == NULL)
1904		p = arg + strlen(arg);
1905	len = min(sizeof pipe->mode_str - 1, (unsigned int)(p - arg));
1906	strncpy(pipe->mode_str, arg, len);
1907	pipe->mode_str[len] = '\0';
1908
1909	if (*p == '-') {
1910		pipe->vrefresh = strtof(p + 1, &endp);
1911		p = endp;
1912	}
1913
1914	if (*p == '@') {
1915		strncpy(pipe->format_str, p + 1, 4);
1916		pipe->format_str[4] = '\0';
1917	}
1918
1919	pipe->fourcc = util_format_fourcc(pipe->format_str);
1920	if (pipe->fourcc == 0)  {
1921		fprintf(stderr, "unknown format %s\n", pipe->format_str);
1922		return -1;
1923	}
1924
1925	return 0;
1926}
1927
1928static int parse_plane(struct plane_arg *plane, const char *p)
1929{
1930	char *end;
1931
1932	plane->plane_id = strtoul(p, &end, 10);
1933	if (*end != '@')
1934		return -EINVAL;
1935
1936	p = end + 1;
1937	plane->crtc_id = strtoul(p, &end, 10);
1938	if (*end != ':')
1939		return -EINVAL;
1940
1941	p = end + 1;
1942	plane->w = strtoul(p, &end, 10);
1943	if (*end != 'x')
1944		return -EINVAL;
1945
1946	p = end + 1;
1947	plane->h = strtoul(p, &end, 10);
1948
1949	if (*end == '+' || *end == '-') {
1950		plane->x = strtol(end, &end, 10);
1951		if (*end != '+' && *end != '-')
1952			return -EINVAL;
1953		plane->y = strtol(end, &end, 10);
1954
1955		plane->has_position = true;
1956	}
1957
1958	if (*end == '*') {
1959		p = end + 1;
1960		plane->scale = strtod(p, &end);
1961		if (plane->scale <= 0.0)
1962			return -EINVAL;
1963	} else {
1964		plane->scale = 1.0;
1965	}
1966
1967	if (*end == '@') {
1968		strncpy(plane->format_str, end + 1, 4);
1969		plane->format_str[4] = '\0';
1970	} else {
1971		strcpy(plane->format_str, "XR24");
1972	}
1973
1974	plane->fourcc = util_format_fourcc(plane->format_str);
1975	if (plane->fourcc == 0) {
1976		fprintf(stderr, "unknown format %s\n", plane->format_str);
1977		return -EINVAL;
1978	}
1979
1980	return 0;
1981}
1982
1983static int parse_property(struct property_arg *p, const char *arg)
1984{
1985	if (sscanf(arg, "%d:%32[^:]:%" SCNu64, &p->obj_id, p->name, &p->value) != 3)
1986		return -1;
1987
1988	p->obj_type = 0;
1989	p->name[DRM_PROP_NAME_LEN] = '\0';
1990
1991	return 0;
1992}
1993
1994static void parse_fill_patterns(char *arg)
1995{
1996	char *fill = strtok(arg, ",");
1997	if (!fill)
1998		return;
1999	primary_fill = util_pattern_enum(fill);
2000	fill = strtok(NULL, ",");
2001	if (!fill)
2002		return;
2003	secondary_fill = util_pattern_enum(fill);
2004}
2005
2006static void usage(char *name)
2007{
2008	fprintf(stderr, "usage: %s [-acDdefMPpsCvrw]\n", name);
2009
2010	fprintf(stderr, "\n Query options:\n\n");
2011	fprintf(stderr, "\t-c\tlist connectors\n");
2012	fprintf(stderr, "\t-e\tlist encoders\n");
2013	fprintf(stderr, "\t-f\tlist framebuffers\n");
2014	fprintf(stderr, "\t-p\tlist CRTCs and planes (pipes)\n");
2015
2016	fprintf(stderr, "\n Test options:\n\n");
2017	fprintf(stderr, "\t-P <plane_id>@<crtc_id>:<w>x<h>[+<x>+<y>][*<scale>][@<format>]\tset a plane\n");
2018	fprintf(stderr, "\t-s <connector_id>[,<connector_id>][@<crtc_id>]:[#<mode index>]<mode>[-<vrefresh>][@<format>]\tset a mode\n");
2019	fprintf(stderr, "\t-C\ttest hw cursor\n");
2020	fprintf(stderr, "\t-v\ttest vsynced page flipping\n");
2021	fprintf(stderr, "\t-r\tset the preferred mode for all connectors\n");
2022	fprintf(stderr, "\t-w <obj_id>:<prop_name>:<value>\tset property\n");
2023	fprintf(stderr, "\t-a \tuse atomic API\n");
2024	fprintf(stderr, "\t-F pattern1,pattern2\tspecify fill patterns\n");
2025
2026	fprintf(stderr, "\n Generic options:\n\n");
2027	fprintf(stderr, "\t-d\tdrop master after mode set\n");
2028	fprintf(stderr, "\t-M module\tuse the given driver\n");
2029	fprintf(stderr, "\t-D device\tuse the given device\n");
2030
2031	fprintf(stderr, "\n\tDefault is to dump all info.\n");
2032	exit(0);
2033}
2034
2035static char optstr[] = "acdD:efF:M:P:ps:Cvrw:";
2036
2037int main(int argc, char **argv)
2038{
2039	struct device dev;
2040
2041	int c;
2042	int encoders = 0, connectors = 0, crtcs = 0, planes = 0, framebuffers = 0;
2043	int drop_master = 0;
2044	int test_vsync = 0;
2045	int test_cursor = 0;
2046	int set_preferred = 0;
2047	int use_atomic = 0;
2048	char *device = NULL;
2049	char *module = NULL;
2050	unsigned int i;
2051	unsigned int count = 0, plane_count = 0;
2052	unsigned int prop_count = 0;
2053	struct pipe_arg *pipe_args = NULL;
2054	struct plane_arg *plane_args = NULL;
2055	struct property_arg *prop_args = NULL;
2056	unsigned int args = 0;
2057	int ret;
2058
2059	memset(&dev, 0, sizeof dev);
2060
2061	opterr = 0;
2062	while ((c = getopt(argc, argv, optstr)) != -1) {
2063		args++;
2064
2065		switch (c) {
2066		case 'a':
2067			use_atomic = 1;
2068			/* Preserve the default behaviour of dumping all information. */
2069			args--;
2070			break;
2071		case 'c':
2072			connectors = 1;
2073			break;
2074		case 'D':
2075			device = optarg;
2076			/* Preserve the default behaviour of dumping all information. */
2077			args--;
2078			break;
2079		case 'd':
2080			drop_master = 1;
2081			break;
2082		case 'e':
2083			encoders = 1;
2084			break;
2085		case 'f':
2086			framebuffers = 1;
2087			break;
2088		case 'F':
2089			parse_fill_patterns(optarg);
2090			break;
2091		case 'M':
2092			module = optarg;
2093			/* Preserve the default behaviour of dumping all information. */
2094			args--;
2095			break;
2096		case 'P':
2097			plane_args = realloc(plane_args,
2098					     (plane_count + 1) * sizeof *plane_args);
2099			if (plane_args == NULL) {
2100				fprintf(stderr, "memory allocation failed\n");
2101				return 1;
2102			}
2103			memset(&plane_args[plane_count], 0, sizeof(*plane_args));
2104
2105			if (parse_plane(&plane_args[plane_count], optarg) < 0)
2106				usage(argv[0]);
2107
2108			plane_count++;
2109			break;
2110		case 'p':
2111			crtcs = 1;
2112			planes = 1;
2113			break;
2114		case 's':
2115			pipe_args = realloc(pipe_args,
2116					    (count + 1) * sizeof *pipe_args);
2117			if (pipe_args == NULL) {
2118				fprintf(stderr, "memory allocation failed\n");
2119				return 1;
2120			}
2121			memset(&pipe_args[count], 0, sizeof(*pipe_args));
2122
2123			if (parse_connector(&pipe_args[count], optarg) < 0)
2124				usage(argv[0]);
2125
2126			count++;
2127			break;
2128		case 'C':
2129			test_cursor = 1;
2130			break;
2131		case 'v':
2132			test_vsync = 1;
2133			break;
2134		case 'r':
2135			set_preferred = 1;
2136			break;
2137		case 'w':
2138			prop_args = realloc(prop_args,
2139					   (prop_count + 1) * sizeof *prop_args);
2140			if (prop_args == NULL) {
2141				fprintf(stderr, "memory allocation failed\n");
2142				return 1;
2143			}
2144			memset(&prop_args[prop_count], 0, sizeof(*prop_args));
2145
2146			if (parse_property(&prop_args[prop_count], optarg) < 0)
2147				usage(argv[0]);
2148
2149			prop_count++;
2150			break;
2151		default:
2152			usage(argv[0]);
2153			break;
2154		}
2155	}
2156
2157	/* Dump all the details when no* arguments are provided. */
2158	if (!args)
2159		encoders = connectors = crtcs = planes = framebuffers = 1;
2160
2161	if (test_vsync && !count) {
2162		fprintf(stderr, "page flipping requires at least one -s option.\n");
2163		return -1;
2164	}
2165	if (set_preferred && count) {
2166		fprintf(stderr, "cannot use -r (preferred) when -s (mode) is set\n");
2167		return -1;
2168	}
2169
2170	if (set_preferred && plane_count) {
2171		fprintf(stderr, "cannot use -r (preferred) when -P (plane) is set\n");
2172		return -1;
2173	}
2174
2175	dev.fd = util_open(device, module);
2176	if (dev.fd < 0)
2177		return -1;
2178
2179	if (use_atomic) {
2180		ret = drmSetClientCap(dev.fd, DRM_CLIENT_CAP_ATOMIC, 1);
2181		if (ret) {
2182			fprintf(stderr, "no atomic modesetting support: %s\n", strerror(errno));
2183			drmClose(dev.fd);
2184			return -1;
2185		}
2186	}
2187
2188	dev.use_atomic = use_atomic;
2189
2190	dev.resources = get_resources(&dev);
2191	if (!dev.resources) {
2192		drmClose(dev.fd);
2193		return 1;
2194	}
2195
2196#define dump_resource(dev, res) if (res) dump_##res(dev)
2197
2198	dump_resource(&dev, encoders);
2199	dump_resource(&dev, connectors);
2200	dump_resource(&dev, crtcs);
2201	dump_resource(&dev, planes);
2202	dump_resource(&dev, framebuffers);
2203
2204	for (i = 0; i < prop_count; ++i)
2205		set_property(&dev, &prop_args[i]);
2206
2207	if (dev.use_atomic) {
2208		dev.req = drmModeAtomicAlloc();
2209
2210		if (set_preferred || (count && plane_count)) {
2211			uint64_t cap = 0;
2212
2213			ret = drmGetCap(dev.fd, DRM_CAP_DUMB_BUFFER, &cap);
2214			if (ret || cap == 0) {
2215				fprintf(stderr, "driver doesn't support the dumb buffer API\n");
2216				return 1;
2217			}
2218
2219			if (set_preferred || count)
2220				set_mode(&dev, pipe_args, count);
2221
2222			if (plane_count)
2223				atomic_set_planes(&dev, plane_args, plane_count, false);
2224
2225			ret = drmModeAtomicCommit(dev.fd, dev.req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
2226			if (ret) {
2227				fprintf(stderr, "Atomic Commit failed [1]\n");
2228				return 1;
2229			}
2230
2231			if (test_vsync)
2232				atomic_test_page_flip(&dev, pipe_args, plane_args, plane_count);
2233
2234			if (drop_master)
2235				drmDropMaster(dev.fd);
2236
2237			getchar();
2238
2239			drmModeAtomicFree(dev.req);
2240			dev.req = drmModeAtomicAlloc();
2241
2242			/* XXX: properly teardown the preferred mode/plane state */
2243			if (plane_count)
2244				atomic_clear_planes(&dev, plane_args, plane_count);
2245
2246			if (count)
2247				atomic_clear_mode(&dev, pipe_args, count);
2248
2249			ret = drmModeAtomicCommit(dev.fd, dev.req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
2250			if (ret)
2251				fprintf(stderr, "Atomic Commit failed\n");
2252
2253			if (plane_count)
2254				atomic_clear_FB(&dev, plane_args, plane_count);
2255		}
2256
2257		drmModeAtomicFree(dev.req);
2258	} else {
2259		if (set_preferred || count || plane_count) {
2260			uint64_t cap = 0;
2261
2262			ret = drmGetCap(dev.fd, DRM_CAP_DUMB_BUFFER, &cap);
2263			if (ret || cap == 0) {
2264				fprintf(stderr, "driver doesn't support the dumb buffer API\n");
2265				return 1;
2266			}
2267
2268			if (set_preferred || count)
2269				set_mode(&dev, pipe_args, count);
2270
2271			if (plane_count)
2272				set_planes(&dev, plane_args, plane_count);
2273
2274			if (test_cursor)
2275				set_cursors(&dev, pipe_args, count);
2276
2277			if (test_vsync)
2278				test_page_flip(&dev, pipe_args, count);
2279
2280			if (drop_master)
2281				drmDropMaster(dev.fd);
2282
2283			getchar();
2284
2285			if (test_cursor)
2286				clear_cursors(&dev);
2287
2288			if (plane_count)
2289				clear_planes(&dev, plane_args, plane_count);
2290
2291			if (set_preferred || count)
2292				clear_mode(&dev);
2293		}
2294	}
2295
2296	free_resources(dev.resources);
2297	drmClose(dev.fd);
2298
2299	return 0;
2300}
2301