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