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