118210155Smrg/*
218210155Smrg * DRM based mode setting test program
318210155Smrg * Copyright 2008 Tungsten Graphics
418210155Smrg *   Jakob Bornecrantz <jakob@tungstengraphics.com>
518210155Smrg * Copyright 2008 Intel Corporation
618210155Smrg *   Jesse Barnes <jesse.barnes@intel.com>
718210155Smrg *
818210155Smrg * Permission is hereby granted, free of charge, to any person obtaining a
918210155Smrg * copy of this software and associated documentation files (the "Software"),
1018210155Smrg * to deal in the Software without restriction, including without limitation
1118210155Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1218210155Smrg * and/or sell copies of the Software, and to permit persons to whom the
1318210155Smrg * Software is furnished to do so, subject to the following conditions:
1418210155Smrg *
1518210155Smrg * The above copyright notice and this permission notice shall be included in
1618210155Smrg * all copies or substantial portions of the Software.
1718210155Smrg *
1818210155Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1918210155Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2018210155Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2118210155Smrg * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2218210155Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2318210155Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
2418210155Smrg * IN THE SOFTWARE.
2518210155Smrg */
2618210155Smrg
2718210155Smrg/*
2818210155Smrg * This fairly simple test program dumps output in a similar format to the
2918210155Smrg * "xrandr" tool everyone knows & loves.  It's necessarily slightly different
3018210155Smrg * since the kernel separates outputs into encoder and connector structures,
3118210155Smrg * each with their own unique ID.  The program also allows test testing of the
3218210155Smrg * memory management and mode setting APIs by allowing the user to specify a
3318210155Smrg * connector and mode to use for mode setting.  If all works as expected, a
3418210155Smrg * blue background should be painted on the monitor attached to the specified
3518210155Smrg * connector after the selected mode is set.
3618210155Smrg *
3718210155Smrg * TODO: use cairo to write the mode info on the selected output once
3818210155Smrg *       the mode has been programmed, along with possible test patterns.
3918210155Smrg */
40fe517fc9Smrg
4118210155Smrg#include <assert.h>
42e88f27b3Smrg#include <ctype.h>
43e88f27b3Smrg#include <stdbool.h>
4418210155Smrg#include <stdio.h>
4518210155Smrg#include <stdlib.h>
4618210155Smrg#include <stdint.h>
47e88f27b3Smrg#include <inttypes.h>
4818210155Smrg#include <unistd.h>
4918210155Smrg#include <string.h>
50424e9256Smrg#include <strings.h>
5118210155Smrg#include <errno.h>
52fe517fc9Smrg#include <poll.h>
5322944501Smrg#include <sys/time.h>
5487bf8e7cSmrg#if HAVE_SYS_SELECT_H
55fe517fc9Smrg#include <sys/select.h>
56fe517fc9Smrg#endif
5787bf8e7cSmrg#include <math.h>
5818210155Smrg
5918210155Smrg#include "xf86drm.h"
6018210155Smrg#include "xf86drmMode.h"
61e88f27b3Smrg#include "drm_fourcc.h"
6218210155Smrg
63fe517fc9Smrg#include "util/common.h"
64fe517fc9Smrg#include "util/format.h"
65fe517fc9Smrg#include "util/kms.h"
66fe517fc9Smrg#include "util/pattern.h"
67fe517fc9Smrg
68e88f27b3Smrg#include "buffers.h"
69a7d7de1eSmrg#include "cursor.h"
70e88f27b3Smrg
71bf6cc7dcSmrgstatic enum util_fill_pattern primary_fill = UTIL_PATTERN_SMPTE;
72bf6cc7dcSmrgstatic enum util_fill_pattern secondary_fill = UTIL_PATTERN_TILES;
7348246ce7Smrgstatic drmModeModeInfo user_mode;
74bf6cc7dcSmrg
75e88f27b3Smrgstruct crtc {
76e88f27b3Smrg	drmModeCrtc *crtc;
77e88f27b3Smrg	drmModeObjectProperties *props;
78e88f27b3Smrg	drmModePropertyRes **props_info;
79e88f27b3Smrg	drmModeModeInfo *mode;
80e88f27b3Smrg};
81e88f27b3Smrg
82e88f27b3Smrgstruct encoder {
83e88f27b3Smrg	drmModeEncoder *encoder;
84e88f27b3Smrg};
85e88f27b3Smrg
86e88f27b3Smrgstruct connector {
87e88f27b3Smrg	drmModeConnector *connector;
88e88f27b3Smrg	drmModeObjectProperties *props;
89e88f27b3Smrg	drmModePropertyRes **props_info;
90fe517fc9Smrg	char *name;
91e88f27b3Smrg};
9218210155Smrg
93e88f27b3Smrgstruct fb {
94e88f27b3Smrg	drmModeFB *fb;
95e88f27b3Smrg};
96e88f27b3Smrg
97e88f27b3Smrgstruct plane {
98e88f27b3Smrg	drmModePlane *plane;
99e88f27b3Smrg	drmModeObjectProperties *props;
100e88f27b3Smrg	drmModePropertyRes **props_info;
101e88f27b3Smrg};
102e88f27b3Smrg
103e88f27b3Smrgstruct resources {
104e88f27b3Smrg	struct crtc *crtcs;
10587bf8e7cSmrg	int count_crtcs;
106e88f27b3Smrg	struct encoder *encoders;
10787bf8e7cSmrg	int count_encoders;
108e88f27b3Smrg	struct connector *connectors;
10987bf8e7cSmrg	int count_connectors;
110e88f27b3Smrg	struct fb *fbs;
11187bf8e7cSmrg	int count_fbs;
112e88f27b3Smrg	struct plane *planes;
11387bf8e7cSmrg	uint32_t count_planes;
114e88f27b3Smrg};
115e88f27b3Smrg
116e88f27b3Smrgstruct device {
117e88f27b3Smrg	int fd;
118e88f27b3Smrg
119e88f27b3Smrg	struct resources *resources;
120e88f27b3Smrg
121e88f27b3Smrg	struct {
122e88f27b3Smrg		unsigned int width;
123e88f27b3Smrg		unsigned int height;
124e88f27b3Smrg
125e88f27b3Smrg		unsigned int fb_id;
1263c748557Ssnj		struct bo *bo;
127424e9256Smrg		struct bo *cursor_bo;
128e88f27b3Smrg	} mode;
1296260e5d5Smrg
1306260e5d5Smrg	int use_atomic;
1316260e5d5Smrg	drmModeAtomicReq *req;
13248246ce7Smrg	int32_t writeback_fence_fd;
133e88f27b3Smrg};
13418210155Smrg
1353c748557Ssnjstatic inline int64_t U642I64(uint64_t val)
1363c748557Ssnj{
1373c748557Ssnj	return (int64_t)*((int64_t *)&val);
1383c748557Ssnj}
13918210155Smrg
14087bf8e7cSmrgstatic float mode_vrefresh(drmModeModeInfo *mode)
14187bf8e7cSmrg{
14248246ce7Smrg	unsigned int num, den;
14348246ce7Smrg
14448246ce7Smrg	num = mode->clock;
14548246ce7Smrg	den = mode->htotal * mode->vtotal;
14648246ce7Smrg
14748246ce7Smrg	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
14848246ce7Smrg		num *= 2;
14948246ce7Smrg	if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
15048246ce7Smrg		den *= 2;
15148246ce7Smrg	if (mode->vscan > 1)
15248246ce7Smrg		den *= mode->vscan;
15348246ce7Smrg
15448246ce7Smrg	return num * 1000.00 / den;
15587bf8e7cSmrg}
15687bf8e7cSmrg
157e88f27b3Smrg#define bit_name_fn(res)					\
158e88f27b3Smrgconst char * res##_str(int type) {				\
159e88f27b3Smrg	unsigned int i;						\
160e88f27b3Smrg	const char *sep = "";					\
161e88f27b3Smrg	for (i = 0; i < ARRAY_SIZE(res##_names); i++) {		\
162e88f27b3Smrg		if (type & (1 << i)) {				\
163e88f27b3Smrg			printf("%s%s", sep, res##_names[i]);	\
164e88f27b3Smrg			sep = ", ";				\
165e88f27b3Smrg		}						\
166e88f27b3Smrg	}							\
167e88f27b3Smrg	return NULL;						\
168e88f27b3Smrg}
169e88f27b3Smrg
170e88f27b3Smrgstatic const char *mode_type_names[] = {
171e88f27b3Smrg	"builtin",
172e88f27b3Smrg	"clock_c",
173e88f27b3Smrg	"crtc_c",
174e88f27b3Smrg	"preferred",
175e88f27b3Smrg	"default",
176e88f27b3Smrg	"userdef",
177e88f27b3Smrg	"driver",
178e88f27b3Smrg};
179e88f27b3Smrg
180e88f27b3Smrgstatic bit_name_fn(mode_type)
181e88f27b3Smrg
182e88f27b3Smrgstatic const char *mode_flag_names[] = {
183e88f27b3Smrg	"phsync",
184e88f27b3Smrg	"nhsync",
185e88f27b3Smrg	"pvsync",
186e88f27b3Smrg	"nvsync",
187e88f27b3Smrg	"interlace",
188e88f27b3Smrg	"dblscan",
189e88f27b3Smrg	"csync",
190e88f27b3Smrg	"pcsync",
191e88f27b3Smrg	"ncsync",
192e88f27b3Smrg	"hskew",
193e88f27b3Smrg	"bcast",
194e88f27b3Smrg	"pixmux",
195e88f27b3Smrg	"dblclk",
196e88f27b3Smrg	"clkdiv2"
197e88f27b3Smrg};
198e88f27b3Smrg
199e88f27b3Smrgstatic bit_name_fn(mode_flag)
20018210155Smrg
2010655efefSmrgstatic void dump_fourcc(uint32_t fourcc)
2020655efefSmrg{
2033b115362Smrg	char *name = drmGetFormatName(fourcc);
2043b115362Smrg	printf(" %s", name);
2053b115362Smrg	free(name);
2060655efefSmrg}
2070655efefSmrg
208e88f27b3Smrgstatic void dump_encoders(struct device *dev)
20918210155Smrg{
21018210155Smrg	drmModeEncoder *encoder;
21118210155Smrg	int i;
21218210155Smrg
21318210155Smrg	printf("Encoders:\n");
21418210155Smrg	printf("id\tcrtc\ttype\tpossible crtcs\tpossible clones\t\n");
21587bf8e7cSmrg	for (i = 0; i < dev->resources->count_encoders; i++) {
216e88f27b3Smrg		encoder = dev->resources->encoders[i].encoder;
217e88f27b3Smrg		if (!encoder)
21818210155Smrg			continue;
219e88f27b3Smrg
22018210155Smrg		printf("%d\t%d\t%s\t0x%08x\t0x%08x\n",
22118210155Smrg		       encoder->encoder_id,
22218210155Smrg		       encoder->crtc_id,
223fe517fc9Smrg		       util_lookup_encoder_type_name(encoder->encoder_type),
22418210155Smrg		       encoder->possible_crtcs,
22518210155Smrg		       encoder->possible_clones);
22618210155Smrg	}
22718210155Smrg	printf("\n");
22818210155Smrg}
22918210155Smrg
23087bf8e7cSmrgstatic void dump_mode(drmModeModeInfo *mode, int index)
23118210155Smrg{
23287bf8e7cSmrg	printf("  #%i %s %.2f %d %d %d %d %d %d %d %d %d",
23387bf8e7cSmrg	       index,
23418210155Smrg	       mode->name,
23587bf8e7cSmrg	       mode_vrefresh(mode),
23618210155Smrg	       mode->hdisplay,
23718210155Smrg	       mode->hsync_start,
23818210155Smrg	       mode->hsync_end,
23918210155Smrg	       mode->htotal,
24018210155Smrg	       mode->vdisplay,
24118210155Smrg	       mode->vsync_start,
24218210155Smrg	       mode->vsync_end,
2432ee35494Smrg	       mode->vtotal,
2442ee35494Smrg	       mode->clock);
245e88f27b3Smrg
246e88f27b3Smrg	printf(" flags: ");
247e88f27b3Smrg	mode_flag_str(mode->flags);
248e88f27b3Smrg	printf("; type: ");
249e88f27b3Smrg	mode_type_str(mode->type);
250e88f27b3Smrg	printf("\n");
25118210155Smrg}
25218210155Smrg
253e88f27b3Smrgstatic void dump_blob(struct device *dev, uint32_t blob_id)
254e88f27b3Smrg{
255e88f27b3Smrg	uint32_t i;
256e88f27b3Smrg	unsigned char *blob_data;
257e88f27b3Smrg	drmModePropertyBlobPtr blob;
258e88f27b3Smrg
259e88f27b3Smrg	blob = drmModeGetPropertyBlob(dev->fd, blob_id);
2603c748557Ssnj	if (!blob) {
2613c748557Ssnj		printf("\n");
262e88f27b3Smrg		return;
2633c748557Ssnj	}
264e88f27b3Smrg
265e88f27b3Smrg	blob_data = blob->data;
266e88f27b3Smrg
267e88f27b3Smrg	for (i = 0; i < blob->length; i++) {
268e88f27b3Smrg		if (i % 16 == 0)
269e88f27b3Smrg			printf("\n\t\t\t");
270e88f27b3Smrg		printf("%.2hhx", blob_data[i]);
271e88f27b3Smrg	}
272e88f27b3Smrg	printf("\n");
273e88f27b3Smrg
274e88f27b3Smrg	drmModeFreePropertyBlob(blob);
275e88f27b3Smrg}
276e88f27b3Smrg
2772b90624aSmrgstatic const char *modifier_to_string(uint64_t modifier)
2782b90624aSmrg{
279636d5e9fSmrg	static char mod_string[4096];
280636d5e9fSmrg
281636d5e9fSmrg	char *modifier_name = drmGetFormatModifierName(modifier);
282636d5e9fSmrg	char *vendor_name = drmGetFormatModifierVendor(modifier);
283636d5e9fSmrg	memset(mod_string, 0x00, sizeof(mod_string));
284636d5e9fSmrg
285636d5e9fSmrg	if (!modifier_name) {
286636d5e9fSmrg		if (vendor_name)
287636d5e9fSmrg			snprintf(mod_string, sizeof(mod_string), "%s_%s",
288636d5e9fSmrg				 vendor_name, "UNKNOWN_MODIFIER");
289636d5e9fSmrg		else
290636d5e9fSmrg			snprintf(mod_string, sizeof(mod_string), "%s_%s",
291636d5e9fSmrg				 "UNKNOWN_VENDOR", "UNKNOWN_MODIFIER");
292636d5e9fSmrg		/* safe, as free is no-op for NULL */
293636d5e9fSmrg		free(vendor_name);
294636d5e9fSmrg		return mod_string;
2952b90624aSmrg	}
296636d5e9fSmrg
297636d5e9fSmrg	if (modifier == DRM_FORMAT_MOD_LINEAR) {
298636d5e9fSmrg		snprintf(mod_string, sizeof(mod_string), "%s", modifier_name);
299636d5e9fSmrg		free(modifier_name);
300636d5e9fSmrg		free(vendor_name);
301636d5e9fSmrg		return mod_string;
302636d5e9fSmrg	}
303636d5e9fSmrg
304636d5e9fSmrg	snprintf(mod_string, sizeof(mod_string), "%s_%s",
305636d5e9fSmrg		 vendor_name, modifier_name);
306636d5e9fSmrg
307636d5e9fSmrg	free(modifier_name);
308636d5e9fSmrg	free(vendor_name);
309636d5e9fSmrg	return mod_string;
3102b90624aSmrg}
3112b90624aSmrg
3122b90624aSmrgstatic void dump_in_formats(struct device *dev, uint32_t blob_id)
3132b90624aSmrg{
314adfa0b0cSmrg	drmModeFormatModifierIterator iter = {0};
3152b90624aSmrg	drmModePropertyBlobPtr blob;
316adfa0b0cSmrg	uint32_t fmt = 0;
3172b90624aSmrg
3182b90624aSmrg	printf("\t\tin_formats blob decoded:\n");
3192b90624aSmrg	blob = drmModeGetPropertyBlob(dev->fd, blob_id);
3202b90624aSmrg	if (!blob) {
3212b90624aSmrg		printf("\n");
3222b90624aSmrg		return;
3232b90624aSmrg	}
3242b90624aSmrg
325adfa0b0cSmrg	while (drmModeFormatModifierBlobIterNext(blob, &iter)) {
326adfa0b0cSmrg		if (!fmt || fmt != iter.fmt) {
327adfa0b0cSmrg			printf("%s\t\t\t", !fmt ? "" : "\n");
328adfa0b0cSmrg			fmt = iter.fmt;
329adfa0b0cSmrg			dump_fourcc(fmt);
330adfa0b0cSmrg			printf(": ");
3312b90624aSmrg		}
332adfa0b0cSmrg
33348246ce7Smrg		printf(" %s(0x%"PRIx64")", modifier_to_string(iter.mod), iter.mod);
3342b90624aSmrg	}
3352b90624aSmrg
336adfa0b0cSmrg	printf("\n");
337adfa0b0cSmrg
3382b90624aSmrg	drmModeFreePropertyBlob(blob);
3392b90624aSmrg}
3402b90624aSmrg
341e88f27b3Smrgstatic void dump_prop(struct device *dev, drmModePropertyPtr prop,
342e88f27b3Smrg		      uint32_t prop_id, uint64_t value)
34318210155Smrg{
34418210155Smrg	int i;
345e88f27b3Smrg	printf("\t%d", prop_id);
346e88f27b3Smrg	if (!prop) {
347e88f27b3Smrg		printf("\n");
348e88f27b3Smrg		return;
349e88f27b3Smrg	}
350e88f27b3Smrg
351e88f27b3Smrg	printf(" %s:\n", prop->name);
352e88f27b3Smrg
353e88f27b3Smrg	printf("\t\tflags:");
354e88f27b3Smrg	if (prop->flags & DRM_MODE_PROP_PENDING)
355e88f27b3Smrg		printf(" pending");
356e88f27b3Smrg	if (prop->flags & DRM_MODE_PROP_IMMUTABLE)
357e88f27b3Smrg		printf(" immutable");
3583c748557Ssnj	if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE))
3593c748557Ssnj		printf(" signed range");
3603c748557Ssnj	if (drm_property_type_is(prop, DRM_MODE_PROP_RANGE))
3613c748557Ssnj		printf(" range");
3623c748557Ssnj	if (drm_property_type_is(prop, DRM_MODE_PROP_ENUM))
363e88f27b3Smrg		printf(" enum");
3643c748557Ssnj	if (drm_property_type_is(prop, DRM_MODE_PROP_BITMASK))
365e88f27b3Smrg		printf(" bitmask");
3663c748557Ssnj	if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB))
367e88f27b3Smrg		printf(" blob");
3683c748557Ssnj	if (drm_property_type_is(prop, DRM_MODE_PROP_OBJECT))
3693c748557Ssnj		printf(" object");
370e88f27b3Smrg	printf("\n");
371e88f27b3Smrg
3723c748557Ssnj	if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE)) {
3733c748557Ssnj		printf("\t\tvalues:");
3743c748557Ssnj		for (i = 0; i < prop->count_values; i++)
3753c748557Ssnj			printf(" %"PRId64, U642I64(prop->values[i]));
3763c748557Ssnj		printf("\n");
3773c748557Ssnj	}
3783c748557Ssnj
3793c748557Ssnj	if (drm_property_type_is(prop, DRM_MODE_PROP_RANGE)) {
380e88f27b3Smrg		printf("\t\tvalues:");
381e88f27b3Smrg		for (i = 0; i < prop->count_values; i++)
382e88f27b3Smrg			printf(" %"PRIu64, prop->values[i]);
383e88f27b3Smrg		printf("\n");
384e88f27b3Smrg	}
38518210155Smrg
3863c748557Ssnj	if (drm_property_type_is(prop, DRM_MODE_PROP_ENUM)) {
387e88f27b3Smrg		printf("\t\tenums:");
388e88f27b3Smrg		for (i = 0; i < prop->count_enums; i++)
389adfa0b0cSmrg			printf(" %s=%"PRIu64, prop->enums[i].name,
39050027b5bSmrg			       (uint64_t)prop->enums[i].value);
391e88f27b3Smrg		printf("\n");
3923c748557Ssnj	} else if (drm_property_type_is(prop, DRM_MODE_PROP_BITMASK)) {
393e88f27b3Smrg		printf("\t\tvalues:");
394e88f27b3Smrg		for (i = 0; i < prop->count_enums; i++)
395e88f27b3Smrg			printf(" %s=0x%llx", prop->enums[i].name,
396e88f27b3Smrg			       (1LL << prop->enums[i].value));
397e88f27b3Smrg		printf("\n");
398e88f27b3Smrg	} else {
399e88f27b3Smrg		assert(prop->count_enums == 0);
400e88f27b3Smrg	}
401e88f27b3Smrg
4023c748557Ssnj	if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB)) {
403e88f27b3Smrg		printf("\t\tblobs:\n");
404e88f27b3Smrg		for (i = 0; i < prop->count_blobs; i++)
405e88f27b3Smrg			dump_blob(dev, prop->blob_ids[i]);
406e88f27b3Smrg		printf("\n");
407e88f27b3Smrg	} else {
408e88f27b3Smrg		assert(prop->count_blobs == 0);
40918210155Smrg	}
410e88f27b3Smrg
411e88f27b3Smrg	printf("\t\tvalue:");
4123c748557Ssnj	if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB))
413e88f27b3Smrg		dump_blob(dev, value);
414fe517fc9Smrg	else if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE))
415fe517fc9Smrg		printf(" %"PRId64"\n", value);
416e88f27b3Smrg	else
417e88f27b3Smrg		printf(" %"PRIu64"\n", value);
4182b90624aSmrg
4192b90624aSmrg	if (strcmp(prop->name, "IN_FORMATS") == 0)
4202b90624aSmrg		dump_in_formats(dev, value);
42118210155Smrg}
42218210155Smrg
423e88f27b3Smrgstatic void dump_connectors(struct device *dev)
42418210155Smrg{
42518210155Smrg	int i, j;
42618210155Smrg
42718210155Smrg	printf("Connectors:\n");
428fe517fc9Smrg	printf("id\tencoder\tstatus\t\tname\t\tsize (mm)\tmodes\tencoders\n");
42987bf8e7cSmrg	for (i = 0; i < dev->resources->count_connectors; i++) {
430e88f27b3Smrg		struct connector *_connector = &dev->resources->connectors[i];
431e88f27b3Smrg		drmModeConnector *connector = _connector->connector;
432e88f27b3Smrg		if (!connector)
43318210155Smrg			continue;
43418210155Smrg
435fe517fc9Smrg		printf("%d\t%d\t%s\t%-15s\t%dx%d\t\t%d\t",
43618210155Smrg		       connector->connector_id,
43718210155Smrg		       connector->encoder_id,
438fe517fc9Smrg		       util_lookup_connector_status_name(connector->connection),
439fe517fc9Smrg		       _connector->name,
44018210155Smrg		       connector->mmWidth, connector->mmHeight,
44118210155Smrg		       connector->count_modes);
44218210155Smrg
44322944501Smrg		for (j = 0; j < connector->count_encoders; j++)
44422944501Smrg			printf("%s%d", j > 0 ? ", " : "", connector->encoders[j]);
44522944501Smrg		printf("\n");
44622944501Smrg
447e88f27b3Smrg		if (connector->count_modes) {
448e88f27b3Smrg			printf("  modes:\n");
44987bf8e7cSmrg			printf("\tindex name refresh (Hz) hdisp hss hse htot vdisp "
4504b3d3f37Smrg			       "vss vse vtot\n");
451e88f27b3Smrg			for (j = 0; j < connector->count_modes; j++)
45287bf8e7cSmrg				dump_mode(&connector->modes[j], j);
453e88f27b3Smrg		}
45422944501Smrg
455e88f27b3Smrg		if (_connector->props) {
456e88f27b3Smrg			printf("  props:\n");
457e88f27b3Smrg			for (j = 0; j < (int)_connector->props->count_props; j++)
458e88f27b3Smrg				dump_prop(dev, _connector->props_info[j],
459e88f27b3Smrg					  _connector->props->props[j],
460e88f27b3Smrg					  _connector->props->prop_values[j]);
461e88f27b3Smrg		}
46218210155Smrg	}
46318210155Smrg	printf("\n");
46418210155Smrg}
46518210155Smrg
466e88f27b3Smrgstatic void dump_crtcs(struct device *dev)
46718210155Smrg{
46818210155Smrg	int i;
469e88f27b3Smrg	uint32_t j;
47018210155Smrg
47118210155Smrg	printf("CRTCs:\n");
47218210155Smrg	printf("id\tfb\tpos\tsize\n");
47387bf8e7cSmrg	for (i = 0; i < dev->resources->count_crtcs; i++) {
474e88f27b3Smrg		struct crtc *_crtc = &dev->resources->crtcs[i];
475e88f27b3Smrg		drmModeCrtc *crtc = _crtc->crtc;
476e88f27b3Smrg		if (!crtc)
47718210155Smrg			continue;
478e88f27b3Smrg
47918210155Smrg		printf("%d\t%d\t(%d,%d)\t(%dx%d)\n",
48018210155Smrg		       crtc->crtc_id,
48118210155Smrg		       crtc->buffer_id,
48218210155Smrg		       crtc->x, crtc->y,
48318210155Smrg		       crtc->width, crtc->height);
48487bf8e7cSmrg		dump_mode(&crtc->mode, 0);
48518210155Smrg
486e88f27b3Smrg		if (_crtc->props) {
487e88f27b3Smrg			printf("  props:\n");
488e88f27b3Smrg			for (j = 0; j < _crtc->props->count_props; j++)
489e88f27b3Smrg				dump_prop(dev, _crtc->props_info[j],
490e88f27b3Smrg					  _crtc->props->props[j],
491e88f27b3Smrg					  _crtc->props->prop_values[j]);
492e88f27b3Smrg		} else {
493e88f27b3Smrg			printf("  no properties found\n");
494e88f27b3Smrg		}
49518210155Smrg	}
49618210155Smrg	printf("\n");
49718210155Smrg}
49818210155Smrg
499e88f27b3Smrgstatic void dump_framebuffers(struct device *dev)
50018210155Smrg{
50118210155Smrg	drmModeFB *fb;
50218210155Smrg	int i;
50318210155Smrg
50418210155Smrg	printf("Frame buffers:\n");
50518210155Smrg	printf("id\tsize\tpitch\n");
50687bf8e7cSmrg	for (i = 0; i < dev->resources->count_fbs; i++) {
507e88f27b3Smrg		fb = dev->resources->fbs[i].fb;
508e88f27b3Smrg		if (!fb)
50918210155Smrg			continue;
510e88f27b3Smrg
51122944501Smrg		printf("%u\t(%ux%u)\t%u\n",
51218210155Smrg		       fb->fb_id,
51322944501Smrg		       fb->width, fb->height,
51422944501Smrg		       fb->pitch);
51518210155Smrg	}
51618210155Smrg	printf("\n");
51718210155Smrg}
51818210155Smrg
519e88f27b3Smrgstatic void dump_planes(struct device *dev)
52018210155Smrg{
521e88f27b3Smrg	unsigned int i, j;
52218210155Smrg
523e88f27b3Smrg	printf("Planes:\n");
524e88f27b3Smrg	printf("id\tcrtc\tfb\tCRTC x,y\tx,y\tgamma size\tpossible crtcs\n");
52518210155Smrg
52687bf8e7cSmrg	for (i = 0; i < dev->resources->count_planes; i++) {
527e88f27b3Smrg		struct plane *plane = &dev->resources->planes[i];
528e88f27b3Smrg		drmModePlane *ovr = plane->plane;
529e88f27b3Smrg		if (!ovr)
53018210155Smrg			continue;
53118210155Smrg
532e88f27b3Smrg		printf("%d\t%d\t%d\t%d,%d\t\t%d,%d\t%-8d\t0x%08x\n",
533e88f27b3Smrg		       ovr->plane_id, ovr->crtc_id, ovr->fb_id,
534e88f27b3Smrg		       ovr->crtc_x, ovr->crtc_y, ovr->x, ovr->y,
535e88f27b3Smrg		       ovr->gamma_size, ovr->possible_crtcs);
536e88f27b3Smrg
537e88f27b3Smrg		if (!ovr->count_formats)
53818210155Smrg			continue;
53918210155Smrg
540e88f27b3Smrg		printf("  formats:");
541e88f27b3Smrg		for (j = 0; j < ovr->count_formats; j++)
5420655efefSmrg			dump_fourcc(ovr->formats[j]);
543e88f27b3Smrg		printf("\n");
544e88f27b3Smrg
545e88f27b3Smrg		if (plane->props) {
546e88f27b3Smrg			printf("  props:\n");
547e88f27b3Smrg			for (j = 0; j < plane->props->count_props; j++)
548e88f27b3Smrg				dump_prop(dev, plane->props_info[j],
549e88f27b3Smrg					  plane->props->props[j],
550e88f27b3Smrg					  plane->props->prop_values[j]);
551e88f27b3Smrg		} else {
552e88f27b3Smrg			printf("  no properties found\n");
55318210155Smrg		}
554e88f27b3Smrg	}
555e88f27b3Smrg	printf("\n");
55618210155Smrg
557e88f27b3Smrg	return;
558e88f27b3Smrg}
559e88f27b3Smrg
560e88f27b3Smrgstatic void free_resources(struct resources *res)
561e88f27b3Smrg{
562fe517fc9Smrg	int i;
563fe517fc9Smrg
564e88f27b3Smrg	if (!res)
565e88f27b3Smrg		return;
56618210155Smrg
56787bf8e7cSmrg#define free_resource(_res, type, Type)					\
568e88f27b3Smrg	do {									\
569e88f27b3Smrg		if (!(_res)->type##s)						\
570e88f27b3Smrg			break;							\
57187bf8e7cSmrg		for (i = 0; i < (int)(_res)->count_##type##s; ++i) {	\
572e88f27b3Smrg			if (!(_res)->type##s[i].type)				\
573e88f27b3Smrg				break;						\
574e88f27b3Smrg			drmModeFree##Type((_res)->type##s[i].type);		\
575e88f27b3Smrg		}								\
576e88f27b3Smrg		free((_res)->type##s);						\
577e88f27b3Smrg	} while (0)
578e88f27b3Smrg
57987bf8e7cSmrg#define free_properties(_res, type)					\
580e88f27b3Smrg	do {									\
58187bf8e7cSmrg		for (i = 0; i < (int)(_res)->count_##type##s; ++i) {	\
58287bf8e7cSmrg			unsigned int j;										\
58387bf8e7cSmrg			for (j = 0; j < res->type##s[i].props->count_props; ++j)\
58487bf8e7cSmrg				drmModeFreeProperty(res->type##s[i].props_info[j]);\
585e88f27b3Smrg			free(res->type##s[i].props_info);			\
58687bf8e7cSmrg			drmModeFreeObjectProperties(res->type##s[i].props);	\
587e88f27b3Smrg		}								\
588e88f27b3Smrg	} while (0)
589e88f27b3Smrg
59087bf8e7cSmrg	free_properties(res, plane);
59187bf8e7cSmrg	free_resource(res, plane, Plane);
592fe517fc9Smrg
59387bf8e7cSmrg	free_properties(res, connector);
59487bf8e7cSmrg	free_properties(res, crtc);
595fe517fc9Smrg
59687bf8e7cSmrg	for (i = 0; i < res->count_connectors; i++)
59787bf8e7cSmrg		free(res->connectors[i].name);
598e88f27b3Smrg
59987bf8e7cSmrg	free_resource(res, fb, FB);
60087bf8e7cSmrg	free_resource(res, connector, Connector);
60187bf8e7cSmrg	free_resource(res, encoder, Encoder);
60287bf8e7cSmrg	free_resource(res, crtc, Crtc);
60318210155Smrg
604e88f27b3Smrg	free(res);
605e88f27b3Smrg}
60618210155Smrg
607e88f27b3Smrgstatic struct resources *get_resources(struct device *dev)
608e88f27b3Smrg{
60987bf8e7cSmrg	drmModeRes *_res;
61087bf8e7cSmrg	drmModePlaneRes *plane_res;
611e88f27b3Smrg	struct resources *res;
612e88f27b3Smrg	int i;
61318210155Smrg
614424e9256Smrg	res = calloc(1, sizeof(*res));
615e88f27b3Smrg	if (res == 0)
616e88f27b3Smrg		return NULL;
617e88f27b3Smrg
618424e9256Smrg	drmSetClientCap(dev->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
619e88f27b3Smrg
62087bf8e7cSmrg	_res = drmModeGetResources(dev->fd);
62187bf8e7cSmrg	if (!_res) {
622e88f27b3Smrg		fprintf(stderr, "drmModeGetResources failed: %s\n",
623e88f27b3Smrg			strerror(errno));
62487bf8e7cSmrg		free(res);
62587bf8e7cSmrg		return NULL;
626e88f27b3Smrg	}
62718210155Smrg
62887bf8e7cSmrg	res->count_crtcs = _res->count_crtcs;
62987bf8e7cSmrg	res->count_encoders = _res->count_encoders;
63087bf8e7cSmrg	res->count_connectors = _res->count_connectors;
63187bf8e7cSmrg	res->count_fbs = _res->count_fbs;
632e88f27b3Smrg
63387bf8e7cSmrg	res->crtcs = calloc(res->count_crtcs, sizeof(*res->crtcs));
63487bf8e7cSmrg	res->encoders = calloc(res->count_encoders, sizeof(*res->encoders));
63587bf8e7cSmrg	res->connectors = calloc(res->count_connectors, sizeof(*res->connectors));
63687bf8e7cSmrg	res->fbs = calloc(res->count_fbs, sizeof(*res->fbs));
63787bf8e7cSmrg
63887bf8e7cSmrg	if (!res->crtcs || !res->encoders || !res->connectors || !res->fbs) {
63987bf8e7cSmrg	    drmModeFreeResources(_res);
640e88f27b3Smrg		goto error;
64187bf8e7cSmrg    }
642e88f27b3Smrg
643e88f27b3Smrg#define get_resource(_res, __res, type, Type)					\
644e88f27b3Smrg	do {									\
64587bf8e7cSmrg		for (i = 0; i < (int)(_res)->count_##type##s; ++i) {	\
64687bf8e7cSmrg			uint32_t type##id = (__res)->type##s[i];			\
64787bf8e7cSmrg			(_res)->type##s[i].type =							\
64887bf8e7cSmrg				drmModeGet##Type(dev->fd, type##id);			\
64987bf8e7cSmrg			if (!(_res)->type##s[i].type)						\
650e88f27b3Smrg				fprintf(stderr, "could not get %s %i: %s\n",	\
65187bf8e7cSmrg					#type, type##id,							\
652e88f27b3Smrg					strerror(errno));			\
653e88f27b3Smrg		}								\
654e88f27b3Smrg	} while (0)
655e88f27b3Smrg
65687bf8e7cSmrg	get_resource(res, _res, crtc, Crtc);
65787bf8e7cSmrg	get_resource(res, _res, encoder, Encoder);
65887bf8e7cSmrg	get_resource(res, _res, connector, Connector);
65987bf8e7cSmrg	get_resource(res, _res, fb, FB);
66087bf8e7cSmrg
66187bf8e7cSmrg	drmModeFreeResources(_res);
662e88f27b3Smrg
663fe517fc9Smrg	/* Set the name of all connectors based on the type name and the per-type ID. */
66487bf8e7cSmrg	for (i = 0; i < res->count_connectors; i++) {
665fe517fc9Smrg		struct connector *connector = &res->connectors[i];
666fe517fc9Smrg		drmModeConnector *conn = connector->connector;
6672b90624aSmrg		int num;
668fe517fc9Smrg
6692b90624aSmrg		num = asprintf(&connector->name, "%s-%u",
67050027b5bSmrg			 drmModeGetConnectorTypeName(conn->connector_type),
671fe517fc9Smrg			 conn->connector_type_id);
6722b90624aSmrg		if (num < 0)
6732b90624aSmrg			goto error;
674fe517fc9Smrg	}
675fe517fc9Smrg
67687bf8e7cSmrg#define get_properties(_res, type, Type)					\
677e88f27b3Smrg	do {									\
67887bf8e7cSmrg		for (i = 0; i < (int)(_res)->count_##type##s; ++i) {	\
679e88f27b3Smrg			struct type *obj = &res->type##s[i];			\
680e88f27b3Smrg			unsigned int j;						\
681e88f27b3Smrg			obj->props =						\
682e88f27b3Smrg				drmModeObjectGetProperties(dev->fd, obj->type->type##_id, \
683e88f27b3Smrg							   DRM_MODE_OBJECT_##Type); \
684e88f27b3Smrg			if (!obj->props) {					\
685e88f27b3Smrg				fprintf(stderr,					\
686e88f27b3Smrg					"could not get %s %i properties: %s\n", \
687e88f27b3Smrg					#type, obj->type->type##_id,		\
688e88f27b3Smrg					strerror(errno));			\
689e88f27b3Smrg				continue;					\
690e88f27b3Smrg			}							\
691424e9256Smrg			obj->props_info = calloc(obj->props->count_props,	\
692424e9256Smrg						 sizeof(*obj->props_info));	\
693e88f27b3Smrg			if (!obj->props_info)					\
694e88f27b3Smrg				continue;					\
695e88f27b3Smrg			for (j = 0; j < obj->props->count_props; ++j)		\
696e88f27b3Smrg				obj->props_info[j] =				\
697e88f27b3Smrg					drmModeGetProperty(dev->fd, obj->props->props[j]); \
698e88f27b3Smrg		}								\
699e88f27b3Smrg	} while (0)
700e88f27b3Smrg
70187bf8e7cSmrg	get_properties(res, crtc, CRTC);
70287bf8e7cSmrg	get_properties(res, connector, CONNECTOR);
703e88f27b3Smrg
70487bf8e7cSmrg	for (i = 0; i < res->count_crtcs; ++i)
705e88f27b3Smrg		res->crtcs[i].mode = &res->crtcs[i].crtc->mode;
706e88f27b3Smrg
70787bf8e7cSmrg	plane_res = drmModeGetPlaneResources(dev->fd);
70887bf8e7cSmrg	if (!plane_res) {
709e88f27b3Smrg		fprintf(stderr, "drmModeGetPlaneResources failed: %s\n",
710e88f27b3Smrg			strerror(errno));
711e88f27b3Smrg		return res;
71218210155Smrg	}
71318210155Smrg
71487bf8e7cSmrg	res->count_planes = plane_res->count_planes;
71587bf8e7cSmrg
71687bf8e7cSmrg	res->planes = calloc(res->count_planes, sizeof(*res->planes));
71787bf8e7cSmrg	if (!res->planes) {
71887bf8e7cSmrg		drmModeFreePlaneResources(plane_res);
719e88f27b3Smrg		goto error;
72087bf8e7cSmrg	}
721e88f27b3Smrg
722e88f27b3Smrg	get_resource(res, plane_res, plane, Plane);
72387bf8e7cSmrg	drmModeFreePlaneResources(plane_res);
72487bf8e7cSmrg	get_properties(res, plane, PLANE);
725e88f27b3Smrg
726e88f27b3Smrg	return res;
727e88f27b3Smrg
728e88f27b3Smrgerror:
729e88f27b3Smrg	free_resources(res);
730e88f27b3Smrg	return NULL;
73118210155Smrg}
73218210155Smrg
73387bf8e7cSmrgstatic struct crtc *get_crtc_by_id(struct device *dev, uint32_t id)
734d049871aSmrg{
735e88f27b3Smrg	int i;
73618210155Smrg
73787bf8e7cSmrg	for (i = 0; i < dev->resources->count_crtcs; ++i) {
738e88f27b3Smrg		drmModeCrtc *crtc = dev->resources->crtcs[i].crtc;
739e88f27b3Smrg		if (crtc && crtc->crtc_id == id)
74087bf8e7cSmrg			return &dev->resources->crtcs[i];
741e88f27b3Smrg	}
742d049871aSmrg
74387bf8e7cSmrg	return NULL;
74487bf8e7cSmrg}
74587bf8e7cSmrg
74687bf8e7cSmrgstatic uint32_t get_crtc_mask(struct device *dev, struct crtc *crtc)
74787bf8e7cSmrg{
74887bf8e7cSmrg	unsigned int i;
74987bf8e7cSmrg
75087bf8e7cSmrg	for (i = 0; i < (unsigned int)dev->resources->count_crtcs; i++) {
75187bf8e7cSmrg		if (crtc->crtc->crtc_id == dev->resources->crtcs[i].crtc->crtc_id)
75287bf8e7cSmrg			return 1 << i;
75387bf8e7cSmrg	}
75487bf8e7cSmrg    /* Unreachable: crtc->crtc is one of resources->crtcs[] */
75587bf8e7cSmrg    /* Don't return zero or static analysers will complain */
75687bf8e7cSmrg	abort();
75787bf8e7cSmrg	return 0;
758d049871aSmrg}
759d049871aSmrg
760fe517fc9Smrgstatic drmModeConnector *get_connector_by_name(struct device *dev, const char *name)
761fe517fc9Smrg{
762fe517fc9Smrg	struct connector *connector;
763fe517fc9Smrg	int i;
764fe517fc9Smrg
76587bf8e7cSmrg	for (i = 0; i < dev->resources->count_connectors; i++) {
766fe517fc9Smrg		connector = &dev->resources->connectors[i];
767fe517fc9Smrg
768fe517fc9Smrg		if (strcmp(connector->name, name) == 0)
769fe517fc9Smrg			return connector->connector;
770fe517fc9Smrg	}
771fe517fc9Smrg
772fe517fc9Smrg	return NULL;
773fe517fc9Smrg}
774fe517fc9Smrg
775e88f27b3Smrgstatic drmModeConnector *get_connector_by_id(struct device *dev, uint32_t id)
77618210155Smrg{
777e88f27b3Smrg	drmModeConnector *connector;
778e88f27b3Smrg	int i;
779e88f27b3Smrg
78087bf8e7cSmrg	for (i = 0; i < dev->resources->count_connectors; i++) {
781e88f27b3Smrg		connector = dev->resources->connectors[i].connector;
782e88f27b3Smrg		if (connector && connector->connector_id == id)
783e88f27b3Smrg			return connector;
784e88f27b3Smrg	}
785e88f27b3Smrg
786e88f27b3Smrg	return NULL;
787e88f27b3Smrg}
788e88f27b3Smrg
789e88f27b3Smrgstatic drmModeEncoder *get_encoder_by_id(struct device *dev, uint32_t id)
790e88f27b3Smrg{
791e88f27b3Smrg	drmModeEncoder *encoder;
792e88f27b3Smrg	int i;
793e88f27b3Smrg
79487bf8e7cSmrg	for (i = 0; i < dev->resources->count_encoders; i++) {
795e88f27b3Smrg		encoder = dev->resources->encoders[i].encoder;
796e88f27b3Smrg		if (encoder && encoder->encoder_id == id)
797e88f27b3Smrg			return encoder;
798e88f27b3Smrg	}
799e88f27b3Smrg
800e88f27b3Smrg	return NULL;
801e88f27b3Smrg}
802e88f27b3Smrg
803e88f27b3Smrg/* -----------------------------------------------------------------------------
804e88f27b3Smrg * Pipes and planes
805e88f27b3Smrg */
806e88f27b3Smrg
807e88f27b3Smrg/*
808e88f27b3Smrg * Mode setting with the kernel interfaces is a bit of a chore.
809e88f27b3Smrg * First you have to find the connector in question and make sure the
810e88f27b3Smrg * requested mode is available.
811e88f27b3Smrg * Then you need to find the encoder attached to that connector so you
812e88f27b3Smrg * can bind it with a free crtc.
813e88f27b3Smrg */
814e88f27b3Smrgstruct pipe_arg {
815fe517fc9Smrg	const char **cons;
816e88f27b3Smrg	uint32_t *con_ids;
817e88f27b3Smrg	unsigned int num_cons;
818e88f27b3Smrg	uint32_t crtc_id;
819e88f27b3Smrg	char mode_str[64];
82048246ce7Smrg	char format_str[8]; /* need to leave room for "_BE" and terminating \0 */
82187bf8e7cSmrg	float vrefresh;
822e88f27b3Smrg	unsigned int fourcc;
823e88f27b3Smrg	drmModeModeInfo *mode;
824e88f27b3Smrg	struct crtc *crtc;
825e88f27b3Smrg	unsigned int fb_id[2], current_fb_id;
826e88f27b3Smrg	struct timeval start;
82748246ce7Smrg	unsigned int out_fb_id;
82848246ce7Smrg	struct bo *out_bo;
829e88f27b3Smrg
830e88f27b3Smrg	int swap_count;
831e88f27b3Smrg};
832e88f27b3Smrg
833e88f27b3Smrgstruct plane_arg {
8342ee35494Smrg	uint32_t plane_id;  /* the id of plane to use */
835e88f27b3Smrg	uint32_t crtc_id;  /* the id of CRTC to bind to */
836e88f27b3Smrg	bool has_position;
837e88f27b3Smrg	int32_t x, y;
838e88f27b3Smrg	uint32_t w, h;
839e88f27b3Smrg	double scale;
840e88f27b3Smrg	unsigned int fb_id;
8416260e5d5Smrg	unsigned int old_fb_id;
842424e9256Smrg	struct bo *bo;
8436260e5d5Smrg	struct bo *old_bo;
84448246ce7Smrg	char format_str[8]; /* need to leave room for "_BE" and terminating \0 */
845e88f27b3Smrg	unsigned int fourcc;
846e88f27b3Smrg};
847e88f27b3Smrg
848e88f27b3Smrgstatic drmModeModeInfo *
849e88f27b3Smrgconnector_find_mode(struct device *dev, uint32_t con_id, const char *mode_str,
85087bf8e7cSmrg	const float vrefresh)
851e88f27b3Smrg{
852e88f27b3Smrg	drmModeConnector *connector;
853e88f27b3Smrg	drmModeModeInfo *mode;
854e88f27b3Smrg	int i;
855e88f27b3Smrg
856e88f27b3Smrg	connector = get_connector_by_id(dev, con_id);
85748246ce7Smrg	if (!connector)
85848246ce7Smrg		return NULL;
85948246ce7Smrg
86048246ce7Smrg	if (strchr(mode_str, ',')) {
86148246ce7Smrg		i = sscanf(mode_str, "%hu,%hu,%hu,%hu,%hu,%hu,%hu,%hu",
86248246ce7Smrg			     &user_mode.hdisplay, &user_mode.hsync_start,
86348246ce7Smrg			     &user_mode.hsync_end, &user_mode.htotal,
86448246ce7Smrg			     &user_mode.vdisplay, &user_mode.vsync_start,
86548246ce7Smrg			     &user_mode.vsync_end, &user_mode.vtotal);
86648246ce7Smrg		if (i == 8) {
86748246ce7Smrg			user_mode.clock = roundf(user_mode.htotal * user_mode.vtotal * vrefresh / 1000);
86848246ce7Smrg			user_mode.vrefresh = roundf(vrefresh);
86948246ce7Smrg			snprintf(user_mode.name, sizeof(user_mode.name), "custom%dx%d", user_mode.hdisplay, user_mode.vdisplay);
87048246ce7Smrg
87148246ce7Smrg			return &user_mode;
87248246ce7Smrg		}
87348246ce7Smrg	}
87448246ce7Smrg
87548246ce7Smrg	if (!connector->count_modes)
876e88f27b3Smrg		return NULL;
877e88f27b3Smrg
87887bf8e7cSmrg	/* Pick by Index */
87987bf8e7cSmrg	if (mode_str[0] == '#') {
88087bf8e7cSmrg		int index = atoi(mode_str + 1);
88187bf8e7cSmrg
88287bf8e7cSmrg		if (index >= connector->count_modes || index < 0)
88387bf8e7cSmrg			return NULL;
88487bf8e7cSmrg		return &connector->modes[index];
88587bf8e7cSmrg	}
88687bf8e7cSmrg
88787bf8e7cSmrg	/* Pick by Name */
888e88f27b3Smrg	for (i = 0; i < connector->count_modes; i++) {
889e88f27b3Smrg		mode = &connector->modes[i];
890e88f27b3Smrg		if (!strcmp(mode->name, mode_str)) {
89187bf8e7cSmrg			/* If the vertical refresh frequency is not specified
89287bf8e7cSmrg			 * then return the first mode that match with the name.
89387bf8e7cSmrg			 * Else, return the mode that match the name and
89487bf8e7cSmrg			 * the specified vertical refresh frequency.
895e88f27b3Smrg			 */
896e88f27b3Smrg			if (vrefresh == 0)
897e88f27b3Smrg				return mode;
89887bf8e7cSmrg			else if (fabs(mode_vrefresh(mode) - vrefresh) < 0.005)
899e88f27b3Smrg				return mode;
90018210155Smrg		}
901e88f27b3Smrg	}
90218210155Smrg
903e88f27b3Smrg	return NULL;
90418210155Smrg}
90518210155Smrg
906e88f27b3Smrgstatic struct crtc *pipe_find_crtc(struct device *dev, struct pipe_arg *pipe)
90718210155Smrg{
908e88f27b3Smrg	uint32_t possible_crtcs = ~0;
909e88f27b3Smrg	uint32_t active_crtcs = 0;
910e88f27b3Smrg	unsigned int crtc_idx;
911e88f27b3Smrg	unsigned int i;
912e88f27b3Smrg	int j;
913e88f27b3Smrg
914e88f27b3Smrg	for (i = 0; i < pipe->num_cons; ++i) {
915e88f27b3Smrg		uint32_t crtcs_for_connector = 0;
916e88f27b3Smrg		drmModeConnector *connector;
917e88f27b3Smrg		drmModeEncoder *encoder;
91887bf8e7cSmrg		struct crtc *crtc;
919e88f27b3Smrg
920e88f27b3Smrg		connector = get_connector_by_id(dev, pipe->con_ids[i]);
921e88f27b3Smrg		if (!connector)
922e88f27b3Smrg			return NULL;
923e88f27b3Smrg
924e88f27b3Smrg		for (j = 0; j < connector->count_encoders; ++j) {
925e88f27b3Smrg			encoder = get_encoder_by_id(dev, connector->encoders[j]);
926e88f27b3Smrg			if (!encoder)
927e88f27b3Smrg				continue;
928e88f27b3Smrg
929e88f27b3Smrg			crtcs_for_connector |= encoder->possible_crtcs;
93087bf8e7cSmrg			crtc = get_crtc_by_id(dev, encoder->crtc_id);
93187bf8e7cSmrg			if (!crtc)
93287bf8e7cSmrg				continue;
93387bf8e7cSmrg			active_crtcs |= get_crtc_mask(dev, crtc);
934e88f27b3Smrg		}
93518210155Smrg
936e88f27b3Smrg		possible_crtcs &= crtcs_for_connector;
93718210155Smrg	}
93818210155Smrg
939e88f27b3Smrg	if (!possible_crtcs)
940e88f27b3Smrg		return NULL;
941e88f27b3Smrg
942e88f27b3Smrg	/* Return the first possible and active CRTC if one exists, or the first
943e88f27b3Smrg	 * possible CRTC otherwise.
944e88f27b3Smrg	 */
945e88f27b3Smrg	if (possible_crtcs & active_crtcs)
946e88f27b3Smrg		crtc_idx = ffs(possible_crtcs & active_crtcs);
947e88f27b3Smrg	else
948e88f27b3Smrg		crtc_idx = ffs(possible_crtcs);
949e88f27b3Smrg
950e88f27b3Smrg	return &dev->resources->crtcs[crtc_idx - 1];
951e88f27b3Smrg}
952e88f27b3Smrg
953e88f27b3Smrgstatic int pipe_find_crtc_and_mode(struct device *dev, struct pipe_arg *pipe)
954e88f27b3Smrg{
955e88f27b3Smrg	drmModeModeInfo *mode = NULL;
956e88f27b3Smrg	int i;
957e88f27b3Smrg
958e88f27b3Smrg	pipe->mode = NULL;
959e88f27b3Smrg
960e88f27b3Smrg	for (i = 0; i < (int)pipe->num_cons; i++) {
961e88f27b3Smrg		mode = connector_find_mode(dev, pipe->con_ids[i],
962e88f27b3Smrg					   pipe->mode_str, pipe->vrefresh);
963e88f27b3Smrg		if (mode == NULL) {
96487bf8e7cSmrg			if (pipe->vrefresh)
96587bf8e7cSmrg				fprintf(stderr,
96687bf8e7cSmrg				"failed to find mode "
96787bf8e7cSmrg				"\"%s-%.2fHz\" for connector %s\n",
96887bf8e7cSmrg				pipe->mode_str, pipe->vrefresh, pipe->cons[i]);
96987bf8e7cSmrg			else
97087bf8e7cSmrg				fprintf(stderr,
971fe517fc9Smrg				"failed to find mode \"%s\" for connector %s\n",
972fe517fc9Smrg				pipe->mode_str, pipe->cons[i]);
973e88f27b3Smrg			return -EINVAL;
974e88f27b3Smrg		}
97518210155Smrg	}
97618210155Smrg
977e88f27b3Smrg	/* If the CRTC ID was specified, get the corresponding CRTC. Otherwise
978e88f27b3Smrg	 * locate a CRTC that can be attached to all the connectors.
979e88f27b3Smrg	 */
980e88f27b3Smrg	if (pipe->crtc_id != (uint32_t)-1) {
98187bf8e7cSmrg		pipe->crtc = get_crtc_by_id(dev, pipe->crtc_id);
982e88f27b3Smrg	} else {
983e88f27b3Smrg		pipe->crtc = pipe_find_crtc(dev, pipe);
98487bf8e7cSmrg		pipe->crtc_id = pipe->crtc->crtc->crtc_id;
98518210155Smrg	}
986d049871aSmrg
987e88f27b3Smrg	if (!pipe->crtc) {
988e88f27b3Smrg		fprintf(stderr, "failed to find CRTC for pipe\n");
989e88f27b3Smrg		return -EINVAL;
990e88f27b3Smrg	}
991d049871aSmrg
992e88f27b3Smrg	pipe->mode = mode;
993e88f27b3Smrg	pipe->crtc->mode = mode;
99418210155Smrg
99518210155Smrg	return 0;
99618210155Smrg}
99718210155Smrg
998e88f27b3Smrg/* -----------------------------------------------------------------------------
999e88f27b3Smrg * Properties
1000e88f27b3Smrg */
1001e88f27b3Smrg
1002e88f27b3Smrgstruct property_arg {
1003e88f27b3Smrg	uint32_t obj_id;
1004e88f27b3Smrg	uint32_t obj_type;
1005e88f27b3Smrg	char name[DRM_PROP_NAME_LEN+1];
1006e88f27b3Smrg	uint32_t prop_id;
1007e88f27b3Smrg	uint64_t value;
1008bf6cc7dcSmrg	bool optional;
1009e88f27b3Smrg};
1010e88f27b3Smrg
1011bf6cc7dcSmrgstatic bool set_property(struct device *dev, struct property_arg *p)
101222944501Smrg{
1013e88f27b3Smrg	drmModeObjectProperties *props = NULL;
1014e88f27b3Smrg	drmModePropertyRes **props_info = NULL;
1015e88f27b3Smrg	const char *obj_type;
1016e88f27b3Smrg	int ret;
1017e88f27b3Smrg	int i;
101822944501Smrg
1019e88f27b3Smrg	p->obj_type = 0;
1020e88f27b3Smrg	p->prop_id = 0;
1021e88f27b3Smrg
102287bf8e7cSmrg#define find_object(_res, type, Type)					\
1023e88f27b3Smrg	do {									\
102487bf8e7cSmrg		for (i = 0; i < (int)(_res)->count_##type##s; ++i) {	\
1025e88f27b3Smrg			struct type *obj = &(_res)->type##s[i];			\
1026e88f27b3Smrg			if (obj->type->type##_id != p->obj_id)			\
1027e88f27b3Smrg				continue;					\
1028e88f27b3Smrg			p->obj_type = DRM_MODE_OBJECT_##Type;			\
1029e88f27b3Smrg			obj_type = #Type;					\
1030e88f27b3Smrg			props = obj->props;					\
1031e88f27b3Smrg			props_info = obj->props_info;				\
1032e88f27b3Smrg		}								\
1033e88f27b3Smrg	} while(0)								\
1034e88f27b3Smrg
103587bf8e7cSmrg	find_object(dev->resources, crtc, CRTC);
1036e88f27b3Smrg	if (p->obj_type == 0)
103787bf8e7cSmrg		find_object(dev->resources, connector, CONNECTOR);
1038e88f27b3Smrg	if (p->obj_type == 0)
103987bf8e7cSmrg		find_object(dev->resources, plane, PLANE);
1040e88f27b3Smrg	if (p->obj_type == 0) {
1041e88f27b3Smrg		fprintf(stderr, "Object %i not found, can't set property\n",
1042e88f27b3Smrg			p->obj_id);
1043bf6cc7dcSmrg		return false;
1044e88f27b3Smrg	}
104522944501Smrg
1046e88f27b3Smrg	if (!props) {
1047e88f27b3Smrg		fprintf(stderr, "%s %i has no properties\n",
1048e88f27b3Smrg			obj_type, p->obj_id);
1049bf6cc7dcSmrg		return false;
105022944501Smrg	}
105122944501Smrg
1052e88f27b3Smrg	for (i = 0; i < (int)props->count_props; ++i) {
1053e88f27b3Smrg		if (!props_info[i])
1054e88f27b3Smrg			continue;
1055e88f27b3Smrg		if (strcmp(props_info[i]->name, p->name) == 0)
1056e88f27b3Smrg			break;
105722944501Smrg	}
105822944501Smrg
1059e88f27b3Smrg	if (i == (int)props->count_props) {
1060bf6cc7dcSmrg		if (!p->optional)
1061bf6cc7dcSmrg			fprintf(stderr, "%s %i has no %s property\n",
1062bf6cc7dcSmrg				obj_type, p->obj_id, p->name);
1063bf6cc7dcSmrg		return false;
1064e88f27b3Smrg	}
106522944501Smrg
1066e88f27b3Smrg	p->prop_id = props->props[i];
106722944501Smrg
10686260e5d5Smrg	if (!dev->use_atomic)
10696260e5d5Smrg		ret = drmModeObjectSetProperty(dev->fd, p->obj_id, p->obj_type,
10706260e5d5Smrg									   p->prop_id, p->value);
10716260e5d5Smrg	else
10726260e5d5Smrg		ret = drmModeAtomicAddProperty(dev->req, p->obj_id, p->prop_id, p->value);
10736260e5d5Smrg
1074e88f27b3Smrg	if (ret < 0)
1075e88f27b3Smrg		fprintf(stderr, "failed to set %s %i property %s to %" PRIu64 ": %s\n",
107648246ce7Smrg			obj_type, p->obj_id, p->name, p->value, strerror(-ret));
1077bf6cc7dcSmrg
1078bf6cc7dcSmrg	return true;
107922944501Smrg}
108022944501Smrg
1081e88f27b3Smrg/* -------------------------------------------------------------------------- */
1082e88f27b3Smrg
1083e88f27b3Smrgstatic void
108422944501Smrgpage_flip_handler(int fd, unsigned int frame,
108522944501Smrg		  unsigned int sec, unsigned int usec, void *data)
108622944501Smrg{
1087e88f27b3Smrg	struct pipe_arg *pipe;
108822944501Smrg	unsigned int new_fb_id;
108922944501Smrg	struct timeval end;
109022944501Smrg	double t;
109122944501Smrg
1092e88f27b3Smrg	pipe = data;
1093e88f27b3Smrg	if (pipe->current_fb_id == pipe->fb_id[0])
1094e88f27b3Smrg		new_fb_id = pipe->fb_id[1];
109522944501Smrg	else
1096e88f27b3Smrg		new_fb_id = pipe->fb_id[0];
1097e88f27b3Smrg
109887bf8e7cSmrg	drmModePageFlip(fd, pipe->crtc_id, new_fb_id,
1099e88f27b3Smrg			DRM_MODE_PAGE_FLIP_EVENT, pipe);
1100e88f27b3Smrg	pipe->current_fb_id = new_fb_id;
1101e88f27b3Smrg	pipe->swap_count++;
1102e88f27b3Smrg	if (pipe->swap_count == 60) {
110322944501Smrg		gettimeofday(&end, NULL);
110422944501Smrg		t = end.tv_sec + end.tv_usec * 1e-6 -
1105e88f27b3Smrg			(pipe->start.tv_sec + pipe->start.tv_usec * 1e-6);
1106e88f27b3Smrg		fprintf(stderr, "freq: %.02fHz\n", pipe->swap_count / t);
1107e88f27b3Smrg		pipe->swap_count = 0;
1108e88f27b3Smrg		pipe->start = end;
110922944501Smrg	}
111022944501Smrg}
111122944501Smrg
1112424e9256Smrgstatic bool format_support(const drmModePlanePtr ovr, uint32_t fmt)
1113424e9256Smrg{
1114424e9256Smrg	unsigned int i;
1115424e9256Smrg
1116424e9256Smrg	for (i = 0; i < ovr->count_formats; ++i) {
1117424e9256Smrg		if (ovr->formats[i] == fmt)
1118424e9256Smrg			return true;
1119424e9256Smrg	}
1120424e9256Smrg
1121424e9256Smrg	return false;
1122424e9256Smrg}
1123424e9256Smrg
11246260e5d5Smrgstatic void add_property(struct device *dev, uint32_t obj_id,
11256260e5d5Smrg			       const char *name, uint64_t value)
11266260e5d5Smrg{
11276260e5d5Smrg	struct property_arg p;
11286260e5d5Smrg
11296260e5d5Smrg	p.obj_id = obj_id;
11306260e5d5Smrg	strcpy(p.name, name);
11316260e5d5Smrg	p.value = value;
11326260e5d5Smrg
11336260e5d5Smrg	set_property(dev, &p);
11346260e5d5Smrg}
11356260e5d5Smrg
1136bf6cc7dcSmrgstatic bool add_property_optional(struct device *dev, uint32_t obj_id,
1137bf6cc7dcSmrg				  const char *name, uint64_t value)
1138bf6cc7dcSmrg{
1139bf6cc7dcSmrg	struct property_arg p;
1140bf6cc7dcSmrg
1141bf6cc7dcSmrg	p.obj_id = obj_id;
1142bf6cc7dcSmrg	strcpy(p.name, name);
1143bf6cc7dcSmrg	p.value = value;
1144bf6cc7dcSmrg	p.optional = true;
1145bf6cc7dcSmrg
1146bf6cc7dcSmrg	return set_property(dev, &p);
1147bf6cc7dcSmrg}
1148bf6cc7dcSmrg
1149bf6cc7dcSmrgstatic void set_gamma(struct device *dev, unsigned crtc_id, unsigned fourcc)
1150bf6cc7dcSmrg{
1151bf6cc7dcSmrg	unsigned blob_id = 0;
115248246ce7Smrg	const struct util_format_info *info;
1153bf6cc7dcSmrg	/* TODO: support 1024-sized LUTs, when the use-case arises */
1154bf6cc7dcSmrg	struct drm_color_lut gamma_lut[256];
1155bf6cc7dcSmrg	int i, ret;
1156bf6cc7dcSmrg
115748246ce7Smrg	info = util_format_info_find(fourcc);
115848246ce7Smrg	if (info->ncolors) {
115948246ce7Smrg		memset(gamma_lut, 0, sizeof(gamma_lut));
116048246ce7Smrg		/* TODO: Add index support for more patterns */
116148246ce7Smrg		util_smpte_fill_lut(info->ncolors, gamma_lut);
1162bf6cc7dcSmrg		drmModeCreatePropertyBlob(dev->fd, gamma_lut, sizeof(gamma_lut), &blob_id);
1163bf6cc7dcSmrg	} else {
116448246ce7Smrg		/*
116548246ce7Smrg		 * Initialize gamma_lut to a linear table for the legacy API below.
116648246ce7Smrg		 * The modern property API resets to a linear/pass-thru table if blob_id
116748246ce7Smrg		 * is 0, hence no PropertyBlob is created here.
116848246ce7Smrg		 */
1169bf6cc7dcSmrg		for (i = 0; i < 256; i++) {
1170bf6cc7dcSmrg			gamma_lut[i].red =
1171bf6cc7dcSmrg			gamma_lut[i].green =
1172bf6cc7dcSmrg			gamma_lut[i].blue = i << 8;
1173bf6cc7dcSmrg		}
1174bf6cc7dcSmrg	}
1175bf6cc7dcSmrg
1176bf6cc7dcSmrg	add_property_optional(dev, crtc_id, "DEGAMMA_LUT", 0);
1177bf6cc7dcSmrg	add_property_optional(dev, crtc_id, "CTM", 0);
1178bf6cc7dcSmrg	if (!add_property_optional(dev, crtc_id, "GAMMA_LUT", blob_id)) {
117948246ce7Smrg		/* If we can't add the GAMMA_LUT property, try the legacy API. */
1180bf6cc7dcSmrg		uint16_t r[256], g[256], b[256];
1181bf6cc7dcSmrg
1182bf6cc7dcSmrg		for (i = 0; i < 256; i++) {
1183bf6cc7dcSmrg			r[i] = gamma_lut[i].red;
1184bf6cc7dcSmrg			g[i] = gamma_lut[i].green;
1185bf6cc7dcSmrg			b[i] = gamma_lut[i].blue;
1186bf6cc7dcSmrg		}
1187bf6cc7dcSmrg
1188bf6cc7dcSmrg		ret = drmModeCrtcSetGamma(dev->fd, crtc_id, 256, r, g, b);
118948246ce7Smrg		if (ret && errno != ENOSYS)
1190bf6cc7dcSmrg			fprintf(stderr, "failed to set gamma: %s\n", strerror(errno));
1191bf6cc7dcSmrg	}
1192bf6cc7dcSmrg}
1193bf6cc7dcSmrg
119487bf8e7cSmrgstatic int
119587bf8e7cSmrgbo_fb_create(int fd, unsigned int fourcc, const uint32_t w, const uint32_t h,
119687bf8e7cSmrg             enum util_fill_pattern pat, struct bo **out_bo, unsigned int *out_fb_id)
119787bf8e7cSmrg{
119887bf8e7cSmrg	uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
119987bf8e7cSmrg	struct bo *bo;
120087bf8e7cSmrg	unsigned int fb_id;
120187bf8e7cSmrg
120287bf8e7cSmrg	bo = bo_create(fd, fourcc, w, h, handles, pitches, offsets, pat);
120387bf8e7cSmrg
120487bf8e7cSmrg	if (bo == NULL)
120587bf8e7cSmrg		return -1;
120687bf8e7cSmrg
120787bf8e7cSmrg	if (drmModeAddFB2(fd, w, h, fourcc, handles, pitches, offsets, &fb_id, 0)) {
120887bf8e7cSmrg		fprintf(stderr, "failed to add fb (%ux%u): %s\n", w, h, strerror(errno));
120987bf8e7cSmrg		bo_destroy(bo);
121087bf8e7cSmrg		return -1;
121187bf8e7cSmrg	}
121287bf8e7cSmrg	*out_bo = bo;
121387bf8e7cSmrg	*out_fb_id = fb_id;
121487bf8e7cSmrg	return 0;
121587bf8e7cSmrg}
121687bf8e7cSmrg
12176260e5d5Smrgstatic int atomic_set_plane(struct device *dev, struct plane_arg *p,
12186260e5d5Smrg							int pattern, bool update)
12196260e5d5Smrg{
12206260e5d5Smrg	struct bo *plane_bo;
12216260e5d5Smrg	int crtc_x, crtc_y, crtc_w, crtc_h;
12226260e5d5Smrg	struct crtc *crtc = NULL;
12236260e5d5Smrg	unsigned int old_fb_id;
12246260e5d5Smrg
12256260e5d5Smrg	/* Find an unused plane which can be connected to our CRTC. Find the
12266260e5d5Smrg	 * CRTC index first, then iterate over available planes.
12276260e5d5Smrg	 */
122887bf8e7cSmrg	crtc = get_crtc_by_id(dev, p->crtc_id);
12296260e5d5Smrg	if (!crtc) {
12306260e5d5Smrg		fprintf(stderr, "CRTC %u not found\n", p->crtc_id);
12316260e5d5Smrg		return -1;
12326260e5d5Smrg	}
12336260e5d5Smrg
12346260e5d5Smrg	if (!update)
12356260e5d5Smrg		fprintf(stderr, "testing %dx%d@%s on plane %u, crtc %u\n",
12366260e5d5Smrg			p->w, p->h, p->format_str, p->plane_id, p->crtc_id);
12376260e5d5Smrg
12386260e5d5Smrg	plane_bo = p->old_bo;
12396260e5d5Smrg	p->old_bo = p->bo;
12406260e5d5Smrg
12416260e5d5Smrg	if (!plane_bo) {
124287bf8e7cSmrg		if (bo_fb_create(dev->fd, p->fourcc, p->w, p->h,
124387bf8e7cSmrg                         pattern, &plane_bo, &p->fb_id))
12446260e5d5Smrg			return -1;
12456260e5d5Smrg	}
12466260e5d5Smrg
12476260e5d5Smrg	p->bo = plane_bo;
12486260e5d5Smrg
12496260e5d5Smrg	old_fb_id = p->fb_id;
12506260e5d5Smrg	p->old_fb_id = old_fb_id;
12516260e5d5Smrg
12526260e5d5Smrg	crtc_w = p->w * p->scale;
12536260e5d5Smrg	crtc_h = p->h * p->scale;
12546260e5d5Smrg	if (!p->has_position) {
12556260e5d5Smrg		/* Default to the middle of the screen */
12566260e5d5Smrg		crtc_x = (crtc->mode->hdisplay - crtc_w) / 2;
12576260e5d5Smrg		crtc_y = (crtc->mode->vdisplay - crtc_h) / 2;
12586260e5d5Smrg	} else {
12596260e5d5Smrg		crtc_x = p->x;
12606260e5d5Smrg		crtc_y = p->y;
12616260e5d5Smrg	}
12626260e5d5Smrg
12636260e5d5Smrg	add_property(dev, p->plane_id, "FB_ID", p->fb_id);
12646260e5d5Smrg	add_property(dev, p->plane_id, "CRTC_ID", p->crtc_id);
12656260e5d5Smrg	add_property(dev, p->plane_id, "SRC_X", 0);
12666260e5d5Smrg	add_property(dev, p->plane_id, "SRC_Y", 0);
12676260e5d5Smrg	add_property(dev, p->plane_id, "SRC_W", p->w << 16);
12686260e5d5Smrg	add_property(dev, p->plane_id, "SRC_H", p->h << 16);
12696260e5d5Smrg	add_property(dev, p->plane_id, "CRTC_X", crtc_x);
12706260e5d5Smrg	add_property(dev, p->plane_id, "CRTC_Y", crtc_y);
12716260e5d5Smrg	add_property(dev, p->plane_id, "CRTC_W", crtc_w);
12726260e5d5Smrg	add_property(dev, p->plane_id, "CRTC_H", crtc_h);
12736260e5d5Smrg
12746260e5d5Smrg	return 0;
12756260e5d5Smrg}
12766260e5d5Smrg
1277e88f27b3Smrgstatic int set_plane(struct device *dev, struct plane_arg *p)
127818210155Smrg{
1279e88f27b3Smrg	drmModePlane *ovr;
12802ee35494Smrg	uint32_t plane_id;
1281e88f27b3Smrg	int crtc_x, crtc_y, crtc_w, crtc_h;
1282e88f27b3Smrg	struct crtc *crtc = NULL;
128387bf8e7cSmrg	unsigned int i, crtc_mask;
1284e88f27b3Smrg
1285e88f27b3Smrg	/* Find an unused plane which can be connected to our CRTC. Find the
1286e88f27b3Smrg	 * CRTC index first, then iterate over available planes.
1287e88f27b3Smrg	 */
128887bf8e7cSmrg	crtc = get_crtc_by_id(dev, p->crtc_id);
1289e88f27b3Smrg	if (!crtc) {
1290e88f27b3Smrg		fprintf(stderr, "CRTC %u not found\n", p->crtc_id);
1291e88f27b3Smrg		return -1;
1292e88f27b3Smrg	}
129387bf8e7cSmrg	crtc_mask = get_crtc_mask(dev, crtc);
12942ee35494Smrg	plane_id = p->plane_id;
12952ee35494Smrg
129687bf8e7cSmrg	for (i = 0; i < dev->resources->count_planes; i++) {
1297e88f27b3Smrg		ovr = dev->resources->planes[i].plane;
12982ee35494Smrg		if (!ovr)
12992ee35494Smrg			continue;
13002ee35494Smrg
13012ee35494Smrg		if (plane_id && plane_id != ovr->plane_id)
13022ee35494Smrg			continue;
13032ee35494Smrg
13042ee35494Smrg		if (!format_support(ovr, p->fourcc))
130518210155Smrg			continue;
1306e88f27b3Smrg
130787bf8e7cSmrg		if ((ovr->possible_crtcs & crtc_mask) &&
13082b90624aSmrg		    (ovr->crtc_id == 0 || ovr->crtc_id == p->crtc_id)) {
1309e88f27b3Smrg			plane_id = ovr->plane_id;
13102ee35494Smrg			break;
13112ee35494Smrg		}
131218210155Smrg	}
131318210155Smrg
131487bf8e7cSmrg	if (i == dev->resources->count_planes) {
1315e88f27b3Smrg		fprintf(stderr, "no unused plane available for CRTC %u\n",
131687bf8e7cSmrg			p->crtc_id);
1317e88f27b3Smrg		return -1;
1318e88f27b3Smrg	}
1319e88f27b3Smrg
1320e88f27b3Smrg	fprintf(stderr, "testing %dx%d@%s overlay plane %u\n",
1321e88f27b3Smrg		p->w, p->h, p->format_str, plane_id);
1322e88f27b3Smrg
1323e88f27b3Smrg	/* just use single plane format for now.. */
132487bf8e7cSmrg	if (bo_fb_create(dev->fd, p->fourcc, p->w, p->h,
132587bf8e7cSmrg	                 secondary_fill, &p->bo, &p->fb_id))
1326e88f27b3Smrg		return -1;
1327e88f27b3Smrg
1328e88f27b3Smrg	crtc_w = p->w * p->scale;
1329e88f27b3Smrg	crtc_h = p->h * p->scale;
1330e88f27b3Smrg	if (!p->has_position) {
1331e88f27b3Smrg		/* Default to the middle of the screen */
1332e88f27b3Smrg		crtc_x = (crtc->mode->hdisplay - crtc_w) / 2;
1333e88f27b3Smrg		crtc_y = (crtc->mode->vdisplay - crtc_h) / 2;
1334e88f27b3Smrg	} else {
1335e88f27b3Smrg		crtc_x = p->x;
1336e88f27b3Smrg		crtc_y = p->y;
133718210155Smrg	}
133818210155Smrg
1339e88f27b3Smrg	/* note src coords (last 4 args) are in Q16 format */
134087bf8e7cSmrg	if (drmModeSetPlane(dev->fd, plane_id, p->crtc_id, p->fb_id,
134187bf8e7cSmrg			    0, crtc_x, crtc_y, crtc_w, crtc_h,
1342e88f27b3Smrg			    0, 0, p->w << 16, p->h << 16)) {
1343e88f27b3Smrg		fprintf(stderr, "failed to enable plane: %s\n",
1344e88f27b3Smrg			strerror(errno));
1345e88f27b3Smrg		return -1;
1346e88f27b3Smrg	}
1347e88f27b3Smrg
134887bf8e7cSmrg	ovr->crtc_id = p->crtc_id;
1349e88f27b3Smrg
1350e88f27b3Smrg	return 0;
1351e88f27b3Smrg}
1352e88f27b3Smrg
13536260e5d5Smrgstatic void atomic_set_planes(struct device *dev, struct plane_arg *p,
13546260e5d5Smrg			      unsigned int count, bool update)
13556260e5d5Smrg{
1356bf6cc7dcSmrg	unsigned int i, pattern = primary_fill;
13576260e5d5Smrg
13586260e5d5Smrg	/* set up planes */
13596260e5d5Smrg	for (i = 0; i < count; i++) {
13606260e5d5Smrg		if (i > 0)
1361bf6cc7dcSmrg			pattern = secondary_fill;
1362bf6cc7dcSmrg		else
1363bf6cc7dcSmrg			set_gamma(dev, p[i].crtc_id, p[i].fourcc);
13646260e5d5Smrg
13656260e5d5Smrg		if (atomic_set_plane(dev, &p[i], pattern, update))
13666260e5d5Smrg			return;
13676260e5d5Smrg	}
13686260e5d5Smrg}
13696260e5d5Smrg
137087bf8e7cSmrgstatic void
137187bf8e7cSmrgatomic_test_page_flip(struct device *dev, struct pipe_arg *pipe_args,
137287bf8e7cSmrg              struct plane_arg *plane_args, unsigned int plane_count)
137387bf8e7cSmrg{
137487bf8e7cSmrg    int ret;
137587bf8e7cSmrg
137687bf8e7cSmrg	gettimeofday(&pipe_args->start, NULL);
137787bf8e7cSmrg	pipe_args->swap_count = 0;
137887bf8e7cSmrg
137987bf8e7cSmrg	while (true) {
138087bf8e7cSmrg		drmModeAtomicFree(dev->req);
138187bf8e7cSmrg		dev->req = drmModeAtomicAlloc();
138287bf8e7cSmrg		atomic_set_planes(dev, plane_args, plane_count, true);
138387bf8e7cSmrg
138487bf8e7cSmrg		ret = drmModeAtomicCommit(dev->fd, dev->req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
138587bf8e7cSmrg		if (ret) {
138687bf8e7cSmrg			fprintf(stderr, "Atomic Commit failed [2]\n");
138787bf8e7cSmrg			return;
138887bf8e7cSmrg		}
138987bf8e7cSmrg
139087bf8e7cSmrg		pipe_args->swap_count++;
139187bf8e7cSmrg		if (pipe_args->swap_count == 60) {
139287bf8e7cSmrg			struct timeval end;
139387bf8e7cSmrg			double t;
139487bf8e7cSmrg
139587bf8e7cSmrg			gettimeofday(&end, NULL);
139687bf8e7cSmrg			t = end.tv_sec + end.tv_usec * 1e-6 -
139787bf8e7cSmrg			    (pipe_args->start.tv_sec + pipe_args->start.tv_usec * 1e-6);
139887bf8e7cSmrg			fprintf(stderr, "freq: %.02fHz\n", pipe_args->swap_count / t);
139987bf8e7cSmrg			pipe_args->swap_count = 0;
140087bf8e7cSmrg			pipe_args->start = end;
140187bf8e7cSmrg		}
140287bf8e7cSmrg	}
140387bf8e7cSmrg}
140487bf8e7cSmrg
14056260e5d5Smrgstatic void atomic_clear_planes(struct device *dev, struct plane_arg *p, unsigned int count)
14066260e5d5Smrg{
14076260e5d5Smrg	unsigned int i;
14086260e5d5Smrg
14096260e5d5Smrg	for (i = 0; i < count; i++) {
14106260e5d5Smrg		add_property(dev, p[i].plane_id, "FB_ID", 0);
14116260e5d5Smrg		add_property(dev, p[i].plane_id, "CRTC_ID", 0);
14126260e5d5Smrg		add_property(dev, p[i].plane_id, "SRC_X", 0);
14136260e5d5Smrg		add_property(dev, p[i].plane_id, "SRC_Y", 0);
14146260e5d5Smrg		add_property(dev, p[i].plane_id, "SRC_W", 0);
14156260e5d5Smrg		add_property(dev, p[i].plane_id, "SRC_H", 0);
14166260e5d5Smrg		add_property(dev, p[i].plane_id, "CRTC_X", 0);
14176260e5d5Smrg		add_property(dev, p[i].plane_id, "CRTC_Y", 0);
14186260e5d5Smrg		add_property(dev, p[i].plane_id, "CRTC_W", 0);
14196260e5d5Smrg		add_property(dev, p[i].plane_id, "CRTC_H", 0);
14206260e5d5Smrg	}
14216260e5d5Smrg}
14226260e5d5Smrg
14236260e5d5Smrgstatic void atomic_clear_FB(struct device *dev, struct plane_arg *p, unsigned int count)
14246260e5d5Smrg{
14256260e5d5Smrg	unsigned int i;
14266260e5d5Smrg
14276260e5d5Smrg	for (i = 0; i < count; i++) {
14286260e5d5Smrg		if (p[i].fb_id) {
14296260e5d5Smrg			drmModeRmFB(dev->fd, p[i].fb_id);
14306260e5d5Smrg			p[i].fb_id = 0;
14316260e5d5Smrg		}
14326260e5d5Smrg		if (p[i].old_fb_id) {
14336260e5d5Smrg			drmModeRmFB(dev->fd, p[i].old_fb_id);
14346260e5d5Smrg			p[i].old_fb_id = 0;
14356260e5d5Smrg		}
14366260e5d5Smrg		if (p[i].bo) {
14376260e5d5Smrg			bo_destroy(p[i].bo);
14386260e5d5Smrg			p[i].bo = NULL;
14396260e5d5Smrg		}
14406260e5d5Smrg		if (p[i].old_bo) {
14416260e5d5Smrg			bo_destroy(p[i].old_bo);
14426260e5d5Smrg			p[i].old_bo = NULL;
14436260e5d5Smrg		}
14446260e5d5Smrg
14456260e5d5Smrg	}
14466260e5d5Smrg}
14476260e5d5Smrg
1448424e9256Smrgstatic void clear_planes(struct device *dev, struct plane_arg *p, unsigned int count)
1449424e9256Smrg{
1450424e9256Smrg	unsigned int i;
1451424e9256Smrg
1452424e9256Smrg	for (i = 0; i < count; i++) {
1453424e9256Smrg		if (p[i].fb_id)
1454424e9256Smrg			drmModeRmFB(dev->fd, p[i].fb_id);
1455424e9256Smrg		if (p[i].bo)
1456424e9256Smrg			bo_destroy(p[i].bo);
1457424e9256Smrg	}
1458424e9256Smrg}
1459424e9256Smrg
146087bf8e7cSmrgstatic int pipe_resolve_connectors(struct device *dev, struct pipe_arg *pipe)
14616260e5d5Smrg{
146287bf8e7cSmrg	drmModeConnector *connector;
14636260e5d5Smrg	unsigned int i;
146487bf8e7cSmrg	uint32_t id;
146587bf8e7cSmrg	char *endp;
14666260e5d5Smrg
146787bf8e7cSmrg	for (i = 0; i < pipe->num_cons; i++) {
146887bf8e7cSmrg		id = strtoul(pipe->cons[i], &endp, 10);
146987bf8e7cSmrg		if (endp == pipe->cons[i]) {
147087bf8e7cSmrg			connector = get_connector_by_name(dev, pipe->cons[i]);
147187bf8e7cSmrg			if (!connector) {
147287bf8e7cSmrg				fprintf(stderr, "no connector named '%s'\n",
147387bf8e7cSmrg					pipe->cons[i]);
147487bf8e7cSmrg				return -ENODEV;
147587bf8e7cSmrg			}
14766260e5d5Smrg
147787bf8e7cSmrg			id = connector->connector_id;
147887bf8e7cSmrg		}
147987bf8e7cSmrg
148087bf8e7cSmrg		pipe->con_ids[i] = id;
148187bf8e7cSmrg	}
148287bf8e7cSmrg
148387bf8e7cSmrg	return 0;
148487bf8e7cSmrg}
148587bf8e7cSmrg
148648246ce7Smrgstatic bool pipe_has_writeback_connector(struct device *dev, struct pipe_arg *pipes,
148748246ce7Smrg		unsigned int count)
148848246ce7Smrg{
148948246ce7Smrg	drmModeConnector *connector;
149048246ce7Smrg	unsigned int i, j;
149148246ce7Smrg
149248246ce7Smrg	for (j = 0; j < count; j++) {
149348246ce7Smrg		struct pipe_arg *pipe = &pipes[j];
149448246ce7Smrg
149548246ce7Smrg		for (i = 0; i < pipe->num_cons; i++) {
149648246ce7Smrg			connector = get_connector_by_id(dev, pipe->con_ids[i]);
149748246ce7Smrg			if (connector && connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
149848246ce7Smrg				return true;
149948246ce7Smrg		}
150048246ce7Smrg	}
150148246ce7Smrg	return false;
150248246ce7Smrg}
150348246ce7Smrg
150487bf8e7cSmrgstatic int pipe_attempt_connector(struct device *dev, drmModeConnector *con,
150587bf8e7cSmrg		struct pipe_arg *pipe)
150687bf8e7cSmrg{
150787bf8e7cSmrg	char *con_str;
150887bf8e7cSmrg	int i;
150987bf8e7cSmrg
151087bf8e7cSmrg	con_str = calloc(8, sizeof(char));
151187bf8e7cSmrg	if (!con_str)
151287bf8e7cSmrg		return -1;
151387bf8e7cSmrg
151487bf8e7cSmrg	sprintf(con_str, "%d", con->connector_id);
151587bf8e7cSmrg	strcpy(pipe->format_str, "XR24");
151687bf8e7cSmrg	pipe->fourcc = util_format_fourcc(pipe->format_str);
151787bf8e7cSmrg	pipe->num_cons = 1;
151887bf8e7cSmrg	pipe->con_ids = calloc(1, sizeof(*pipe->con_ids));
151987bf8e7cSmrg	pipe->cons = calloc(1, sizeof(*pipe->cons));
152087bf8e7cSmrg
152187bf8e7cSmrg	if (!pipe->con_ids || !pipe->cons)
152287bf8e7cSmrg		goto free_con_str;
152387bf8e7cSmrg
152487bf8e7cSmrg	pipe->con_ids[0] = con->connector_id;
152587bf8e7cSmrg	pipe->cons[0] = (const char*)con_str;
152687bf8e7cSmrg
152787bf8e7cSmrg	pipe->crtc = pipe_find_crtc(dev, pipe);
152887bf8e7cSmrg	if (!pipe->crtc)
152987bf8e7cSmrg		goto free_all;
153087bf8e7cSmrg
153187bf8e7cSmrg	pipe->crtc_id = pipe->crtc->crtc->crtc_id;
153287bf8e7cSmrg
153387bf8e7cSmrg	/* Return the first mode if no preferred. */
153487bf8e7cSmrg	pipe->mode = &con->modes[0];
153587bf8e7cSmrg
153687bf8e7cSmrg	for (i = 0; i < con->count_modes; i++) {
153787bf8e7cSmrg		drmModeModeInfo *current_mode = &con->modes[i];
153887bf8e7cSmrg
153987bf8e7cSmrg		if (current_mode->type & DRM_MODE_TYPE_PREFERRED) {
154087bf8e7cSmrg			pipe->mode = current_mode;
154187bf8e7cSmrg			break;
154287bf8e7cSmrg		}
154387bf8e7cSmrg	}
154487bf8e7cSmrg
154587bf8e7cSmrg	sprintf(pipe->mode_str, "%dx%d", pipe->mode->hdisplay, pipe->mode->vdisplay);
154687bf8e7cSmrg
154787bf8e7cSmrg	return 0;
154887bf8e7cSmrg
154987bf8e7cSmrgfree_all:
155087bf8e7cSmrg	free(pipe->cons);
155187bf8e7cSmrg	free(pipe->con_ids);
155287bf8e7cSmrgfree_con_str:
155387bf8e7cSmrg	free(con_str);
155487bf8e7cSmrg	return -1;
155587bf8e7cSmrg}
155687bf8e7cSmrg
155787bf8e7cSmrgstatic int pipe_find_preferred(struct device *dev, struct pipe_arg **out_pipes)
155887bf8e7cSmrg{
155987bf8e7cSmrg	struct pipe_arg *pipes;
156087bf8e7cSmrg	struct resources *res = dev->resources;
156187bf8e7cSmrg	drmModeConnector *con = NULL;
156287bf8e7cSmrg	int i, connected = 0, attempted = 0;
156387bf8e7cSmrg
156487bf8e7cSmrg	for (i = 0; i < res->count_connectors; i++) {
156587bf8e7cSmrg		con = res->connectors[i].connector;
156648246ce7Smrg		if (!con || con->connection != DRM_MODE_CONNECTED ||
156748246ce7Smrg		    con->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
15686260e5d5Smrg			continue;
156987bf8e7cSmrg		connected++;
157087bf8e7cSmrg	}
157187bf8e7cSmrg	if (!connected) {
157287bf8e7cSmrg		printf("no connected connector!\n");
157387bf8e7cSmrg		return 0;
15746260e5d5Smrg	}
15756260e5d5Smrg
157687bf8e7cSmrg	pipes = calloc(connected, sizeof(struct pipe_arg));
157787bf8e7cSmrg	if (!pipes)
157887bf8e7cSmrg		return 0;
15796260e5d5Smrg
158087bf8e7cSmrg	for (i = 0; i < res->count_connectors && attempted < connected; i++) {
158187bf8e7cSmrg		con = res->connectors[i].connector;
158287bf8e7cSmrg		if (!con || con->connection != DRM_MODE_CONNECTED)
15836260e5d5Smrg			continue;
15846260e5d5Smrg
158587bf8e7cSmrg		if (pipe_attempt_connector(dev, con, &pipes[attempted]) < 0) {
158687bf8e7cSmrg			printf("failed fetching preferred mode for connector\n");
158787bf8e7cSmrg			continue;
15886260e5d5Smrg		}
158987bf8e7cSmrg		attempted++;
15906260e5d5Smrg	}
159187bf8e7cSmrg
159287bf8e7cSmrg	*out_pipes = pipes;
159387bf8e7cSmrg	return attempted;
15946260e5d5Smrg}
15956260e5d5Smrg
159687bf8e7cSmrgstatic struct plane *get_primary_plane_by_crtc(struct device *dev, struct crtc *crtc)
15976260e5d5Smrg{
15986260e5d5Smrg	unsigned int i;
15996260e5d5Smrg
160087bf8e7cSmrg	for (i = 0; i < dev->resources->count_planes; i++) {
160187bf8e7cSmrg		struct plane *plane = &dev->resources->planes[i];
160287bf8e7cSmrg		drmModePlane *ovr = plane->plane;
160387bf8e7cSmrg		if (!ovr)
16046260e5d5Smrg			continue;
16056260e5d5Smrg
160687bf8e7cSmrg		// XXX: add is_primary_plane and (?) format checks
16076260e5d5Smrg
160887bf8e7cSmrg		if (ovr->possible_crtcs & get_crtc_mask(dev, crtc))
160987bf8e7cSmrg            return plane;
16106260e5d5Smrg	}
161187bf8e7cSmrg	return NULL;
16126260e5d5Smrg}
1613424e9256Smrg
161448246ce7Smrgstatic unsigned int set_mode(struct device *dev, struct pipe_arg **pipe_args, unsigned int count)
1615e88f27b3Smrg{
161687bf8e7cSmrg	unsigned int i, j;
161787bf8e7cSmrg	int ret, x = 0;
161887bf8e7cSmrg	int preferred = count == 0;
161948246ce7Smrg	struct pipe_arg *pipes;
1620e88f27b3Smrg
162187bf8e7cSmrg	if (preferred) {
162248246ce7Smrg		count = pipe_find_preferred(dev, pipe_args);
162387bf8e7cSmrg		if (!count) {
162487bf8e7cSmrg			fprintf(stderr, "can't find any preferred connector/mode.\n");
162548246ce7Smrg			return 0;
162648246ce7Smrg		}
162748246ce7Smrg
162848246ce7Smrg		pipes = *pipe_args;
162948246ce7Smrg	} else {
163048246ce7Smrg		pipes = *pipe_args;
163148246ce7Smrg
163248246ce7Smrg		for (i = 0; i < count; i++) {
163348246ce7Smrg			struct pipe_arg *pipe = &pipes[i];
163448246ce7Smrg
163548246ce7Smrg			ret = pipe_resolve_connectors(dev, pipe);
163648246ce7Smrg			if (ret < 0)
163748246ce7Smrg				return 0;
163848246ce7Smrg
163948246ce7Smrg			ret = pipe_find_crtc_and_mode(dev, pipe);
164048246ce7Smrg			if (ret < 0)
164148246ce7Smrg				continue;
164287bf8e7cSmrg		}
1643e88f27b3Smrg	}
1644e88f27b3Smrg
164587bf8e7cSmrg	if (!dev->use_atomic) {
164687bf8e7cSmrg		for (i = 0; i < count; i++) {
164787bf8e7cSmrg			struct pipe_arg *pipe = &pipes[i];
164818210155Smrg
164987bf8e7cSmrg			if (pipe->mode == NULL)
165087bf8e7cSmrg				continue;
1651424e9256Smrg
165287bf8e7cSmrg			if (!preferred) {
165387bf8e7cSmrg				dev->mode.width += pipe->mode->hdisplay;
165487bf8e7cSmrg				if (dev->mode.height < pipe->mode->vdisplay)
165587bf8e7cSmrg					dev->mode.height = pipe->mode->vdisplay;
165687bf8e7cSmrg			} else {
165787bf8e7cSmrg				/* XXX: Use a clone mode, more like atomic. We could do per
165887bf8e7cSmrg				 * connector bo/fb, so we don't have the stretched image.
165987bf8e7cSmrg				 */
166087bf8e7cSmrg				if (dev->mode.width < pipe->mode->hdisplay)
166187bf8e7cSmrg					dev->mode.width = pipe->mode->hdisplay;
166287bf8e7cSmrg				if (dev->mode.height < pipe->mode->vdisplay)
166387bf8e7cSmrg					dev->mode.height = pipe->mode->vdisplay;
166487bf8e7cSmrg			}
166587bf8e7cSmrg		}
166618210155Smrg
166787bf8e7cSmrg		if (bo_fb_create(dev->fd, pipes[0].fourcc, dev->mode.width, dev->mode.height,
166887bf8e7cSmrg			             primary_fill, &dev->mode.bo, &dev->mode.fb_id))
166948246ce7Smrg			return 0;
167087bf8e7cSmrg	}
1671424e9256Smrg
167218210155Smrg	for (i = 0; i < count; i++) {
1673e88f27b3Smrg		struct pipe_arg *pipe = &pipes[i];
167487bf8e7cSmrg		uint32_t blob_id;
1675e88f27b3Smrg
1676e88f27b3Smrg		if (pipe->mode == NULL)
167718210155Smrg			continue;
167818210155Smrg
167987bf8e7cSmrg		printf("setting mode %s-%.2fHz on connectors ",
168087bf8e7cSmrg		       pipe->mode->name, mode_vrefresh(pipe->mode));
168187bf8e7cSmrg		for (j = 0; j < pipe->num_cons; ++j) {
1682fe517fc9Smrg			printf("%s, ", pipe->cons[j]);
168387bf8e7cSmrg			if (dev->use_atomic)
168487bf8e7cSmrg				add_property(dev, pipe->con_ids[j], "CRTC_ID", pipe->crtc_id);
168587bf8e7cSmrg		}
168687bf8e7cSmrg		printf("crtc %d\n", pipe->crtc_id);
1687e88f27b3Smrg
168887bf8e7cSmrg		if (!dev->use_atomic) {
168987bf8e7cSmrg			ret = drmModeSetCrtc(dev->fd, pipe->crtc_id, dev->mode.fb_id,
169087bf8e7cSmrg								 x, 0, pipe->con_ids, pipe->num_cons,
169187bf8e7cSmrg								 pipe->mode);
1692e88f27b3Smrg
169387bf8e7cSmrg			/* XXX: Actually check if this is needed */
169487bf8e7cSmrg			drmModeDirtyFB(dev->fd, dev->mode.fb_id, NULL, 0);
169518210155Smrg
169687bf8e7cSmrg			if (!preferred)
169787bf8e7cSmrg				x += pipe->mode->hdisplay;
169818210155Smrg
169987bf8e7cSmrg			if (ret) {
170087bf8e7cSmrg				fprintf(stderr, "failed to set mode: %s\n", strerror(errno));
170148246ce7Smrg				return 0;
170287bf8e7cSmrg			}
170387bf8e7cSmrg
170487bf8e7cSmrg			set_gamma(dev, pipe->crtc_id, pipe->fourcc);
170587bf8e7cSmrg		} else {
170687bf8e7cSmrg			drmModeCreatePropertyBlob(dev->fd, pipe->mode, sizeof(*pipe->mode), &blob_id);
170787bf8e7cSmrg			add_property(dev, pipe->crtc_id, "MODE_ID", blob_id);
170887bf8e7cSmrg			add_property(dev, pipe->crtc_id, "ACTIVE", 1);
170987bf8e7cSmrg
171087bf8e7cSmrg			/* By default atomic modeset does not set a primary plane, shrug */
171187bf8e7cSmrg			if (preferred) {
171287bf8e7cSmrg				struct plane *plane = get_primary_plane_by_crtc(dev, pipe->crtc);
171387bf8e7cSmrg				struct plane_arg plane_args = {
171487bf8e7cSmrg					.plane_id = plane->plane->plane_id,
171587bf8e7cSmrg					.crtc_id = pipe->crtc_id,
171687bf8e7cSmrg					.w = pipe->mode->hdisplay,
171787bf8e7cSmrg					.h = pipe->mode->vdisplay,
171887bf8e7cSmrg					.scale = 1.0,
171987bf8e7cSmrg					.format_str = "XR24",
172087bf8e7cSmrg					.fourcc = util_format_fourcc(pipe->format_str),
172187bf8e7cSmrg				};
172287bf8e7cSmrg
172387bf8e7cSmrg				atomic_set_planes(dev, &plane_args, 1, false);
172487bf8e7cSmrg			}
172518210155Smrg		}
172687bf8e7cSmrg	}
172748246ce7Smrg
172848246ce7Smrg	return count;
172948246ce7Smrg}
173048246ce7Smrg
173148246ce7Smrgstatic void writeback_config(struct device *dev, struct pipe_arg *pipes, unsigned int count)
173248246ce7Smrg{
173348246ce7Smrg	drmModeConnector *connector;
173448246ce7Smrg	unsigned int i, j;
173548246ce7Smrg
173648246ce7Smrg	for (j = 0; j < count; j++) {
173748246ce7Smrg		struct pipe_arg *pipe = &pipes[j];
173848246ce7Smrg
173948246ce7Smrg		for (i = 0; i < pipe->num_cons; i++) {
174048246ce7Smrg			connector = get_connector_by_id(dev, pipe->con_ids[i]);
174148246ce7Smrg			if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) {
174248246ce7Smrg				if (!pipe->mode) {
174348246ce7Smrg					fprintf(stderr, "no mode for writeback\n");
174448246ce7Smrg					return;
174548246ce7Smrg				}
174648246ce7Smrg				bo_fb_create(dev->fd, pipes[j].fourcc,
174748246ce7Smrg					     pipe->mode->hdisplay, pipe->mode->vdisplay,
174848246ce7Smrg					     UTIL_PATTERN_PLAIN,
174948246ce7Smrg					     &pipe->out_bo, &pipe->out_fb_id);
175048246ce7Smrg				add_property(dev, pipe->con_ids[i], "WRITEBACK_FB_ID",
175148246ce7Smrg					     pipe->out_fb_id);
175248246ce7Smrg				add_property(dev, pipe->con_ids[i], "WRITEBACK_OUT_FENCE_PTR",
175348246ce7Smrg					     (uintptr_t)(&dev->writeback_fence_fd));
175448246ce7Smrg			}
175548246ce7Smrg		}
175648246ce7Smrg	}
175748246ce7Smrg}
175848246ce7Smrg
175948246ce7Smrgstatic int poll_writeback_fence(int fd, int timeout)
176048246ce7Smrg{
176148246ce7Smrg	struct pollfd fds = { fd, POLLIN };
176248246ce7Smrg	int ret;
176348246ce7Smrg
176448246ce7Smrg	do {
176548246ce7Smrg		ret = poll(&fds, 1, timeout);
176648246ce7Smrg		if (ret > 0) {
176748246ce7Smrg			if (fds.revents & (POLLERR | POLLNVAL))
176848246ce7Smrg				return -EINVAL;
176948246ce7Smrg
177048246ce7Smrg			return 0;
177148246ce7Smrg		} else if (ret == 0) {
177248246ce7Smrg			return -ETIMEDOUT;
177348246ce7Smrg		} else {
177448246ce7Smrg			ret = -errno;
177548246ce7Smrg			if (ret == -EINTR || ret == -EAGAIN)
177648246ce7Smrg				continue;
177748246ce7Smrg			return ret;
177848246ce7Smrg		}
177948246ce7Smrg	} while (1);
178048246ce7Smrg
178148246ce7Smrg}
178248246ce7Smrg
178348246ce7Smrgstatic void dump_output_fb(struct device *dev, struct pipe_arg *pipes, char *dump_path,
178448246ce7Smrg			   unsigned int count)
178548246ce7Smrg{
178648246ce7Smrg	drmModeConnector *connector;
178748246ce7Smrg	unsigned int i, j;
178848246ce7Smrg
178948246ce7Smrg	for (j = 0; j < count; j++) {
179048246ce7Smrg		struct pipe_arg *pipe = &pipes[j];
179148246ce7Smrg
179248246ce7Smrg		for (i = 0; i < pipe->num_cons; i++) {
179348246ce7Smrg			connector = get_connector_by_id(dev, pipe->con_ids[i]);
179448246ce7Smrg			if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
179548246ce7Smrg				bo_dump(pipe->out_bo, dump_path);
179648246ce7Smrg		}
179748246ce7Smrg	}
179887bf8e7cSmrg}
179987bf8e7cSmrg
180087bf8e7cSmrgstatic void atomic_clear_mode(struct device *dev, struct pipe_arg *pipes, unsigned int count)
180187bf8e7cSmrg{
180287bf8e7cSmrg	unsigned int i;
180387bf8e7cSmrg	unsigned int j;
180487bf8e7cSmrg
180587bf8e7cSmrg	for (i = 0; i < count; i++) {
180687bf8e7cSmrg		struct pipe_arg *pipe = &pipes[i];
1807bf6cc7dcSmrg
180887bf8e7cSmrg		if (pipe->mode == NULL)
180987bf8e7cSmrg			continue;
181087bf8e7cSmrg
181187bf8e7cSmrg		for (j = 0; j < pipe->num_cons; ++j)
181287bf8e7cSmrg			add_property(dev, pipe->con_ids[j], "CRTC_ID",0);
181387bf8e7cSmrg
181487bf8e7cSmrg		add_property(dev, pipe->crtc_id, "MODE_ID", 0);
181587bf8e7cSmrg		add_property(dev, pipe->crtc_id, "ACTIVE", 0);
181618210155Smrg	}
1817424e9256Smrg}
181822944501Smrg
1819424e9256Smrgstatic void clear_mode(struct device *dev)
1820424e9256Smrg{
1821424e9256Smrg	if (dev->mode.fb_id)
1822424e9256Smrg		drmModeRmFB(dev->fd, dev->mode.fb_id);
1823424e9256Smrg	if (dev->mode.bo)
1824424e9256Smrg		bo_destroy(dev->mode.bo);
1825e88f27b3Smrg}
182622944501Smrg
1827e88f27b3Smrgstatic void set_planes(struct device *dev, struct plane_arg *p, unsigned int count)
1828e88f27b3Smrg{
1829e88f27b3Smrg	unsigned int i;
1830e88f27b3Smrg
1831e88f27b3Smrg	/* set up planes/overlays */
1832e88f27b3Smrg	for (i = 0; i < count; i++)
1833e88f27b3Smrg		if (set_plane(dev, &p[i]))
1834e88f27b3Smrg			return;
1835e88f27b3Smrg}
1836e88f27b3Smrg
1837a7d7de1eSmrgstatic void set_cursors(struct device *dev, struct pipe_arg *pipes, unsigned int count)
1838a7d7de1eSmrg{
1839424e9256Smrg	uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
1840adfa0b0cSmrg	uint32_t cw = 64;
1841adfa0b0cSmrg	uint32_t ch = 64;
18423c748557Ssnj	struct bo *bo;
1843adfa0b0cSmrg	uint64_t value;
1844a7d7de1eSmrg	unsigned int i;
1845a7d7de1eSmrg	int ret;
1846a7d7de1eSmrg
1847adfa0b0cSmrg	ret = drmGetCap(dev->fd, DRM_CAP_CURSOR_WIDTH, &value);
1848adfa0b0cSmrg	if (!ret)
1849adfa0b0cSmrg		cw = value;
1850adfa0b0cSmrg
1851adfa0b0cSmrg	ret = drmGetCap(dev->fd, DRM_CAP_CURSOR_HEIGHT, &value);
1852adfa0b0cSmrg	if (!ret)
1853adfa0b0cSmrg		ch = value;
1854adfa0b0cSmrg
1855a7d7de1eSmrg
1856a7d7de1eSmrg	/* create cursor bo.. just using PATTERN_PLAIN as it has
1857a7d7de1eSmrg	 * translucent alpha
1858a7d7de1eSmrg	 */
18593c748557Ssnj	bo = bo_create(dev->fd, DRM_FORMAT_ARGB8888, cw, ch, handles, pitches,
1860fe517fc9Smrg		       offsets, UTIL_PATTERN_PLAIN);
1861a7d7de1eSmrg	if (bo == NULL)
1862a7d7de1eSmrg		return;
1863a7d7de1eSmrg
1864424e9256Smrg	dev->mode.cursor_bo = bo;
1865424e9256Smrg
1866a7d7de1eSmrg	for (i = 0; i < count; i++) {
1867a7d7de1eSmrg		struct pipe_arg *pipe = &pipes[i];
1868a7d7de1eSmrg		ret = cursor_init(dev->fd, handles[0],
186987bf8e7cSmrg				pipe->crtc_id,
1870a7d7de1eSmrg				pipe->mode->hdisplay, pipe->mode->vdisplay,
1871a7d7de1eSmrg				cw, ch);
1872a7d7de1eSmrg		if (ret) {
1873a7d7de1eSmrg			fprintf(stderr, "failed to init cursor for CRTC[%u]\n",
1874a7d7de1eSmrg					pipe->crtc_id);
1875a7d7de1eSmrg			return;
1876a7d7de1eSmrg		}
1877a7d7de1eSmrg	}
1878a7d7de1eSmrg
1879a7d7de1eSmrg	cursor_start();
1880a7d7de1eSmrg}
1881a7d7de1eSmrg
1882a7d7de1eSmrgstatic void clear_cursors(struct device *dev)
1883a7d7de1eSmrg{
1884a7d7de1eSmrg	cursor_stop();
1885424e9256Smrg
1886424e9256Smrg	if (dev->mode.cursor_bo)
1887424e9256Smrg		bo_destroy(dev->mode.cursor_bo);
1888a7d7de1eSmrg}
1889a7d7de1eSmrg
1890e88f27b3Smrgstatic void test_page_flip(struct device *dev, struct pipe_arg *pipes, unsigned int count)
1891e88f27b3Smrg{
1892e88f27b3Smrg	unsigned int other_fb_id;
18933c748557Ssnj	struct bo *other_bo;
1894e88f27b3Smrg	drmEventContext evctx;
1895e88f27b3Smrg	unsigned int i;
1896e88f27b3Smrg	int ret;
1897e88f27b3Smrg
189887bf8e7cSmrg	if (bo_fb_create(dev->fd, pipes[0].fourcc, dev->mode.width, dev->mode.height,
189987bf8e7cSmrg	                 UTIL_PATTERN_PLAIN, &other_bo, &other_fb_id))
190022944501Smrg		return;
190122944501Smrg
190222944501Smrg	for (i = 0; i < count; i++) {
1903e88f27b3Smrg		struct pipe_arg *pipe = &pipes[i];
1904e88f27b3Smrg
1905e88f27b3Smrg		if (pipe->mode == NULL)
190622944501Smrg			continue;
190722944501Smrg
190887bf8e7cSmrg		ret = drmModePageFlip(dev->fd, pipe->crtc_id,
1909e88f27b3Smrg				      other_fb_id, DRM_MODE_PAGE_FLIP_EVENT,
1910e88f27b3Smrg				      pipe);
1911e88f27b3Smrg		if (ret) {
1912e88f27b3Smrg			fprintf(stderr, "failed to page flip: %s\n", strerror(errno));
1913424e9256Smrg			goto err_rmfb;
1914e88f27b3Smrg		}
1915e88f27b3Smrg		gettimeofday(&pipe->start, NULL);
1916e88f27b3Smrg		pipe->swap_count = 0;
1917e88f27b3Smrg		pipe->fb_id[0] = dev->mode.fb_id;
1918e88f27b3Smrg		pipe->fb_id[1] = other_fb_id;
1919e88f27b3Smrg		pipe->current_fb_id = other_fb_id;
192022944501Smrg	}
192122944501Smrg
192222944501Smrg	memset(&evctx, 0, sizeof evctx);
192322944501Smrg	evctx.version = DRM_EVENT_CONTEXT_VERSION;
192422944501Smrg	evctx.vblank_handler = NULL;
192522944501Smrg	evctx.page_flip_handler = page_flip_handler;
1926fe517fc9Smrg
192722944501Smrg	while (1) {
192822944501Smrg#if 0
192922944501Smrg		struct pollfd pfd[2];
193022944501Smrg
193122944501Smrg		pfd[0].fd = 0;
193222944501Smrg		pfd[0].events = POLLIN;
193322944501Smrg		pfd[1].fd = fd;
193422944501Smrg		pfd[1].events = POLLIN;
193522944501Smrg
193622944501Smrg		if (poll(pfd, 2, -1) < 0) {
193722944501Smrg			fprintf(stderr, "poll error\n");
193822944501Smrg			break;
193922944501Smrg		}
194022944501Smrg
194122944501Smrg		if (pfd[0].revents)
194222944501Smrg			break;
194322944501Smrg#else
194422944501Smrg		struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 };
194522944501Smrg		fd_set fds;
194622944501Smrg
194722944501Smrg		FD_ZERO(&fds);
194822944501Smrg		FD_SET(0, &fds);
1949e88f27b3Smrg		FD_SET(dev->fd, &fds);
1950e88f27b3Smrg		ret = select(dev->fd + 1, &fds, NULL, NULL, &timeout);
195122944501Smrg
195222944501Smrg		if (ret <= 0) {
195322944501Smrg			fprintf(stderr, "select timed out or error (ret %d)\n",
195422944501Smrg				ret);
195522944501Smrg			continue;
195622944501Smrg		} else if (FD_ISSET(0, &fds)) {
195722944501Smrg			break;
195822944501Smrg		}
195922944501Smrg#endif
196022944501Smrg
1961e88f27b3Smrg		drmHandleEvent(dev->fd, &evctx);
196222944501Smrg	}
1963e88f27b3Smrg
1964424e9256Smrgerr_rmfb:
1965424e9256Smrg	drmModeRmFB(dev->fd, other_fb_id);
19663c748557Ssnj	bo_destroy(other_bo);
196718210155Smrg}
196818210155Smrg
1969e88f27b3Smrg#define min(a, b)	((a) < (b) ? (a) : (b))
197018210155Smrg
1971e88f27b3Smrgstatic int parse_connector(struct pipe_arg *pipe, const char *arg)
197218210155Smrg{
1973e88f27b3Smrg	unsigned int len;
1974e88f27b3Smrg	unsigned int i;
1975e88f27b3Smrg	const char *p;
1976e88f27b3Smrg	char *endp;
1977e88f27b3Smrg
1978e88f27b3Smrg	pipe->vrefresh = 0;
1979e88f27b3Smrg	pipe->crtc_id = (uint32_t)-1;
1980e88f27b3Smrg	strcpy(pipe->format_str, "XR24");
1981e88f27b3Smrg
1982e88f27b3Smrg	/* Count the number of connectors and allocate them. */
1983e88f27b3Smrg	pipe->num_cons = 1;
1984fe517fc9Smrg	for (p = arg; *p && *p != ':' && *p != '@'; ++p) {
1985e88f27b3Smrg		if (*p == ',')
1986e88f27b3Smrg			pipe->num_cons++;
1987e88f27b3Smrg	}
1988e88f27b3Smrg
1989424e9256Smrg	pipe->con_ids = calloc(pipe->num_cons, sizeof(*pipe->con_ids));
1990fe517fc9Smrg	pipe->cons = calloc(pipe->num_cons, sizeof(*pipe->cons));
1991fe517fc9Smrg	if (pipe->con_ids == NULL || pipe->cons == NULL)
1992e88f27b3Smrg		return -1;
1993e88f27b3Smrg
1994e88f27b3Smrg	/* Parse the connectors. */
1995e88f27b3Smrg	for (i = 0, p = arg; i < pipe->num_cons; ++i, p = endp + 1) {
1996fe517fc9Smrg		endp = strpbrk(p, ",@:");
1997fe517fc9Smrg		if (!endp)
1998fe517fc9Smrg			break;
1999fe517fc9Smrg
2000fe517fc9Smrg		pipe->cons[i] = strndup(p, endp - p);
2001fe517fc9Smrg
2002e88f27b3Smrg		if (*endp != ',')
2003e88f27b3Smrg			break;
2004e88f27b3Smrg	}
2005e88f27b3Smrg
2006e88f27b3Smrg	if (i != pipe->num_cons - 1)
2007e88f27b3Smrg		return -1;
2008e88f27b3Smrg
2009e88f27b3Smrg	/* Parse the remaining parameters. */
201087bf8e7cSmrg	if (!endp)
201187bf8e7cSmrg		return -1;
2012e88f27b3Smrg	if (*endp == '@') {
2013e88f27b3Smrg		arg = endp + 1;
2014e88f27b3Smrg		pipe->crtc_id = strtoul(arg, &endp, 10);
2015e88f27b3Smrg	}
2016e88f27b3Smrg	if (*endp != ':')
2017e88f27b3Smrg		return -1;
2018e88f27b3Smrg
2019e88f27b3Smrg	arg = endp + 1;
2020e88f27b3Smrg
2021e88f27b3Smrg	/* Search for the vertical refresh or the format. */
2022e88f27b3Smrg	p = strpbrk(arg, "-@");
2023e88f27b3Smrg	if (p == NULL)
2024e88f27b3Smrg		p = arg + strlen(arg);
2025e88f27b3Smrg	len = min(sizeof pipe->mode_str - 1, (unsigned int)(p - arg));
2026e88f27b3Smrg	strncpy(pipe->mode_str, arg, len);
2027e88f27b3Smrg	pipe->mode_str[len] = '\0';
2028e88f27b3Smrg
2029e88f27b3Smrg	if (*p == '-') {
203087bf8e7cSmrg		pipe->vrefresh = strtof(p + 1, &endp);
2031e88f27b3Smrg		p = endp;
2032e88f27b3Smrg	}
2033e88f27b3Smrg
2034e88f27b3Smrg	if (*p == '@') {
203548246ce7Smrg		len = sizeof(pipe->format_str) - 1;
203648246ce7Smrg		strncpy(pipe->format_str, p + 1, len);
203748246ce7Smrg		pipe->format_str[len] = '\0';
2038e88f27b3Smrg	}
2039e88f27b3Smrg
2040fe517fc9Smrg	pipe->fourcc = util_format_fourcc(pipe->format_str);
2041e88f27b3Smrg	if (pipe->fourcc == 0)  {
2042e88f27b3Smrg		fprintf(stderr, "unknown format %s\n", pipe->format_str);
2043e88f27b3Smrg		return -1;
2044e88f27b3Smrg	}
2045e88f27b3Smrg
2046e88f27b3Smrg	return 0;
2047e88f27b3Smrg}
2048e88f27b3Smrg
2049e88f27b3Smrgstatic int parse_plane(struct plane_arg *plane, const char *p)
2050e88f27b3Smrg{
205148246ce7Smrg	unsigned int len;
2052e88f27b3Smrg	char *end;
2053e88f27b3Smrg
20542ee35494Smrg	plane->plane_id = strtoul(p, &end, 10);
20552ee35494Smrg	if (*end != '@')
20562ee35494Smrg		return -EINVAL;
20572ee35494Smrg
20582ee35494Smrg	p = end + 1;
2059e88f27b3Smrg	plane->crtc_id = strtoul(p, &end, 10);
2060e88f27b3Smrg	if (*end != ':')
2061e88f27b3Smrg		return -EINVAL;
2062e88f27b3Smrg
2063e88f27b3Smrg	p = end + 1;
2064e88f27b3Smrg	plane->w = strtoul(p, &end, 10);
2065e88f27b3Smrg	if (*end != 'x')
2066e88f27b3Smrg		return -EINVAL;
2067e88f27b3Smrg
2068e88f27b3Smrg	p = end + 1;
2069e88f27b3Smrg	plane->h = strtoul(p, &end, 10);
2070e88f27b3Smrg
2071e88f27b3Smrg	if (*end == '+' || *end == '-') {
2072e88f27b3Smrg		plane->x = strtol(end, &end, 10);
2073e88f27b3Smrg		if (*end != '+' && *end != '-')
2074e88f27b3Smrg			return -EINVAL;
2075e88f27b3Smrg		plane->y = strtol(end, &end, 10);
2076e88f27b3Smrg
2077e88f27b3Smrg		plane->has_position = true;
2078e88f27b3Smrg	}
2079e88f27b3Smrg
2080e88f27b3Smrg	if (*end == '*') {
2081e88f27b3Smrg		p = end + 1;
2082e88f27b3Smrg		plane->scale = strtod(p, &end);
2083e88f27b3Smrg		if (plane->scale <= 0.0)
2084e88f27b3Smrg			return -EINVAL;
2085e88f27b3Smrg	} else {
2086e88f27b3Smrg		plane->scale = 1.0;
2087e88f27b3Smrg	}
2088e88f27b3Smrg
2089e88f27b3Smrg	if (*end == '@') {
209048246ce7Smrg		len = sizeof(plane->format_str) - 1;
209148246ce7Smrg		strncpy(plane->format_str, end + 1, len);
209248246ce7Smrg		plane->format_str[len] = '\0';
2093e88f27b3Smrg	} else {
2094e88f27b3Smrg		strcpy(plane->format_str, "XR24");
2095e88f27b3Smrg	}
2096e88f27b3Smrg
2097fe517fc9Smrg	plane->fourcc = util_format_fourcc(plane->format_str);
2098e88f27b3Smrg	if (plane->fourcc == 0) {
2099e88f27b3Smrg		fprintf(stderr, "unknown format %s\n", plane->format_str);
2100e88f27b3Smrg		return -EINVAL;
2101e88f27b3Smrg	}
2102e88f27b3Smrg
2103e88f27b3Smrg	return 0;
2104e88f27b3Smrg}
2105e88f27b3Smrg
2106e88f27b3Smrgstatic int parse_property(struct property_arg *p, const char *arg)
2107e88f27b3Smrg{
2108e88f27b3Smrg	if (sscanf(arg, "%d:%32[^:]:%" SCNu64, &p->obj_id, p->name, &p->value) != 3)
2109e88f27b3Smrg		return -1;
2110e88f27b3Smrg
2111e88f27b3Smrg	p->obj_type = 0;
2112e88f27b3Smrg	p->name[DRM_PROP_NAME_LEN] = '\0';
2113e88f27b3Smrg
2114e88f27b3Smrg	return 0;
2115e88f27b3Smrg}
2116e88f27b3Smrg
2117bf6cc7dcSmrgstatic void parse_fill_patterns(char *arg)
2118bf6cc7dcSmrg{
2119bf6cc7dcSmrg	char *fill = strtok(arg, ",");
2120bf6cc7dcSmrg	if (!fill)
2121bf6cc7dcSmrg		return;
2122bf6cc7dcSmrg	primary_fill = util_pattern_enum(fill);
2123bf6cc7dcSmrg	fill = strtok(NULL, ",");
2124bf6cc7dcSmrg	if (!fill)
2125bf6cc7dcSmrg		return;
2126bf6cc7dcSmrg	secondary_fill = util_pattern_enum(fill);
2127bf6cc7dcSmrg}
2128bf6cc7dcSmrg
2129e88f27b3Smrgstatic void usage(char *name)
2130e88f27b3Smrg{
213148246ce7Smrg	fprintf(stderr, "usage: %s [-acDdefMoPpsCvrw]\n", name);
2132e88f27b3Smrg
2133e88f27b3Smrg	fprintf(stderr, "\n Query options:\n\n");
213418210155Smrg	fprintf(stderr, "\t-c\tlist connectors\n");
2135e88f27b3Smrg	fprintf(stderr, "\t-e\tlist encoders\n");
213618210155Smrg	fprintf(stderr, "\t-f\tlist framebuffers\n");
2137e88f27b3Smrg	fprintf(stderr, "\t-p\tlist CRTCs and planes (pipes)\n");
2138e88f27b3Smrg
2139e88f27b3Smrg	fprintf(stderr, "\n Test options:\n\n");
214048246ce7Smrg	fprintf(stderr, "\t-P <plane_id>@<crtc_id>:<w>x<h>[+<x>+<y>][*<scale>][@<format>]\tset a plane, see 'plane-topology'\n");
214148246ce7Smrg	fprintf(stderr, "\t-s <connector_id>[,<connector_id>][@<crtc_id>]:mode[@<format>]\tset a mode, see 'mode-topology'\n");
214248246ce7Smrg	fprintf(stderr, "\t\twhere mode can be specified as:\n");
214348246ce7Smrg	fprintf(stderr, "\t\t<hdisp>x<vdisp>[-<vrefresh>]\n");
214448246ce7Smrg	fprintf(stderr, "\t\t<hdisp>,<hss>,<hse>,<htot>,<vdisp>,<vss>,<vse>,<vtot>-<vrefresh>\n");
214548246ce7Smrg	fprintf(stderr, "\t\t#<mode index>\n");
2146a7d7de1eSmrg	fprintf(stderr, "\t-C\ttest hw cursor\n");
214722944501Smrg	fprintf(stderr, "\t-v\ttest vsynced page flipping\n");
214887bf8e7cSmrg	fprintf(stderr, "\t-r\tset the preferred mode for all connectors\n");
214948246ce7Smrg	fprintf(stderr, "\t-w <obj_id>:<prop_name>:<value>\tset property, see 'property'\n");
21506260e5d5Smrg	fprintf(stderr, "\t-a \tuse atomic API\n");
2151bf6cc7dcSmrg	fprintf(stderr, "\t-F pattern1,pattern2\tspecify fill patterns\n");
215248246ce7Smrg	fprintf(stderr, "\t-o <desired file path> \t Dump writeback output buffer to file\n");
2153e88f27b3Smrg
2154e88f27b3Smrg	fprintf(stderr, "\n Generic options:\n\n");
2155e88f27b3Smrg	fprintf(stderr, "\t-d\tdrop master after mode set\n");
2156e88f27b3Smrg	fprintf(stderr, "\t-M module\tuse the given driver\n");
2157e88f27b3Smrg	fprintf(stderr, "\t-D device\tuse the given device\n");
2158e88f27b3Smrg
215918210155Smrg	fprintf(stderr, "\n\tDefault is to dump all info.\n");
216048246ce7Smrg
216148246ce7Smrg	fprintf(stderr, "\n");
216248246ce7Smrg	fprintf(stderr, "Plane Topology is defined as:\n");
216348246ce7Smrg	fprintf(stderr, "\tplane-topology\t::= plane-id '@' crtc-id ':' width 'x' height ( <plane-offsets> )? ;\n");
216448246ce7Smrg	fprintf(stderr, "\tplane-offsets\t::= '+' x-offset '+' y-offset ( <plane-scale> )? ;\n");
216548246ce7Smrg	fprintf(stderr, "\tplane-scale\t::= '*' scale ( <plane-format> )? ;\n");
216648246ce7Smrg	fprintf(stderr, "\tplane-format\t::= '@' format ;\n");
216748246ce7Smrg
216848246ce7Smrg	fprintf(stderr, "\n");
216948246ce7Smrg	fprintf(stderr, "Mode Topology is defined as:\n");
217048246ce7Smrg	fprintf(stderr, "\tmode-topology\t::= connector-id ( ',' connector-id )* ( '@' crtc-id )? ':' <mode-selection> ( '@' format )? ;\n");
217148246ce7Smrg	fprintf(stderr, "\tmode-selection\t::=  <indexed-mode> | <named-mode> | <custom-mode> ;\n");
217248246ce7Smrg	fprintf(stderr, "\tindexed-mode\t::=  '#' mode-index ;\n");
217348246ce7Smrg	fprintf(stderr, "\tnamed-mode\t::=  width 'x' height ( '-' vrefresh )? ;\n");
217448246ce7Smrg	fprintf(stderr, "\tcustom-mode\t::=  hdisplay ',' hsyncstart ',' hsyncend ',' htotal ',' vdisplay ',' vsyncstart ',' vsyncend ',' vtotal '-' vrefresh ;\n");
217548246ce7Smrg
217648246ce7Smrg	fprintf(stderr, "\n");
217748246ce7Smrg	fprintf(stderr, "Property is defined as:\n");
217848246ce7Smrg	fprintf(stderr, "\tproperty\t::= object-id ':' property-name ':' value ;\n");
217918210155Smrg	exit(0);
218018210155Smrg}
218118210155Smrg
218248246ce7Smrgstatic char optstr[] = "acdD:efF:M:P:ps:Cvrw:o:";
2183e88f27b3Smrg
218418210155Smrgint main(int argc, char **argv)
218518210155Smrg{
2186e88f27b3Smrg	struct device dev;
2187e88f27b3Smrg
218818210155Smrg	int c;
2189e88f27b3Smrg	int encoders = 0, connectors = 0, crtcs = 0, planes = 0, framebuffers = 0;
2190e88f27b3Smrg	int drop_master = 0;
219122944501Smrg	int test_vsync = 0;
2192a7d7de1eSmrg	int test_cursor = 0;
219387bf8e7cSmrg	int set_preferred = 0;
21946260e5d5Smrg	int use_atomic = 0;
2195e88f27b3Smrg	char *device = NULL;
2196e88f27b3Smrg	char *module = NULL;
2197e88f27b3Smrg	unsigned int i;
2198fe517fc9Smrg	unsigned int count = 0, plane_count = 0;
2199e88f27b3Smrg	unsigned int prop_count = 0;
2200e88f27b3Smrg	struct pipe_arg *pipe_args = NULL;
2201e88f27b3Smrg	struct plane_arg *plane_args = NULL;
2202e88f27b3Smrg	struct property_arg *prop_args = NULL;
2203e88f27b3Smrg	unsigned int args = 0;
2204e88f27b3Smrg	int ret;
220548246ce7Smrg	char *dump_path = NULL;
2206e88f27b3Smrg
2207e88f27b3Smrg	memset(&dev, 0, sizeof dev);
2208e88f27b3Smrg
220918210155Smrg	opterr = 0;
221018210155Smrg	while ((c = getopt(argc, argv, optstr)) != -1) {
2211e88f27b3Smrg		args++;
2212e88f27b3Smrg
221318210155Smrg		switch (c) {
22146260e5d5Smrg		case 'a':
22156260e5d5Smrg			use_atomic = 1;
221687bf8e7cSmrg			/* Preserve the default behaviour of dumping all information. */
221787bf8e7cSmrg			args--;
22186260e5d5Smrg			break;
221918210155Smrg		case 'c':
222018210155Smrg			connectors = 1;
222118210155Smrg			break;
2222e88f27b3Smrg		case 'D':
2223e88f27b3Smrg			device = optarg;
222487bf8e7cSmrg			/* Preserve the default behaviour of dumping all information. */
2225e88f27b3Smrg			args--;
2226e88f27b3Smrg			break;
2227e88f27b3Smrg		case 'd':
2228e88f27b3Smrg			drop_master = 1;
222918210155Smrg			break;
2230e88f27b3Smrg		case 'e':
2231e88f27b3Smrg			encoders = 1;
223218210155Smrg			break;
223318210155Smrg		case 'f':
223418210155Smrg			framebuffers = 1;
223518210155Smrg			break;
2236bf6cc7dcSmrg		case 'F':
2237bf6cc7dcSmrg			parse_fill_patterns(optarg);
2238bf6cc7dcSmrg			break;
2239e88f27b3Smrg		case 'M':
2240e88f27b3Smrg			module = optarg;
2241e88f27b3Smrg			/* Preserve the default behaviour of dumping all information. */
2242e88f27b3Smrg			args--;
2243e88f27b3Smrg			break;
224448246ce7Smrg		case 'o':
224548246ce7Smrg			dump_path = optarg;
224648246ce7Smrg			break;
2247e88f27b3Smrg		case 'P':
2248e88f27b3Smrg			plane_args = realloc(plane_args,
2249e88f27b3Smrg					     (plane_count + 1) * sizeof *plane_args);
2250e88f27b3Smrg			if (plane_args == NULL) {
2251e88f27b3Smrg				fprintf(stderr, "memory allocation failed\n");
2252e88f27b3Smrg				return 1;
2253e88f27b3Smrg			}
2254424e9256Smrg			memset(&plane_args[plane_count], 0, sizeof(*plane_args));
2255e88f27b3Smrg
2256e88f27b3Smrg			if (parse_plane(&plane_args[plane_count], optarg) < 0)
2257e88f27b3Smrg				usage(argv[0]);
2258e88f27b3Smrg
2259e88f27b3Smrg			plane_count++;
2260e88f27b3Smrg			break;
2261e88f27b3Smrg		case 'p':
2262e88f27b3Smrg			crtcs = 1;
2263e88f27b3Smrg			planes = 1;
226422944501Smrg			break;
226518210155Smrg		case 's':
2266e88f27b3Smrg			pipe_args = realloc(pipe_args,
2267e88f27b3Smrg					    (count + 1) * sizeof *pipe_args);
2268e88f27b3Smrg			if (pipe_args == NULL) {
2269e88f27b3Smrg				fprintf(stderr, "memory allocation failed\n");
2270e88f27b3Smrg				return 1;
2271e88f27b3Smrg			}
2272424e9256Smrg			memset(&pipe_args[count], 0, sizeof(*pipe_args));
2273e88f27b3Smrg
2274e88f27b3Smrg			if (parse_connector(&pipe_args[count], optarg) < 0)
227518210155Smrg				usage(argv[0]);
2276e88f27b3Smrg
2277fe517fc9Smrg			count++;
227818210155Smrg			break;
2279a7d7de1eSmrg		case 'C':
2280a7d7de1eSmrg			test_cursor = 1;
2281a7d7de1eSmrg			break;
2282e88f27b3Smrg		case 'v':
2283e88f27b3Smrg			test_vsync = 1;
2284e88f27b3Smrg			break;
228587bf8e7cSmrg		case 'r':
228687bf8e7cSmrg			set_preferred = 1;
228787bf8e7cSmrg			break;
2288e88f27b3Smrg		case 'w':
2289e88f27b3Smrg			prop_args = realloc(prop_args,
2290e88f27b3Smrg					   (prop_count + 1) * sizeof *prop_args);
2291e88f27b3Smrg			if (prop_args == NULL) {
2292e88f27b3Smrg				fprintf(stderr, "memory allocation failed\n");
2293e88f27b3Smrg				return 1;
2294e88f27b3Smrg			}
2295424e9256Smrg			memset(&prop_args[prop_count], 0, sizeof(*prop_args));
2296e88f27b3Smrg
2297e88f27b3Smrg			if (parse_property(&prop_args[prop_count], optarg) < 0)
2298e88f27b3Smrg				usage(argv[0]);
2299e88f27b3Smrg
2300e88f27b3Smrg			prop_count++;
2301e88f27b3Smrg			break;
230218210155Smrg		default:
230318210155Smrg			usage(argv[0]);
230418210155Smrg			break;
230518210155Smrg		}
230618210155Smrg	}
230718210155Smrg
230887bf8e7cSmrg	/* Dump all the details when no* arguments are provided. */
230987bf8e7cSmrg	if (!args)
2310e88f27b3Smrg		encoders = connectors = crtcs = planes = framebuffers = 1;
231118210155Smrg
231248246ce7Smrg	if (test_vsync && !count && !set_preferred) {
231348246ce7Smrg		fprintf(stderr, "page flipping requires at least one -s or -r option.\n");
23146260e5d5Smrg		return -1;
23156260e5d5Smrg	}
231687bf8e7cSmrg	if (set_preferred && count) {
231787bf8e7cSmrg		fprintf(stderr, "cannot use -r (preferred) when -s (mode) is set\n");
231822944501Smrg		return -1;
231922944501Smrg	}
232022944501Smrg
232187bf8e7cSmrg	dev.fd = util_open(device, module);
232287bf8e7cSmrg	if (dev.fd < 0)
2323a7d7de1eSmrg		return -1;
232487bf8e7cSmrg
232587bf8e7cSmrg	if (use_atomic) {
232687bf8e7cSmrg		ret = drmSetClientCap(dev.fd, DRM_CLIENT_CAP_ATOMIC, 1);
232748246ce7Smrg		drmSetClientCap(dev.fd, DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1);
232887bf8e7cSmrg		if (ret) {
232987bf8e7cSmrg			fprintf(stderr, "no atomic modesetting support: %s\n", strerror(errno));
233087bf8e7cSmrg			drmClose(dev.fd);
233187bf8e7cSmrg			return -1;
233287bf8e7cSmrg		}
2333a7d7de1eSmrg	}
2334a7d7de1eSmrg
233587bf8e7cSmrg	dev.use_atomic = use_atomic;
233687bf8e7cSmrg
2337e88f27b3Smrg	dev.resources = get_resources(&dev);
2338e88f27b3Smrg	if (!dev.resources) {
2339e88f27b3Smrg		drmClose(dev.fd);
234018210155Smrg		return 1;
234118210155Smrg	}
234218210155Smrg
2343e88f27b3Smrg#define dump_resource(dev, res) if (res) dump_##res(dev)
2344e88f27b3Smrg
2345e88f27b3Smrg	dump_resource(&dev, encoders);
2346e88f27b3Smrg	dump_resource(&dev, connectors);
2347e88f27b3Smrg	dump_resource(&dev, crtcs);
2348e88f27b3Smrg	dump_resource(&dev, planes);
2349e88f27b3Smrg	dump_resource(&dev, framebuffers);
2350e88f27b3Smrg
235148246ce7Smrg	if (dev.use_atomic)
235248246ce7Smrg		dev.req = drmModeAtomicAlloc();
235348246ce7Smrg
2354e88f27b3Smrg	for (i = 0; i < prop_count; ++i)
2355e88f27b3Smrg		set_property(&dev, &prop_args[i]);
2356e88f27b3Smrg
23576260e5d5Smrg	if (dev.use_atomic) {
235887bf8e7cSmrg		if (set_preferred || (count && plane_count)) {
23596260e5d5Smrg			uint64_t cap = 0;
23606260e5d5Smrg
23616260e5d5Smrg			ret = drmGetCap(dev.fd, DRM_CAP_DUMB_BUFFER, &cap);
23626260e5d5Smrg			if (ret || cap == 0) {
23636260e5d5Smrg				fprintf(stderr, "driver doesn't support the dumb buffer API\n");
23646260e5d5Smrg				return 1;
23656260e5d5Smrg			}
23666260e5d5Smrg
236787bf8e7cSmrg			if (set_preferred || count)
236848246ce7Smrg				count = set_mode(&dev, &pipe_args, count);
236948246ce7Smrg
237048246ce7Smrg			if (dump_path) {
237148246ce7Smrg				if (!pipe_has_writeback_connector(&dev, pipe_args, count)) {
237248246ce7Smrg					fprintf(stderr, "No writeback connector found, can not dump.\n");
237348246ce7Smrg					return 1;
237448246ce7Smrg				}
237548246ce7Smrg
237648246ce7Smrg				writeback_config(&dev, pipe_args, count);
237748246ce7Smrg			}
237887bf8e7cSmrg
237987bf8e7cSmrg			if (plane_count)
238087bf8e7cSmrg				atomic_set_planes(&dev, plane_args, plane_count, false);
23816260e5d5Smrg
23826260e5d5Smrg			ret = drmModeAtomicCommit(dev.fd, dev.req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
23836260e5d5Smrg			if (ret) {
23846260e5d5Smrg				fprintf(stderr, "Atomic Commit failed [1]\n");
23856260e5d5Smrg				return 1;
23866260e5d5Smrg			}
23876260e5d5Smrg
238848246ce7Smrg			/*
238948246ce7Smrg			 * Since only writeback connectors have an output fb, this should only be
239048246ce7Smrg			 * called for writeback.
239148246ce7Smrg			 */
239248246ce7Smrg			if (dump_path) {
239348246ce7Smrg				ret = poll_writeback_fence(dev.writeback_fence_fd, 1000);
239448246ce7Smrg				if (ret)
239548246ce7Smrg					fprintf(stderr, "Poll for writeback error: %d. Skipping Dump.\n",
239648246ce7Smrg							ret);
239748246ce7Smrg				dump_output_fb(&dev, pipe_args, dump_path, count);
239848246ce7Smrg			}
239948246ce7Smrg
240087bf8e7cSmrg			if (test_vsync)
240187bf8e7cSmrg				atomic_test_page_flip(&dev, pipe_args, plane_args, plane_count);
24026260e5d5Smrg
24036260e5d5Smrg			if (drop_master)
24046260e5d5Smrg				drmDropMaster(dev.fd);
24056260e5d5Smrg
24066260e5d5Smrg			getchar();
24076260e5d5Smrg
24086260e5d5Smrg			drmModeAtomicFree(dev.req);
24096260e5d5Smrg			dev.req = drmModeAtomicAlloc();
24106260e5d5Smrg
241187bf8e7cSmrg			/* XXX: properly teardown the preferred mode/plane state */
241287bf8e7cSmrg			if (plane_count)
241387bf8e7cSmrg				atomic_clear_planes(&dev, plane_args, plane_count);
241487bf8e7cSmrg
241587bf8e7cSmrg			if (count)
241687bf8e7cSmrg				atomic_clear_mode(&dev, pipe_args, count);
241748246ce7Smrg		}
241887bf8e7cSmrg
241948246ce7Smrg		ret = drmModeAtomicCommit(dev.fd, dev.req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
242048246ce7Smrg		if (ret)
242148246ce7Smrg			fprintf(stderr, "Atomic Commit failed\n");
24226260e5d5Smrg
242348246ce7Smrg		if (count && plane_count)
242448246ce7Smrg			atomic_clear_FB(&dev, plane_args, plane_count);
2425e88f27b3Smrg
24266260e5d5Smrg		drmModeAtomicFree(dev.req);
24276260e5d5Smrg	} else {
242848246ce7Smrg		if (dump_path) {
242948246ce7Smrg			fprintf(stderr, "writeback / dump is only supported in atomic mode\n");
243048246ce7Smrg			return 1;
243148246ce7Smrg		}
243248246ce7Smrg
243387bf8e7cSmrg		if (set_preferred || count || plane_count) {
24346260e5d5Smrg			uint64_t cap = 0;
24356260e5d5Smrg
24366260e5d5Smrg			ret = drmGetCap(dev.fd, DRM_CAP_DUMB_BUFFER, &cap);
24376260e5d5Smrg			if (ret || cap == 0) {
24386260e5d5Smrg				fprintf(stderr, "driver doesn't support the dumb buffer API\n");
24396260e5d5Smrg				return 1;
24406260e5d5Smrg			}
24416260e5d5Smrg
244287bf8e7cSmrg			if (set_preferred || count)
244348246ce7Smrg				count = set_mode(&dev, &pipe_args, count);
2444e88f27b3Smrg
24456260e5d5Smrg			if (plane_count)
24466260e5d5Smrg				set_planes(&dev, plane_args, plane_count);
2447e88f27b3Smrg
24486260e5d5Smrg			if (test_cursor)
24496260e5d5Smrg				set_cursors(&dev, pipe_args, count);
2450a7d7de1eSmrg
24516260e5d5Smrg			if (test_vsync)
24526260e5d5Smrg				test_page_flip(&dev, pipe_args, count);
2453e88f27b3Smrg
24546260e5d5Smrg			if (drop_master)
24556260e5d5Smrg				drmDropMaster(dev.fd);
2456e88f27b3Smrg
24576260e5d5Smrg			getchar();
2458a7d7de1eSmrg
24596260e5d5Smrg			if (test_cursor)
24606260e5d5Smrg				clear_cursors(&dev);
2461a7d7de1eSmrg
24626260e5d5Smrg			if (plane_count)
24636260e5d5Smrg				clear_planes(&dev, plane_args, plane_count);
24646260e5d5Smrg
246587bf8e7cSmrg			if (set_preferred || count)
24666260e5d5Smrg				clear_mode(&dev);
24676260e5d5Smrg		}
246818210155Smrg	}
246918210155Smrg
2470e88f27b3Smrg	free_resources(dev.resources);
247187bf8e7cSmrg	drmClose(dev.fd);
247218210155Smrg
247318210155Smrg	return 0;
247418210155Smrg}
2475