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