Home | History | Annotate | 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 #ifdef HAVE_CONFIG_H
     42 #include "config.h"
     43 #endif
     44 
     45 #include <assert.h>
     46 #include <ctype.h>
     47 #include <stdbool.h>
     48 #include <stdio.h>
     49 #include <stdlib.h>
     50 #include <stdint.h>
     51 #include <inttypes.h>
     52 #include <unistd.h>
     53 #include <string.h>
     54 #include <strings.h>
     55 #include <errno.h>
     56 #include <poll.h>
     57 #include <sys/time.h>
     58 #ifdef HAVE_SYS_SELECT_H
     59 #include <sys/select.h>
     60 #endif
     61 
     62 #include "xf86drm.h"
     63 #include "xf86drmMode.h"
     64 #include "drm_fourcc.h"
     65 
     66 #include "util/common.h"
     67 #include "util/format.h"
     68 #include "util/kms.h"
     69 #include "util/pattern.h"
     70 
     71 #include "buffers.h"
     72 #include "cursor.h"
     73 
     74 struct crtc {
     75 	drmModeCrtc *crtc;
     76 	drmModeObjectProperties *props;
     77 	drmModePropertyRes **props_info;
     78 	drmModeModeInfo *mode;
     79 };
     80 
     81 struct encoder {
     82 	drmModeEncoder *encoder;
     83 };
     84 
     85 struct connector {
     86 	drmModeConnector *connector;
     87 	drmModeObjectProperties *props;
     88 	drmModePropertyRes **props_info;
     89 	char *name;
     90 };
     91 
     92 struct fb {
     93 	drmModeFB *fb;
     94 };
     95 
     96 struct plane {
     97 	drmModePlane *plane;
     98 	drmModeObjectProperties *props;
     99 	drmModePropertyRes **props_info;
    100 };
    101 
    102 struct resources {
    103 	drmModeRes *res;
    104 	drmModePlaneRes *plane_res;
    105 
    106 	struct crtc *crtcs;
    107 	struct encoder *encoders;
    108 	struct connector *connectors;
    109 	struct fb *fbs;
    110 	struct plane *planes;
    111 };
    112 
    113 struct device {
    114 	int fd;
    115 
    116 	struct resources *resources;
    117 
    118 	struct {
    119 		unsigned int width;
    120 		unsigned int height;
    121 
    122 		unsigned int fb_id;
    123 		struct bo *bo;
    124 		struct bo *cursor_bo;
    125 	} mode;
    126 };
    127 
    128 static inline int64_t U642I64(uint64_t val)
    129 {
    130 	return (int64_t)*((int64_t *)&val);
    131 }
    132 
    133 #define bit_name_fn(res)					\
    134 const char * res##_str(int type) {				\
    135 	unsigned int i;						\
    136 	const char *sep = "";					\
    137 	for (i = 0; i < ARRAY_SIZE(res##_names); i++) {		\
    138 		if (type & (1 << i)) {				\
    139 			printf("%s%s", sep, res##_names[i]);	\
    140 			sep = ", ";				\
    141 		}						\
    142 	}							\
    143 	return NULL;						\
    144 }
    145 
    146 static const char *mode_type_names[] = {
    147 	"builtin",
    148 	"clock_c",
    149 	"crtc_c",
    150 	"preferred",
    151 	"default",
    152 	"userdef",
    153 	"driver",
    154 };
    155 
    156 static bit_name_fn(mode_type)
    157 
    158 static const char *mode_flag_names[] = {
    159 	"phsync",
    160 	"nhsync",
    161 	"pvsync",
    162 	"nvsync",
    163 	"interlace",
    164 	"dblscan",
    165 	"csync",
    166 	"pcsync",
    167 	"ncsync",
    168 	"hskew",
    169 	"bcast",
    170 	"pixmux",
    171 	"dblclk",
    172 	"clkdiv2"
    173 };
    174 
    175 static bit_name_fn(mode_flag)
    176 
    177 static void dump_encoders(struct device *dev)
    178 {
    179 	drmModeEncoder *encoder;
    180 	int i;
    181 
    182 	printf("Encoders:\n");
    183 	printf("id\tcrtc\ttype\tpossible crtcs\tpossible clones\t\n");
    184 	for (i = 0; i < dev->resources->res->count_encoders; i++) {
    185 		encoder = dev->resources->encoders[i].encoder;
    186 		if (!encoder)
    187 			continue;
    188 
    189 		printf("%d\t%d\t%s\t0x%08x\t0x%08x\n",
    190 		       encoder->encoder_id,
    191 		       encoder->crtc_id,
    192 		       util_lookup_encoder_type_name(encoder->encoder_type),
    193 		       encoder->possible_crtcs,
    194 		       encoder->possible_clones);
    195 	}
    196 	printf("\n");
    197 }
    198 
    199 static void dump_mode(drmModeModeInfo *mode)
    200 {
    201 	printf("  %s %d %d %d %d %d %d %d %d %d %d",
    202 	       mode->name,
    203 	       mode->vrefresh,
    204 	       mode->hdisplay,
    205 	       mode->hsync_start,
    206 	       mode->hsync_end,
    207 	       mode->htotal,
    208 	       mode->vdisplay,
    209 	       mode->vsync_start,
    210 	       mode->vsync_end,
    211 	       mode->vtotal,
    212 	       mode->clock);
    213 
    214 	printf(" flags: ");
    215 	mode_flag_str(mode->flags);
    216 	printf("; type: ");
    217 	mode_type_str(mode->type);
    218 	printf("\n");
    219 }
    220 
    221 static void dump_blob(struct device *dev, uint32_t blob_id)
    222 {
    223 	uint32_t i;
    224 	unsigned char *blob_data;
    225 	drmModePropertyBlobPtr blob;
    226 
    227 	blob = drmModeGetPropertyBlob(dev->fd, blob_id);
    228 	if (!blob) {
    229 		printf("\n");
    230 		return;
    231 	}
    232 
    233 	blob_data = blob->data;
    234 
    235 	for (i = 0; i < blob->length; i++) {
    236 		if (i % 16 == 0)
    237 			printf("\n\t\t\t");
    238 		printf("%.2hhx", blob_data[i]);
    239 	}
    240 	printf("\n");
    241 
    242 	drmModeFreePropertyBlob(blob);
    243 }
    244 
    245 static void dump_prop(struct device *dev, drmModePropertyPtr prop,
    246 		      uint32_t prop_id, uint64_t value)
    247 {
    248 	int i;
    249 	printf("\t%d", prop_id);
    250 	if (!prop) {
    251 		printf("\n");
    252 		return;
    253 	}
    254 
    255 	printf(" %s:\n", prop->name);
    256 
    257 	printf("\t\tflags:");
    258 	if (prop->flags & DRM_MODE_PROP_PENDING)
    259 		printf(" pending");
    260 	if (prop->flags & DRM_MODE_PROP_IMMUTABLE)
    261 		printf(" immutable");
    262 	if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE))
    263 		printf(" signed range");
    264 	if (drm_property_type_is(prop, DRM_MODE_PROP_RANGE))
    265 		printf(" range");
    266 	if (drm_property_type_is(prop, DRM_MODE_PROP_ENUM))
    267 		printf(" enum");
    268 	if (drm_property_type_is(prop, DRM_MODE_PROP_BITMASK))
    269 		printf(" bitmask");
    270 	if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB))
    271 		printf(" blob");
    272 	if (drm_property_type_is(prop, DRM_MODE_PROP_OBJECT))
    273 		printf(" object");
    274 	printf("\n");
    275 
    276 	if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE)) {
    277 		printf("\t\tvalues:");
    278 		for (i = 0; i < prop->count_values; i++)
    279 			printf(" %"PRId64, U642I64(prop->values[i]));
    280 		printf("\n");
    281 	}
    282 
    283 	if (drm_property_type_is(prop, DRM_MODE_PROP_RANGE)) {
    284 		printf("\t\tvalues:");
    285 		for (i = 0; i < prop->count_values; i++)
    286 			printf(" %"PRIu64, prop->values[i]);
    287 		printf("\n");
    288 	}
    289 
    290 	if (drm_property_type_is(prop, DRM_MODE_PROP_ENUM)) {
    291 		printf("\t\tenums:");
    292 		for (i = 0; i < prop->count_enums; i++)
    293 			printf(" %s=%llu", prop->enums[i].name,
    294 			       prop->enums[i].value);
    295 		printf("\n");
    296 	} else if (drm_property_type_is(prop, DRM_MODE_PROP_BITMASK)) {
    297 		printf("\t\tvalues:");
    298 		for (i = 0; i < prop->count_enums; i++)
    299 			printf(" %s=0x%llx", prop->enums[i].name,
    300 			       (1LL << prop->enums[i].value));
    301 		printf("\n");
    302 	} else {
    303 		assert(prop->count_enums == 0);
    304 	}
    305 
    306 	if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB)) {
    307 		printf("\t\tblobs:\n");
    308 		for (i = 0; i < prop->count_blobs; i++)
    309 			dump_blob(dev, prop->blob_ids[i]);
    310 		printf("\n");
    311 	} else {
    312 		assert(prop->count_blobs == 0);
    313 	}
    314 
    315 	printf("\t\tvalue:");
    316 	if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB))
    317 		dump_blob(dev, value);
    318 	else if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE))
    319 		printf(" %"PRId64"\n", value);
    320 	else
    321 		printf(" %"PRIu64"\n", value);
    322 }
    323 
    324 static void dump_connectors(struct device *dev)
    325 {
    326 	int i, j;
    327 
    328 	printf("Connectors:\n");
    329 	printf("id\tencoder\tstatus\t\tname\t\tsize (mm)\tmodes\tencoders\n");
    330 	for (i = 0; i < dev->resources->res->count_connectors; i++) {
    331 		struct connector *_connector = &dev->resources->connectors[i];
    332 		drmModeConnector *connector = _connector->connector;
    333 		if (!connector)
    334 			continue;
    335 
    336 		printf("%d\t%d\t%s\t%-15s\t%dx%d\t\t%d\t",
    337 		       connector->connector_id,
    338 		       connector->encoder_id,
    339 		       util_lookup_connector_status_name(connector->connection),
    340 		       _connector->name,
    341 		       connector->mmWidth, connector->mmHeight,
    342 		       connector->count_modes);
    343 
    344 		for (j = 0; j < connector->count_encoders; j++)
    345 			printf("%s%d", j > 0 ? ", " : "", connector->encoders[j]);
    346 		printf("\n");
    347 
    348 		if (connector->count_modes) {
    349 			printf("  modes:\n");
    350 			printf("\tname refresh (Hz) hdisp hss hse htot vdisp "
    351 			       "vss vse vtot)\n");
    352 			for (j = 0; j < connector->count_modes; j++)
    353 				dump_mode(&connector->modes[j]);
    354 		}
    355 
    356 		if (_connector->props) {
    357 			printf("  props:\n");
    358 			for (j = 0; j < (int)_connector->props->count_props; j++)
    359 				dump_prop(dev, _connector->props_info[j],
    360 					  _connector->props->props[j],
    361 					  _connector->props->prop_values[j]);
    362 		}
    363 	}
    364 	printf("\n");
    365 }
    366 
    367 static void dump_crtcs(struct device *dev)
    368 {
    369 	int i;
    370 	uint32_t j;
    371 
    372 	printf("CRTCs:\n");
    373 	printf("id\tfb\tpos\tsize\n");
    374 	for (i = 0; i < dev->resources->res->count_crtcs; i++) {
    375 		struct crtc *_crtc = &dev->resources->crtcs[i];
    376 		drmModeCrtc *crtc = _crtc->crtc;
    377 		if (!crtc)
    378 			continue;
    379 
    380 		printf("%d\t%d\t(%d,%d)\t(%dx%d)\n",
    381 		       crtc->crtc_id,
    382 		       crtc->buffer_id,
    383 		       crtc->x, crtc->y,
    384 		       crtc->width, crtc->height);
    385 		dump_mode(&crtc->mode);
    386 
    387 		if (_crtc->props) {
    388 			printf("  props:\n");
    389 			for (j = 0; j < _crtc->props->count_props; j++)
    390 				dump_prop(dev, _crtc->props_info[j],
    391 					  _crtc->props->props[j],
    392 					  _crtc->props->prop_values[j]);
    393 		} else {
    394 			printf("  no properties found\n");
    395 		}
    396 	}
    397 	printf("\n");
    398 }
    399 
    400 static void dump_framebuffers(struct device *dev)
    401 {
    402 	drmModeFB *fb;
    403 	int i;
    404 
    405 	printf("Frame buffers:\n");
    406 	printf("id\tsize\tpitch\n");
    407 	for (i = 0; i < dev->resources->res->count_fbs; i++) {
    408 		fb = dev->resources->fbs[i].fb;
    409 		if (!fb)
    410 			continue;
    411 
    412 		printf("%u\t(%ux%u)\t%u\n",
    413 		       fb->fb_id,
    414 		       fb->width, fb->height,
    415 		       fb->pitch);
    416 	}
    417 	printf("\n");
    418 }
    419 
    420 static void dump_planes(struct device *dev)
    421 {
    422 	unsigned int i, j;
    423 
    424 	printf("Planes:\n");
    425 	printf("id\tcrtc\tfb\tCRTC x,y\tx,y\tgamma size\tpossible crtcs\n");
    426 
    427 	if (!dev->resources->plane_res)
    428 		return;
    429 
    430 	for (i = 0; i < dev->resources->plane_res->count_planes; i++) {
    431 		struct plane *plane = &dev->resources->planes[i];
    432 		drmModePlane *ovr = plane->plane;
    433 		if (!ovr)
    434 			continue;
    435 
    436 		printf("%d\t%d\t%d\t%d,%d\t\t%d,%d\t%-8d\t0x%08x\n",
    437 		       ovr->plane_id, ovr->crtc_id, ovr->fb_id,
    438 		       ovr->crtc_x, ovr->crtc_y, ovr->x, ovr->y,
    439 		       ovr->gamma_size, ovr->possible_crtcs);
    440 
    441 		if (!ovr->count_formats)
    442 			continue;
    443 
    444 		printf("  formats:");
    445 		for (j = 0; j < ovr->count_formats; j++)
    446 			printf(" %4.4s", (char *)&ovr->formats[j]);
    447 		printf("\n");
    448 
    449 		if (plane->props) {
    450 			printf("  props:\n");
    451 			for (j = 0; j < plane->props->count_props; j++)
    452 				dump_prop(dev, plane->props_info[j],
    453 					  plane->props->props[j],
    454 					  plane->props->prop_values[j]);
    455 		} else {
    456 			printf("  no properties found\n");
    457 		}
    458 	}
    459 	printf("\n");
    460 
    461 	return;
    462 }
    463 
    464 static void free_resources(struct resources *res)
    465 {
    466 	int i;
    467 
    468 	if (!res)
    469 		return;
    470 
    471 #define free_resource(_res, __res, type, Type)					\
    472 	do {									\
    473 		if (!(_res)->type##s)						\
    474 			break;							\
    475 		for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) {	\
    476 			if (!(_res)->type##s[i].type)				\
    477 				break;						\
    478 			drmModeFree##Type((_res)->type##s[i].type);		\
    479 		}								\
    480 		free((_res)->type##s);						\
    481 	} while (0)
    482 
    483 #define free_properties(_res, __res, type)					\
    484 	do {									\
    485 		for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) {	\
    486 			drmModeFreeObjectProperties(res->type##s[i].props);	\
    487 			free(res->type##s[i].props_info);			\
    488 		}								\
    489 	} while (0)
    490 
    491 	if (res->res) {
    492 		free_properties(res, res, crtc);
    493 
    494 		free_resource(res, res, crtc, Crtc);
    495 		free_resource(res, res, encoder, Encoder);
    496 
    497 		for (i = 0; i < res->res->count_connectors; i++)
    498 			free(res->connectors[i].name);
    499 
    500 		free_resource(res, res, connector, Connector);
    501 		free_resource(res, res, fb, FB);
    502 
    503 		drmModeFreeResources(res->res);
    504 	}
    505 
    506 	if (res->plane_res) {
    507 		free_properties(res, plane_res, plane);
    508 
    509 		free_resource(res, plane_res, plane, Plane);
    510 
    511 		drmModeFreePlaneResources(res->plane_res);
    512 	}
    513 
    514 	free(res);
    515 }
    516 
    517 static struct resources *get_resources(struct device *dev)
    518 {
    519 	struct resources *res;
    520 	int i;
    521 
    522 	res = calloc(1, sizeof(*res));
    523 	if (res == 0)
    524 		return NULL;
    525 
    526 	drmSetClientCap(dev->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
    527 	drmSetClientCap(dev->fd, DRM_CLIENT_CAP_ATOMIC, 1);
    528 
    529 	res->res = drmModeGetResources(dev->fd);
    530 	if (!res->res) {
    531 		fprintf(stderr, "drmModeGetResources failed: %s\n",
    532 			strerror(errno));
    533 		goto error;
    534 	}
    535 
    536 	res->crtcs = calloc(res->res->count_crtcs, sizeof(*res->crtcs));
    537 	res->encoders = calloc(res->res->count_encoders, sizeof(*res->encoders));
    538 	res->connectors = calloc(res->res->count_connectors, sizeof(*res->connectors));
    539 	res->fbs = calloc(res->res->count_fbs, sizeof(*res->fbs));
    540 
    541 	if (!res->crtcs || !res->encoders || !res->connectors || !res->fbs)
    542 		goto error;
    543 
    544 #define get_resource(_res, __res, type, Type)					\
    545 	do {									\
    546 		for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) {	\
    547 			(_res)->type##s[i].type =				\
    548 				drmModeGet##Type(dev->fd, (_res)->__res->type##s[i]); \
    549 			if (!(_res)->type##s[i].type)				\
    550 				fprintf(stderr, "could not get %s %i: %s\n",	\
    551 					#type, (_res)->__res->type##s[i],	\
    552 					strerror(errno));			\
    553 		}								\
    554 	} while (0)
    555 
    556 	get_resource(res, res, crtc, Crtc);
    557 	get_resource(res, res, encoder, Encoder);
    558 	get_resource(res, res, connector, Connector);
    559 	get_resource(res, res, fb, FB);
    560 
    561 	/* Set the name of all connectors based on the type name and the per-type ID. */
    562 	for (i = 0; i < res->res->count_connectors; i++) {
    563 		struct connector *connector = &res->connectors[i];
    564 		drmModeConnector *conn = connector->connector;
    565 
    566 		asprintf(&connector->name, "%s-%u",
    567 			 util_lookup_connector_type_name(conn->connector_type),
    568 			 conn->connector_type_id);
    569 	}
    570 
    571 #define get_properties(_res, __res, type, Type)					\
    572 	do {									\
    573 		for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) {	\
    574 			struct type *obj = &res->type##s[i];			\
    575 			unsigned int j;						\
    576 			obj->props =						\
    577 				drmModeObjectGetProperties(dev->fd, obj->type->type##_id, \
    578 							   DRM_MODE_OBJECT_##Type); \
    579 			if (!obj->props) {					\
    580 				fprintf(stderr,					\
    581 					"could not get %s %i properties: %s\n", \
    582 					#type, obj->type->type##_id,		\
    583 					strerror(errno));			\
    584 				continue;					\
    585 			}							\
    586 			obj->props_info = calloc(obj->props->count_props,	\
    587 						 sizeof(*obj->props_info));	\
    588 			if (!obj->props_info)					\
    589 				continue;					\
    590 			for (j = 0; j < obj->props->count_props; ++j)		\
    591 				obj->props_info[j] =				\
    592 					drmModeGetProperty(dev->fd, obj->props->props[j]); \
    593 		}								\
    594 	} while (0)
    595 
    596 	get_properties(res, res, crtc, CRTC);
    597 	get_properties(res, res, connector, CONNECTOR);
    598 
    599 	for (i = 0; i < res->res->count_crtcs; ++i)
    600 		res->crtcs[i].mode = &res->crtcs[i].crtc->mode;
    601 
    602 	res->plane_res = drmModeGetPlaneResources(dev->fd);
    603 	if (!res->plane_res) {
    604 		fprintf(stderr, "drmModeGetPlaneResources failed: %s\n",
    605 			strerror(errno));
    606 		return res;
    607 	}
    608 
    609 	res->planes = calloc(res->plane_res->count_planes, sizeof(*res->planes));
    610 	if (!res->planes)
    611 		goto error;
    612 
    613 	get_resource(res, plane_res, plane, Plane);
    614 	get_properties(res, plane_res, plane, PLANE);
    615 
    616 	return res;
    617 
    618 error:
    619 	free_resources(res);
    620 	return NULL;
    621 }
    622 
    623 static int get_crtc_index(struct device *dev, uint32_t id)
    624 {
    625 	int i;
    626 
    627 	for (i = 0; i < dev->resources->res->count_crtcs; ++i) {
    628 		drmModeCrtc *crtc = dev->resources->crtcs[i].crtc;
    629 		if (crtc && crtc->crtc_id == id)
    630 			return i;
    631 	}
    632 
    633 	return -1;
    634 }
    635 
    636 static drmModeConnector *get_connector_by_name(struct device *dev, const char *name)
    637 {
    638 	struct connector *connector;
    639 	int i;
    640 
    641 	for (i = 0; i < dev->resources->res->count_connectors; i++) {
    642 		connector = &dev->resources->connectors[i];
    643 
    644 		if (strcmp(connector->name, name) == 0)
    645 			return connector->connector;
    646 	}
    647 
    648 	return NULL;
    649 }
    650 
    651 static drmModeConnector *get_connector_by_id(struct device *dev, uint32_t id)
    652 {
    653 	drmModeConnector *connector;
    654 	int i;
    655 
    656 	for (i = 0; i < dev->resources->res->count_connectors; i++) {
    657 		connector = dev->resources->connectors[i].connector;
    658 		if (connector && connector->connector_id == id)
    659 			return connector;
    660 	}
    661 
    662 	return NULL;
    663 }
    664 
    665 static drmModeEncoder *get_encoder_by_id(struct device *dev, uint32_t id)
    666 {
    667 	drmModeEncoder *encoder;
    668 	int i;
    669 
    670 	for (i = 0; i < dev->resources->res->count_encoders; i++) {
    671 		encoder = dev->resources->encoders[i].encoder;
    672 		if (encoder && encoder->encoder_id == id)
    673 			return encoder;
    674 	}
    675 
    676 	return NULL;
    677 }
    678 
    679 /* -----------------------------------------------------------------------------
    680  * Pipes and planes
    681  */
    682 
    683 /*
    684  * Mode setting with the kernel interfaces is a bit of a chore.
    685  * First you have to find the connector in question and make sure the
    686  * requested mode is available.
    687  * Then you need to find the encoder attached to that connector so you
    688  * can bind it with a free crtc.
    689  */
    690 struct pipe_arg {
    691 	const char **cons;
    692 	uint32_t *con_ids;
    693 	unsigned int num_cons;
    694 	uint32_t crtc_id;
    695 	char mode_str[64];
    696 	char format_str[5];
    697 	unsigned int vrefresh;
    698 	unsigned int fourcc;
    699 	drmModeModeInfo *mode;
    700 	struct crtc *crtc;
    701 	unsigned int fb_id[2], current_fb_id;
    702 	struct timeval start;
    703 
    704 	int swap_count;
    705 };
    706 
    707 struct plane_arg {
    708 	uint32_t plane_id;  /* the id of plane to use */
    709 	uint32_t crtc_id;  /* the id of CRTC to bind to */
    710 	bool has_position;
    711 	int32_t x, y;
    712 	uint32_t w, h;
    713 	double scale;
    714 	unsigned int fb_id;
    715 	struct bo *bo;
    716 	char format_str[5]; /* need to leave room for terminating \0 */
    717 	unsigned int fourcc;
    718 };
    719 
    720 static drmModeModeInfo *
    721 connector_find_mode(struct device *dev, uint32_t con_id, const char *mode_str,
    722         const unsigned int vrefresh)
    723 {
    724 	drmModeConnector *connector;
    725 	drmModeModeInfo *mode;
    726 	int i;
    727 
    728 	connector = get_connector_by_id(dev, con_id);
    729 	if (!connector || !connector->count_modes)
    730 		return NULL;
    731 
    732 	for (i = 0; i < connector->count_modes; i++) {
    733 		mode = &connector->modes[i];
    734 		if (!strcmp(mode->name, mode_str)) {
    735 			/* If the vertical refresh frequency is not specified then return the
    736 			 * first mode that match with the name. Else, return the mode that match
    737 			 * the name and the specified vertical refresh frequency.
    738 			 */
    739 			if (vrefresh == 0)
    740 				return mode;
    741 			else if (mode->vrefresh == vrefresh)
    742 				return mode;
    743 		}
    744 	}
    745 
    746 	return NULL;
    747 }
    748 
    749 static struct crtc *pipe_find_crtc(struct device *dev, struct pipe_arg *pipe)
    750 {
    751 	uint32_t possible_crtcs = ~0;
    752 	uint32_t active_crtcs = 0;
    753 	unsigned int crtc_idx;
    754 	unsigned int i;
    755 	int j;
    756 
    757 	for (i = 0; i < pipe->num_cons; ++i) {
    758 		uint32_t crtcs_for_connector = 0;
    759 		drmModeConnector *connector;
    760 		drmModeEncoder *encoder;
    761 		int idx;
    762 
    763 		connector = get_connector_by_id(dev, pipe->con_ids[i]);
    764 		if (!connector)
    765 			return NULL;
    766 
    767 		for (j = 0; j < connector->count_encoders; ++j) {
    768 			encoder = get_encoder_by_id(dev, connector->encoders[j]);
    769 			if (!encoder)
    770 				continue;
    771 
    772 			crtcs_for_connector |= encoder->possible_crtcs;
    773 
    774 			idx = get_crtc_index(dev, encoder->crtc_id);
    775 			if (idx >= 0)
    776 				active_crtcs |= 1 << idx;
    777 		}
    778 
    779 		possible_crtcs &= crtcs_for_connector;
    780 	}
    781 
    782 	if (!possible_crtcs)
    783 		return NULL;
    784 
    785 	/* Return the first possible and active CRTC if one exists, or the first
    786 	 * possible CRTC otherwise.
    787 	 */
    788 	if (possible_crtcs & active_crtcs)
    789 		crtc_idx = ffs(possible_crtcs & active_crtcs);
    790 	else
    791 		crtc_idx = ffs(possible_crtcs);
    792 
    793 	return &dev->resources->crtcs[crtc_idx - 1];
    794 }
    795 
    796 static int pipe_find_crtc_and_mode(struct device *dev, struct pipe_arg *pipe)
    797 {
    798 	drmModeModeInfo *mode = NULL;
    799 	int i;
    800 
    801 	pipe->mode = NULL;
    802 
    803 	for (i = 0; i < (int)pipe->num_cons; i++) {
    804 		mode = connector_find_mode(dev, pipe->con_ids[i],
    805 					   pipe->mode_str, pipe->vrefresh);
    806 		if (mode == NULL) {
    807 			fprintf(stderr,
    808 				"failed to find mode \"%s\" for connector %s\n",
    809 				pipe->mode_str, pipe->cons[i]);
    810 			return -EINVAL;
    811 		}
    812 	}
    813 
    814 	/* If the CRTC ID was specified, get the corresponding CRTC. Otherwise
    815 	 * locate a CRTC that can be attached to all the connectors.
    816 	 */
    817 	if (pipe->crtc_id != (uint32_t)-1) {
    818 		for (i = 0; i < dev->resources->res->count_crtcs; i++) {
    819 			struct crtc *crtc = &dev->resources->crtcs[i];
    820 
    821 			if (pipe->crtc_id == crtc->crtc->crtc_id) {
    822 				pipe->crtc = crtc;
    823 				break;
    824 			}
    825 		}
    826 	} else {
    827 		pipe->crtc = pipe_find_crtc(dev, pipe);
    828 	}
    829 
    830 	if (!pipe->crtc) {
    831 		fprintf(stderr, "failed to find CRTC for pipe\n");
    832 		return -EINVAL;
    833 	}
    834 
    835 	pipe->mode = mode;
    836 	pipe->crtc->mode = mode;
    837 
    838 	return 0;
    839 }
    840 
    841 /* -----------------------------------------------------------------------------
    842  * Properties
    843  */
    844 
    845 struct property_arg {
    846 	uint32_t obj_id;
    847 	uint32_t obj_type;
    848 	char name[DRM_PROP_NAME_LEN+1];
    849 	uint32_t prop_id;
    850 	uint64_t value;
    851 };
    852 
    853 static void set_property(struct device *dev, struct property_arg *p)
    854 {
    855 	drmModeObjectProperties *props = NULL;
    856 	drmModePropertyRes **props_info = NULL;
    857 	const char *obj_type;
    858 	int ret;
    859 	int i;
    860 
    861 	p->obj_type = 0;
    862 	p->prop_id = 0;
    863 
    864 #define find_object(_res, __res, type, Type)					\
    865 	do {									\
    866 		for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) {	\
    867 			struct type *obj = &(_res)->type##s[i];			\
    868 			if (obj->type->type##_id != p->obj_id)			\
    869 				continue;					\
    870 			p->obj_type = DRM_MODE_OBJECT_##Type;			\
    871 			obj_type = #Type;					\
    872 			props = obj->props;					\
    873 			props_info = obj->props_info;				\
    874 		}								\
    875 	} while(0)								\
    876 
    877 	find_object(dev->resources, res, crtc, CRTC);
    878 	if (p->obj_type == 0)
    879 		find_object(dev->resources, res, connector, CONNECTOR);
    880 	if (p->obj_type == 0)
    881 		find_object(dev->resources, plane_res, plane, PLANE);
    882 	if (p->obj_type == 0) {
    883 		fprintf(stderr, "Object %i not found, can't set property\n",
    884 			p->obj_id);
    885 			return;
    886 	}
    887 
    888 	if (!props) {
    889 		fprintf(stderr, "%s %i has no properties\n",
    890 			obj_type, p->obj_id);
    891 		return;
    892 	}
    893 
    894 	for (i = 0; i < (int)props->count_props; ++i) {
    895 		if (!props_info[i])
    896 			continue;
    897 		if (strcmp(props_info[i]->name, p->name) == 0)
    898 			break;
    899 	}
    900 
    901 	if (i == (int)props->count_props) {
    902 		fprintf(stderr, "%s %i has no %s property\n",
    903 			obj_type, p->obj_id, p->name);
    904 		return;
    905 	}
    906 
    907 	p->prop_id = props->props[i];
    908 
    909 	ret = drmModeObjectSetProperty(dev->fd, p->obj_id, p->obj_type,
    910 				       p->prop_id, p->value);
    911 	if (ret < 0)
    912 		fprintf(stderr, "failed to set %s %i property %s to %" PRIu64 ": %s\n",
    913 			obj_type, p->obj_id, p->name, p->value, strerror(errno));
    914 }
    915 
    916 /* -------------------------------------------------------------------------- */
    917 
    918 static void
    919 page_flip_handler(int fd, unsigned int frame,
    920 		  unsigned int sec, unsigned int usec, void *data)
    921 {
    922 	struct pipe_arg *pipe;
    923 	unsigned int new_fb_id;
    924 	struct timeval end;
    925 	double t;
    926 
    927 	pipe = data;
    928 	if (pipe->current_fb_id == pipe->fb_id[0])
    929 		new_fb_id = pipe->fb_id[1];
    930 	else
    931 		new_fb_id = pipe->fb_id[0];
    932 
    933 	drmModePageFlip(fd, pipe->crtc->crtc->crtc_id, new_fb_id,
    934 			DRM_MODE_PAGE_FLIP_EVENT, pipe);
    935 	pipe->current_fb_id = new_fb_id;
    936 	pipe->swap_count++;
    937 	if (pipe->swap_count == 60) {
    938 		gettimeofday(&end, NULL);
    939 		t = end.tv_sec + end.tv_usec * 1e-6 -
    940 			(pipe->start.tv_sec + pipe->start.tv_usec * 1e-6);
    941 		fprintf(stderr, "freq: %.02fHz\n", pipe->swap_count / t);
    942 		pipe->swap_count = 0;
    943 		pipe->start = end;
    944 	}
    945 }
    946 
    947 static bool format_support(const drmModePlanePtr ovr, uint32_t fmt)
    948 {
    949 	unsigned int i;
    950 
    951 	for (i = 0; i < ovr->count_formats; ++i) {
    952 		if (ovr->formats[i] == fmt)
    953 			return true;
    954 	}
    955 
    956 	return false;
    957 }
    958 
    959 static int set_plane(struct device *dev, struct plane_arg *p)
    960 {
    961 	drmModePlane *ovr;
    962 	uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
    963 	uint32_t plane_id;
    964 	struct bo *plane_bo;
    965 	uint32_t plane_flags = 0;
    966 	int crtc_x, crtc_y, crtc_w, crtc_h;
    967 	struct crtc *crtc = NULL;
    968 	unsigned int pipe;
    969 	unsigned int i;
    970 
    971 	/* Find an unused plane which can be connected to our CRTC. Find the
    972 	 * CRTC index first, then iterate over available planes.
    973 	 */
    974 	for (i = 0; i < (unsigned int)dev->resources->res->count_crtcs; i++) {
    975 		if (p->crtc_id == dev->resources->res->crtcs[i]) {
    976 			crtc = &dev->resources->crtcs[i];
    977 			pipe = i;
    978 			break;
    979 		}
    980 	}
    981 
    982 	if (!crtc) {
    983 		fprintf(stderr, "CRTC %u not found\n", p->crtc_id);
    984 		return -1;
    985 	}
    986 
    987 	plane_id = p->plane_id;
    988 
    989 	for (i = 0; i < dev->resources->plane_res->count_planes; i++) {
    990 		ovr = dev->resources->planes[i].plane;
    991 		if (!ovr)
    992 			continue;
    993 
    994 		if (plane_id && plane_id != ovr->plane_id)
    995 			continue;
    996 
    997 		if (!format_support(ovr, p->fourcc))
    998 			continue;
    999 
   1000 		if ((ovr->possible_crtcs & (1 << pipe)) && !ovr->crtc_id) {
   1001 			plane_id = ovr->plane_id;
   1002 			break;
   1003 		}
   1004 	}
   1005 
   1006 	if (i == dev->resources->plane_res->count_planes) {
   1007 		fprintf(stderr, "no unused plane available for CRTC %u\n",
   1008 			crtc->crtc->crtc_id);
   1009 		return -1;
   1010 	}
   1011 
   1012 	fprintf(stderr, "testing %dx%d@%s overlay plane %u\n",
   1013 		p->w, p->h, p->format_str, plane_id);
   1014 
   1015 	plane_bo = bo_create(dev->fd, p->fourcc, p->w, p->h, handles,
   1016 			     pitches, offsets, UTIL_PATTERN_TILES);
   1017 	if (plane_bo == NULL)
   1018 		return -1;
   1019 
   1020 	p->bo = plane_bo;
   1021 
   1022 	/* just use single plane format for now.. */
   1023 	if (drmModeAddFB2(dev->fd, p->w, p->h, p->fourcc,
   1024 			handles, pitches, offsets, &p->fb_id, plane_flags)) {
   1025 		fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
   1026 		return -1;
   1027 	}
   1028 
   1029 	crtc_w = p->w * p->scale;
   1030 	crtc_h = p->h * p->scale;
   1031 	if (!p->has_position) {
   1032 		/* Default to the middle of the screen */
   1033 		crtc_x = (crtc->mode->hdisplay - crtc_w) / 2;
   1034 		crtc_y = (crtc->mode->vdisplay - crtc_h) / 2;
   1035 	} else {
   1036 		crtc_x = p->x;
   1037 		crtc_y = p->y;
   1038 	}
   1039 
   1040 	/* note src coords (last 4 args) are in Q16 format */
   1041 	if (drmModeSetPlane(dev->fd, plane_id, crtc->crtc->crtc_id, p->fb_id,
   1042 			    plane_flags, crtc_x, crtc_y, crtc_w, crtc_h,
   1043 			    0, 0, p->w << 16, p->h << 16)) {
   1044 		fprintf(stderr, "failed to enable plane: %s\n",
   1045 			strerror(errno));
   1046 		return -1;
   1047 	}
   1048 
   1049 	ovr->crtc_id = crtc->crtc->crtc_id;
   1050 
   1051 	return 0;
   1052 }
   1053 
   1054 static void clear_planes(struct device *dev, struct plane_arg *p, unsigned int count)
   1055 {
   1056 	unsigned int i;
   1057 
   1058 	for (i = 0; i < count; i++) {
   1059 		if (p[i].fb_id)
   1060 			drmModeRmFB(dev->fd, p[i].fb_id);
   1061 		if (p[i].bo)
   1062 			bo_destroy(p[i].bo);
   1063 	}
   1064 }
   1065 
   1066 
   1067 static void set_mode(struct device *dev, struct pipe_arg *pipes, unsigned int count)
   1068 {
   1069 	uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
   1070 	unsigned int fb_id;
   1071 	struct bo *bo;
   1072 	unsigned int i;
   1073 	unsigned int j;
   1074 	int ret, x;
   1075 
   1076 	dev->mode.width = 0;
   1077 	dev->mode.height = 0;
   1078 	dev->mode.fb_id = 0;
   1079 
   1080 	for (i = 0; i < count; i++) {
   1081 		struct pipe_arg *pipe = &pipes[i];
   1082 
   1083 		ret = pipe_find_crtc_and_mode(dev, pipe);
   1084 		if (ret < 0)
   1085 			continue;
   1086 
   1087 		dev->mode.width += pipe->mode->hdisplay;
   1088 		if (dev->mode.height < pipe->mode->vdisplay)
   1089 			dev->mode.height = pipe->mode->vdisplay;
   1090 	}
   1091 
   1092 	bo = bo_create(dev->fd, pipes[0].fourcc, dev->mode.width,
   1093 		       dev->mode.height, handles, pitches, offsets,
   1094 		       UTIL_PATTERN_SMPTE);
   1095 	if (bo == NULL)
   1096 		return;
   1097 
   1098 	dev->mode.bo = bo;
   1099 
   1100 	ret = drmModeAddFB2(dev->fd, dev->mode.width, dev->mode.height,
   1101 			    pipes[0].fourcc, handles, pitches, offsets, &fb_id, 0);
   1102 	if (ret) {
   1103 		fprintf(stderr, "failed to add fb (%ux%u): %s\n",
   1104 			dev->mode.width, dev->mode.height, strerror(errno));
   1105 		return;
   1106 	}
   1107 
   1108 	dev->mode.fb_id = fb_id;
   1109 
   1110 	x = 0;
   1111 	for (i = 0; i < count; i++) {
   1112 		struct pipe_arg *pipe = &pipes[i];
   1113 
   1114 		if (pipe->mode == NULL)
   1115 			continue;
   1116 
   1117 		printf("setting mode %s-%dHz@%s on connectors ",
   1118 		       pipe->mode_str, pipe->mode->vrefresh, pipe->format_str);
   1119 		for (j = 0; j < pipe->num_cons; ++j)
   1120 			printf("%s, ", pipe->cons[j]);
   1121 		printf("crtc %d\n", pipe->crtc->crtc->crtc_id);
   1122 
   1123 		ret = drmModeSetCrtc(dev->fd, pipe->crtc->crtc->crtc_id, fb_id,
   1124 				     x, 0, pipe->con_ids, pipe->num_cons,
   1125 				     pipe->mode);
   1126 
   1127 		/* XXX: Actually check if this is needed */
   1128 		drmModeDirtyFB(dev->fd, fb_id, NULL, 0);
   1129 
   1130 		x += pipe->mode->hdisplay;
   1131 
   1132 		if (ret) {
   1133 			fprintf(stderr, "failed to set mode: %s\n", strerror(errno));
   1134 			return;
   1135 		}
   1136 	}
   1137 }
   1138 
   1139 static void clear_mode(struct device *dev)
   1140 {
   1141 	if (dev->mode.fb_id)
   1142 		drmModeRmFB(dev->fd, dev->mode.fb_id);
   1143 	if (dev->mode.bo)
   1144 		bo_destroy(dev->mode.bo);
   1145 }
   1146 
   1147 static void set_planes(struct device *dev, struct plane_arg *p, unsigned int count)
   1148 {
   1149 	unsigned int i;
   1150 
   1151 	/* set up planes/overlays */
   1152 	for (i = 0; i < count; i++)
   1153 		if (set_plane(dev, &p[i]))
   1154 			return;
   1155 }
   1156 
   1157 static void set_cursors(struct device *dev, struct pipe_arg *pipes, unsigned int count)
   1158 {
   1159 	uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
   1160 	struct bo *bo;
   1161 	unsigned int i;
   1162 	int ret;
   1163 
   1164 	/* maybe make cursor width/height configurable some day */
   1165 	uint32_t cw = 64;
   1166 	uint32_t ch = 64;
   1167 
   1168 	/* create cursor bo.. just using PATTERN_PLAIN as it has
   1169 	 * translucent alpha
   1170 	 */
   1171 	bo = bo_create(dev->fd, DRM_FORMAT_ARGB8888, cw, ch, handles, pitches,
   1172 		       offsets, UTIL_PATTERN_PLAIN);
   1173 	if (bo == NULL)
   1174 		return;
   1175 
   1176 	dev->mode.cursor_bo = bo;
   1177 
   1178 	for (i = 0; i < count; i++) {
   1179 		struct pipe_arg *pipe = &pipes[i];
   1180 		ret = cursor_init(dev->fd, handles[0],
   1181 				pipe->crtc->crtc->crtc_id,
   1182 				pipe->mode->hdisplay, pipe->mode->vdisplay,
   1183 				cw, ch);
   1184 		if (ret) {
   1185 			fprintf(stderr, "failed to init cursor for CRTC[%u]\n",
   1186 					pipe->crtc_id);
   1187 			return;
   1188 		}
   1189 	}
   1190 
   1191 	cursor_start();
   1192 }
   1193 
   1194 static void clear_cursors(struct device *dev)
   1195 {
   1196 	cursor_stop();
   1197 
   1198 	if (dev->mode.cursor_bo)
   1199 		bo_destroy(dev->mode.cursor_bo);
   1200 }
   1201 
   1202 static void test_page_flip(struct device *dev, struct pipe_arg *pipes, unsigned int count)
   1203 {
   1204 	uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
   1205 	unsigned int other_fb_id;
   1206 	struct bo *other_bo;
   1207 	drmEventContext evctx;
   1208 	unsigned int i;
   1209 	int ret;
   1210 
   1211 	other_bo = bo_create(dev->fd, pipes[0].fourcc, dev->mode.width,
   1212 			     dev->mode.height, handles, pitches, offsets,
   1213 			     UTIL_PATTERN_PLAIN);
   1214 	if (other_bo == NULL)
   1215 		return;
   1216 
   1217 	ret = drmModeAddFB2(dev->fd, dev->mode.width, dev->mode.height,
   1218 			    pipes[0].fourcc, handles, pitches, offsets,
   1219 			    &other_fb_id, 0);
   1220 	if (ret) {
   1221 		fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
   1222 		goto err;
   1223 	}
   1224 
   1225 	for (i = 0; i < count; i++) {
   1226 		struct pipe_arg *pipe = &pipes[i];
   1227 
   1228 		if (pipe->mode == NULL)
   1229 			continue;
   1230 
   1231 		ret = drmModePageFlip(dev->fd, pipe->crtc->crtc->crtc_id,
   1232 				      other_fb_id, DRM_MODE_PAGE_FLIP_EVENT,
   1233 				      pipe);
   1234 		if (ret) {
   1235 			fprintf(stderr, "failed to page flip: %s\n", strerror(errno));
   1236 			goto err_rmfb;
   1237 		}
   1238 		gettimeofday(&pipe->start, NULL);
   1239 		pipe->swap_count = 0;
   1240 		pipe->fb_id[0] = dev->mode.fb_id;
   1241 		pipe->fb_id[1] = other_fb_id;
   1242 		pipe->current_fb_id = other_fb_id;
   1243 	}
   1244 
   1245 	memset(&evctx, 0, sizeof evctx);
   1246 	evctx.version = DRM_EVENT_CONTEXT_VERSION;
   1247 	evctx.vblank_handler = NULL;
   1248 	evctx.page_flip_handler = page_flip_handler;
   1249 
   1250 	while (1) {
   1251 #if 0
   1252 		struct pollfd pfd[2];
   1253 
   1254 		pfd[0].fd = 0;
   1255 		pfd[0].events = POLLIN;
   1256 		pfd[1].fd = fd;
   1257 		pfd[1].events = POLLIN;
   1258 
   1259 		if (poll(pfd, 2, -1) < 0) {
   1260 			fprintf(stderr, "poll error\n");
   1261 			break;
   1262 		}
   1263 
   1264 		if (pfd[0].revents)
   1265 			break;
   1266 #else
   1267 		struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 };
   1268 		fd_set fds;
   1269 
   1270 		FD_ZERO(&fds);
   1271 		FD_SET(0, &fds);
   1272 		FD_SET(dev->fd, &fds);
   1273 		ret = select(dev->fd + 1, &fds, NULL, NULL, &timeout);
   1274 
   1275 		if (ret <= 0) {
   1276 			fprintf(stderr, "select timed out or error (ret %d)\n",
   1277 				ret);
   1278 			continue;
   1279 		} else if (FD_ISSET(0, &fds)) {
   1280 			break;
   1281 		}
   1282 #endif
   1283 
   1284 		drmHandleEvent(dev->fd, &evctx);
   1285 	}
   1286 
   1287 err_rmfb:
   1288 	drmModeRmFB(dev->fd, other_fb_id);
   1289 err:
   1290 	bo_destroy(other_bo);
   1291 }
   1292 
   1293 #define min(a, b)	((a) < (b) ? (a) : (b))
   1294 
   1295 static int parse_connector(struct pipe_arg *pipe, const char *arg)
   1296 {
   1297 	unsigned int len;
   1298 	unsigned int i;
   1299 	const char *p;
   1300 	char *endp;
   1301 
   1302 	pipe->vrefresh = 0;
   1303 	pipe->crtc_id = (uint32_t)-1;
   1304 	strcpy(pipe->format_str, "XR24");
   1305 
   1306 	/* Count the number of connectors and allocate them. */
   1307 	pipe->num_cons = 1;
   1308 	for (p = arg; *p && *p != ':' && *p != '@'; ++p) {
   1309 		if (*p == ',')
   1310 			pipe->num_cons++;
   1311 	}
   1312 
   1313 	pipe->con_ids = calloc(pipe->num_cons, sizeof(*pipe->con_ids));
   1314 	pipe->cons = calloc(pipe->num_cons, sizeof(*pipe->cons));
   1315 	if (pipe->con_ids == NULL || pipe->cons == NULL)
   1316 		return -1;
   1317 
   1318 	/* Parse the connectors. */
   1319 	for (i = 0, p = arg; i < pipe->num_cons; ++i, p = endp + 1) {
   1320 		endp = strpbrk(p, ",@:");
   1321 		if (!endp)
   1322 			break;
   1323 
   1324 		pipe->cons[i] = strndup(p, endp - p);
   1325 
   1326 		if (*endp != ',')
   1327 			break;
   1328 	}
   1329 
   1330 	if (i != pipe->num_cons - 1)
   1331 		return -1;
   1332 
   1333 	/* Parse the remaining parameters. */
   1334 	if (*endp == '@') {
   1335 		arg = endp + 1;
   1336 		pipe->crtc_id = strtoul(arg, &endp, 10);
   1337 	}
   1338 	if (*endp != ':')
   1339 		return -1;
   1340 
   1341 	arg = endp + 1;
   1342 
   1343 	/* Search for the vertical refresh or the format. */
   1344 	p = strpbrk(arg, "-@");
   1345 	if (p == NULL)
   1346 		p = arg + strlen(arg);
   1347 	len = min(sizeof pipe->mode_str - 1, (unsigned int)(p - arg));
   1348 	strncpy(pipe->mode_str, arg, len);
   1349 	pipe->mode_str[len] = '\0';
   1350 
   1351 	if (*p == '-') {
   1352 		pipe->vrefresh = strtoul(p + 1, &endp, 10);
   1353 		p = endp;
   1354 	}
   1355 
   1356 	if (*p == '@') {
   1357 		strncpy(pipe->format_str, p + 1, 4);
   1358 		pipe->format_str[4] = '\0';
   1359 	}
   1360 
   1361 	pipe->fourcc = util_format_fourcc(pipe->format_str);
   1362 	if (pipe->fourcc == 0)  {
   1363 		fprintf(stderr, "unknown format %s\n", pipe->format_str);
   1364 		return -1;
   1365 	}
   1366 
   1367 	return 0;
   1368 }
   1369 
   1370 static int parse_plane(struct plane_arg *plane, const char *p)
   1371 {
   1372 	char *end;
   1373 
   1374 	plane->plane_id = strtoul(p, &end, 10);
   1375 	if (*end != '@')
   1376 		return -EINVAL;
   1377 
   1378 	p = end + 1;
   1379 	plane->crtc_id = strtoul(p, &end, 10);
   1380 	if (*end != ':')
   1381 		return -EINVAL;
   1382 
   1383 	p = end + 1;
   1384 	plane->w = strtoul(p, &end, 10);
   1385 	if (*end != 'x')
   1386 		return -EINVAL;
   1387 
   1388 	p = end + 1;
   1389 	plane->h = strtoul(p, &end, 10);
   1390 
   1391 	if (*end == '+' || *end == '-') {
   1392 		plane->x = strtol(end, &end, 10);
   1393 		if (*end != '+' && *end != '-')
   1394 			return -EINVAL;
   1395 		plane->y = strtol(end, &end, 10);
   1396 
   1397 		plane->has_position = true;
   1398 	}
   1399 
   1400 	if (*end == '*') {
   1401 		p = end + 1;
   1402 		plane->scale = strtod(p, &end);
   1403 		if (plane->scale <= 0.0)
   1404 			return -EINVAL;
   1405 	} else {
   1406 		plane->scale = 1.0;
   1407 	}
   1408 
   1409 	if (*end == '@') {
   1410 		p = end + 1;
   1411 		if (strlen(p) != 4)
   1412 			return -EINVAL;
   1413 
   1414 		strcpy(plane->format_str, p);
   1415 	} else {
   1416 		strcpy(plane->format_str, "XR24");
   1417 	}
   1418 
   1419 	plane->fourcc = util_format_fourcc(plane->format_str);
   1420 	if (plane->fourcc == 0) {
   1421 		fprintf(stderr, "unknown format %s\n", plane->format_str);
   1422 		return -EINVAL;
   1423 	}
   1424 
   1425 	return 0;
   1426 }
   1427 
   1428 static int parse_property(struct property_arg *p, const char *arg)
   1429 {
   1430 	if (sscanf(arg, "%d:%32[^:]:%" SCNu64, &p->obj_id, p->name, &p->value) != 3)
   1431 		return -1;
   1432 
   1433 	p->obj_type = 0;
   1434 	p->name[DRM_PROP_NAME_LEN] = '\0';
   1435 
   1436 	return 0;
   1437 }
   1438 
   1439 static void usage(char *name)
   1440 {
   1441 	fprintf(stderr, "usage: %s [-cDdefMPpsCvw]\n", name);
   1442 
   1443 	fprintf(stderr, "\n Query options:\n\n");
   1444 	fprintf(stderr, "\t-c\tlist connectors\n");
   1445 	fprintf(stderr, "\t-e\tlist encoders\n");
   1446 	fprintf(stderr, "\t-f\tlist framebuffers\n");
   1447 	fprintf(stderr, "\t-p\tlist CRTCs and planes (pipes)\n");
   1448 
   1449 	fprintf(stderr, "\n Test options:\n\n");
   1450 	fprintf(stderr, "\t-P <plane_id>@<crtc_id>:<w>x<h>[+<x>+<y>][*<scale>][@<format>]\tset a plane\n");
   1451 	fprintf(stderr, "\t-s <connector_id>[,<connector_id>][@<crtc_id>]:<mode>[-<vrefresh>][@<format>]\tset a mode\n");
   1452 	fprintf(stderr, "\t-C\ttest hw cursor\n");
   1453 	fprintf(stderr, "\t-v\ttest vsynced page flipping\n");
   1454 	fprintf(stderr, "\t-w <obj_id>:<prop_name>:<value>\tset property\n");
   1455 
   1456 	fprintf(stderr, "\n Generic options:\n\n");
   1457 	fprintf(stderr, "\t-d\tdrop master after mode set\n");
   1458 	fprintf(stderr, "\t-M module\tuse the given driver\n");
   1459 	fprintf(stderr, "\t-D device\tuse the given device\n");
   1460 
   1461 	fprintf(stderr, "\n\tDefault is to dump all info.\n");
   1462 	exit(0);
   1463 }
   1464 
   1465 static int page_flipping_supported(void)
   1466 {
   1467 	/*FIXME: generic ioctl needed? */
   1468 	return 1;
   1469 #if 0
   1470 	int ret, value;
   1471 	struct drm_i915_getparam gp;
   1472 
   1473 	gp.param = I915_PARAM_HAS_PAGEFLIPPING;
   1474 	gp.value = &value;
   1475 
   1476 	ret = drmCommandWriteRead(fd, DRM_I915_GETPARAM, &gp, sizeof(gp));
   1477 	if (ret) {
   1478 		fprintf(stderr, "drm_i915_getparam: %m\n");
   1479 		return 0;
   1480 	}
   1481 
   1482 	return *gp.value;
   1483 #endif
   1484 }
   1485 
   1486 static int cursor_supported(void)
   1487 {
   1488 	/*FIXME: generic ioctl needed? */
   1489 	return 1;
   1490 }
   1491 
   1492 static int pipe_resolve_connectors(struct device *dev, struct pipe_arg *pipe)
   1493 {
   1494 	drmModeConnector *connector;
   1495 	unsigned int i;
   1496 	uint32_t id;
   1497 	char *endp;
   1498 
   1499 	for (i = 0; i < pipe->num_cons; i++) {
   1500 		id = strtoul(pipe->cons[i], &endp, 10);
   1501 		if (endp == pipe->cons[i]) {
   1502 			connector = get_connector_by_name(dev, pipe->cons[i]);
   1503 			if (!connector) {
   1504 				fprintf(stderr, "no connector named '%s'\n",
   1505 					pipe->cons[i]);
   1506 				return -ENODEV;
   1507 			}
   1508 
   1509 			id = connector->connector_id;
   1510 		}
   1511 
   1512 		pipe->con_ids[i] = id;
   1513 	}
   1514 
   1515 	return 0;
   1516 }
   1517 
   1518 static char optstr[] = "cdD:efM:P:ps:Cvw:";
   1519 
   1520 int main(int argc, char **argv)
   1521 {
   1522 	struct device dev;
   1523 
   1524 	int c;
   1525 	int encoders = 0, connectors = 0, crtcs = 0, planes = 0, framebuffers = 0;
   1526 	int drop_master = 0;
   1527 	int test_vsync = 0;
   1528 	int test_cursor = 0;
   1529 	char *device = NULL;
   1530 	char *module = NULL;
   1531 	unsigned int i;
   1532 	unsigned int count = 0, plane_count = 0;
   1533 	unsigned int prop_count = 0;
   1534 	struct pipe_arg *pipe_args = NULL;
   1535 	struct plane_arg *plane_args = NULL;
   1536 	struct property_arg *prop_args = NULL;
   1537 	unsigned int args = 0;
   1538 	int ret;
   1539 
   1540 	memset(&dev, 0, sizeof dev);
   1541 
   1542 	opterr = 0;
   1543 	while ((c = getopt(argc, argv, optstr)) != -1) {
   1544 		args++;
   1545 
   1546 		switch (c) {
   1547 		case 'c':
   1548 			connectors = 1;
   1549 			break;
   1550 		case 'D':
   1551 			device = optarg;
   1552 			args--;
   1553 			break;
   1554 		case 'd':
   1555 			drop_master = 1;
   1556 			break;
   1557 		case 'e':
   1558 			encoders = 1;
   1559 			break;
   1560 		case 'f':
   1561 			framebuffers = 1;
   1562 			break;
   1563 		case 'M':
   1564 			module = optarg;
   1565 			/* Preserve the default behaviour of dumping all information. */
   1566 			args--;
   1567 			break;
   1568 		case 'P':
   1569 			plane_args = realloc(plane_args,
   1570 					     (plane_count + 1) * sizeof *plane_args);
   1571 			if (plane_args == NULL) {
   1572 				fprintf(stderr, "memory allocation failed\n");
   1573 				return 1;
   1574 			}
   1575 			memset(&plane_args[plane_count], 0, sizeof(*plane_args));
   1576 
   1577 			if (parse_plane(&plane_args[plane_count], optarg) < 0)
   1578 				usage(argv[0]);
   1579 
   1580 			plane_count++;
   1581 			break;
   1582 		case 'p':
   1583 			crtcs = 1;
   1584 			planes = 1;
   1585 			break;
   1586 		case 's':
   1587 			pipe_args = realloc(pipe_args,
   1588 					    (count + 1) * sizeof *pipe_args);
   1589 			if (pipe_args == NULL) {
   1590 				fprintf(stderr, "memory allocation failed\n");
   1591 				return 1;
   1592 			}
   1593 			memset(&pipe_args[count], 0, sizeof(*pipe_args));
   1594 
   1595 			if (parse_connector(&pipe_args[count], optarg) < 0)
   1596 				usage(argv[0]);
   1597 
   1598 			count++;
   1599 			break;
   1600 		case 'C':
   1601 			test_cursor = 1;
   1602 			break;
   1603 		case 'v':
   1604 			test_vsync = 1;
   1605 			break;
   1606 		case 'w':
   1607 			prop_args = realloc(prop_args,
   1608 					   (prop_count + 1) * sizeof *prop_args);
   1609 			if (prop_args == NULL) {
   1610 				fprintf(stderr, "memory allocation failed\n");
   1611 				return 1;
   1612 			}
   1613 			memset(&prop_args[prop_count], 0, sizeof(*prop_args));
   1614 
   1615 			if (parse_property(&prop_args[prop_count], optarg) < 0)
   1616 				usage(argv[0]);
   1617 
   1618 			prop_count++;
   1619 			break;
   1620 		default:
   1621 			usage(argv[0]);
   1622 			break;
   1623 		}
   1624 	}
   1625 
   1626 	if (!args)
   1627 		encoders = connectors = crtcs = planes = framebuffers = 1;
   1628 
   1629 	dev.fd = util_open(device, module);
   1630 	if (dev.fd < 0)
   1631 		return -1;
   1632 
   1633 	if (test_vsync && !page_flipping_supported()) {
   1634 		fprintf(stderr, "page flipping not supported by drm.\n");
   1635 		return -1;
   1636 	}
   1637 
   1638 	if (test_vsync && !count) {
   1639 		fprintf(stderr, "page flipping requires at least one -s option.\n");
   1640 		return -1;
   1641 	}
   1642 
   1643 	if (test_cursor && !cursor_supported()) {
   1644 		fprintf(stderr, "hw cursor not supported by drm.\n");
   1645 		return -1;
   1646 	}
   1647 
   1648 	dev.resources = get_resources(&dev);
   1649 	if (!dev.resources) {
   1650 		drmClose(dev.fd);
   1651 		return 1;
   1652 	}
   1653 
   1654 	for (i = 0; i < count; i++) {
   1655 		if (pipe_resolve_connectors(&dev, &pipe_args[i]) < 0) {
   1656 			free_resources(dev.resources);
   1657 			drmClose(dev.fd);
   1658 			return 1;
   1659 		}
   1660 	}
   1661 
   1662 #define dump_resource(dev, res) if (res) dump_##res(dev)
   1663 
   1664 	dump_resource(&dev, encoders);
   1665 	dump_resource(&dev, connectors);
   1666 	dump_resource(&dev, crtcs);
   1667 	dump_resource(&dev, planes);
   1668 	dump_resource(&dev, framebuffers);
   1669 
   1670 	for (i = 0; i < prop_count; ++i)
   1671 		set_property(&dev, &prop_args[i]);
   1672 
   1673 	if (count || plane_count) {
   1674 		uint64_t cap = 0;
   1675 
   1676 		ret = drmGetCap(dev.fd, DRM_CAP_DUMB_BUFFER, &cap);
   1677 		if (ret || cap == 0) {
   1678 			fprintf(stderr, "driver doesn't support the dumb buffer API\n");
   1679 			return 1;
   1680 		}
   1681 
   1682 		if (count)
   1683 			set_mode(&dev, pipe_args, count);
   1684 
   1685 		if (plane_count)
   1686 			set_planes(&dev, plane_args, plane_count);
   1687 
   1688 		if (test_cursor)
   1689 			set_cursors(&dev, pipe_args, count);
   1690 
   1691 		if (test_vsync)
   1692 			test_page_flip(&dev, pipe_args, count);
   1693 
   1694 		if (drop_master)
   1695 			drmDropMaster(dev.fd);
   1696 
   1697 		getchar();
   1698 
   1699 		if (test_cursor)
   1700 			clear_cursors(&dev);
   1701 
   1702 		if (plane_count)
   1703 			clear_planes(&dev, plane_args, plane_count);
   1704 
   1705 		if (count)
   1706 			clear_mode(&dev);
   1707 	}
   1708 
   1709 	free_resources(dev.resources);
   1710 
   1711 	return 0;
   1712 }
   1713