modetest: switch usage to proper options grammar
[drm/libdrm.git] / tests / modetest / modetest.c
blobd9e761e6cfa001b5fb3d4bb2932dc76ffed917a5
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>
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:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
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.
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.
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.
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>
59 #include "xf86drm.h"
60 #include "xf86drmMode.h"
61 #include "drm_fourcc.h"
63 #include "util/common.h"
64 #include "util/format.h"
65 #include "util/kms.h"
66 #include "util/pattern.h"
68 #include "buffers.h"
69 #include "cursor.h"
71 static enum util_fill_pattern primary_fill = UTIL_PATTERN_SMPTE;
72 static enum util_fill_pattern secondary_fill = UTIL_PATTERN_TILES;
73 static drmModeModeInfo user_mode;
75 struct crtc {
76 drmModeCrtc *crtc;
77 drmModeObjectProperties *props;
78 drmModePropertyRes **props_info;
79 drmModeModeInfo *mode;
82 struct encoder {
83 drmModeEncoder *encoder;
86 struct connector {
87 drmModeConnector *connector;
88 drmModeObjectProperties *props;
89 drmModePropertyRes **props_info;
90 char *name;
93 struct fb {
94 drmModeFB *fb;
97 struct plane {
98 drmModePlane *plane;
99 drmModeObjectProperties *props;
100 drmModePropertyRes **props_info;
103 struct resources {
104 struct crtc *crtcs;
105 int count_crtcs;
106 struct encoder *encoders;
107 int count_encoders;
108 struct connector *connectors;
109 int count_connectors;
110 struct fb *fbs;
111 int count_fbs;
112 struct plane *planes;
113 uint32_t count_planes;
116 struct device {
117 int fd;
119 struct resources *resources;
121 struct {
122 unsigned int width;
123 unsigned int height;
125 unsigned int fb_id;
126 struct bo *bo;
127 struct bo *cursor_bo;
128 } mode;
130 int use_atomic;
131 drmModeAtomicReq *req;
132 int32_t writeback_fence_fd;
135 static inline int64_t U642I64(uint64_t val)
137 return (int64_t)*((int64_t *)&val);
140 static float mode_vrefresh(drmModeModeInfo *mode)
142 unsigned int num, den;
144 num = mode->clock;
145 den = mode->htotal * mode->vtotal;
147 if (mode->flags & DRM_MODE_FLAG_INTERLACE)
148 num *= 2;
149 if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
150 den *= 2;
151 if (mode->vscan > 1)
152 den *= mode->vscan;
154 return num * 1000.00 / den;
157 #define bit_name_fn(res) \
158 const char * res##_str(int type) { \
159 unsigned int i; \
160 const char *sep = ""; \
161 for (i = 0; i < ARRAY_SIZE(res##_names); i++) { \
162 if (type & (1 << i)) { \
163 printf("%s%s", sep, res##_names[i]); \
164 sep = ", "; \
167 return NULL; \
170 static const char *mode_type_names[] = {
171 "builtin",
172 "clock_c",
173 "crtc_c",
174 "preferred",
175 "default",
176 "userdef",
177 "driver",
180 static bit_name_fn(mode_type)
182 static const char *mode_flag_names[] = {
183 "phsync",
184 "nhsync",
185 "pvsync",
186 "nvsync",
187 "interlace",
188 "dblscan",
189 "csync",
190 "pcsync",
191 "ncsync",
192 "hskew",
193 "bcast",
194 "pixmux",
195 "dblclk",
196 "clkdiv2"
199 static bit_name_fn(mode_flag)
201 static void dump_fourcc(uint32_t fourcc)
203 char *name = drmGetFormatName(fourcc);
204 printf(" %s", name);
205 free(name);
208 static void dump_encoders(struct device *dev)
210 drmModeEncoder *encoder;
211 int i;
213 printf("Encoders:\n");
214 printf("id\tcrtc\ttype\tpossible crtcs\tpossible clones\t\n");
215 for (i = 0; i < dev->resources->count_encoders; i++) {
216 encoder = dev->resources->encoders[i].encoder;
217 if (!encoder)
218 continue;
220 printf("%d\t%d\t%s\t0x%08x\t0x%08x\n",
221 encoder->encoder_id,
222 encoder->crtc_id,
223 util_lookup_encoder_type_name(encoder->encoder_type),
224 encoder->possible_crtcs,
225 encoder->possible_clones);
227 printf("\n");
230 static void dump_mode(drmModeModeInfo *mode, int index)
232 printf(" #%i %s %.2f %d %d %d %d %d %d %d %d %d",
233 index,
234 mode->name,
235 mode_vrefresh(mode),
236 mode->hdisplay,
237 mode->hsync_start,
238 mode->hsync_end,
239 mode->htotal,
240 mode->vdisplay,
241 mode->vsync_start,
242 mode->vsync_end,
243 mode->vtotal,
244 mode->clock);
246 printf(" flags: ");
247 mode_flag_str(mode->flags);
248 printf("; type: ");
249 mode_type_str(mode->type);
250 printf("\n");
253 static void dump_blob(struct device *dev, uint32_t blob_id)
255 uint32_t i;
256 unsigned char *blob_data;
257 drmModePropertyBlobPtr blob;
259 blob = drmModeGetPropertyBlob(dev->fd, blob_id);
260 if (!blob) {
261 printf("\n");
262 return;
265 blob_data = blob->data;
267 for (i = 0; i < blob->length; i++) {
268 if (i % 16 == 0)
269 printf("\n\t\t\t");
270 printf("%.2hhx", blob_data[i]);
272 printf("\n");
274 drmModeFreePropertyBlob(blob);
277 static const char *modifier_to_string(uint64_t modifier)
279 static char mod_string[4096];
281 char *modifier_name = drmGetFormatModifierName(modifier);
282 char *vendor_name = drmGetFormatModifierVendor(modifier);
283 memset(mod_string, 0x00, sizeof(mod_string));
285 if (!modifier_name) {
286 if (vendor_name)
287 snprintf(mod_string, sizeof(mod_string), "%s_%s",
288 vendor_name, "UNKNOWN_MODIFIER");
289 else
290 snprintf(mod_string, sizeof(mod_string), "%s_%s",
291 "UNKNOWN_VENDOR", "UNKNOWN_MODIFIER");
292 /* safe, as free is no-op for NULL */
293 free(vendor_name);
294 return mod_string;
297 if (modifier == DRM_FORMAT_MOD_LINEAR) {
298 snprintf(mod_string, sizeof(mod_string), "%s", modifier_name);
299 free(modifier_name);
300 free(vendor_name);
301 return mod_string;
304 snprintf(mod_string, sizeof(mod_string), "%s_%s",
305 vendor_name, modifier_name);
307 free(modifier_name);
308 free(vendor_name);
309 return mod_string;
312 static void dump_in_formats(struct device *dev, uint32_t blob_id)
314 drmModeFormatModifierIterator iter = {0};
315 drmModePropertyBlobPtr blob;
316 uint32_t fmt = 0;
318 printf("\t\tin_formats blob decoded:\n");
319 blob = drmModeGetPropertyBlob(dev->fd, blob_id);
320 if (!blob) {
321 printf("\n");
322 return;
325 while (drmModeFormatModifierBlobIterNext(blob, &iter)) {
326 if (!fmt || fmt != iter.fmt) {
327 printf("%s\t\t\t", !fmt ? "" : "\n");
328 fmt = iter.fmt;
329 dump_fourcc(fmt);
330 printf(": ");
333 printf(" %s(0x%"PRIx64")", modifier_to_string(iter.mod), iter.mod);
336 printf("\n");
338 drmModeFreePropertyBlob(blob);
341 static void dump_prop(struct device *dev, drmModePropertyPtr prop,
342 uint32_t prop_id, uint64_t value)
344 int i;
345 printf("\t%d", prop_id);
346 if (!prop) {
347 printf("\n");
348 return;
351 printf(" %s:\n", prop->name);
353 printf("\t\tflags:");
354 if (prop->flags & DRM_MODE_PROP_PENDING)
355 printf(" pending");
356 if (prop->flags & DRM_MODE_PROP_IMMUTABLE)
357 printf(" immutable");
358 if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE))
359 printf(" signed range");
360 if (drm_property_type_is(prop, DRM_MODE_PROP_RANGE))
361 printf(" range");
362 if (drm_property_type_is(prop, DRM_MODE_PROP_ENUM))
363 printf(" enum");
364 if (drm_property_type_is(prop, DRM_MODE_PROP_BITMASK))
365 printf(" bitmask");
366 if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB))
367 printf(" blob");
368 if (drm_property_type_is(prop, DRM_MODE_PROP_OBJECT))
369 printf(" object");
370 printf("\n");
372 if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE)) {
373 printf("\t\tvalues:");
374 for (i = 0; i < prop->count_values; i++)
375 printf(" %"PRId64, U642I64(prop->values[i]));
376 printf("\n");
379 if (drm_property_type_is(prop, DRM_MODE_PROP_RANGE)) {
380 printf("\t\tvalues:");
381 for (i = 0; i < prop->count_values; i++)
382 printf(" %"PRIu64, prop->values[i]);
383 printf("\n");
386 if (drm_property_type_is(prop, DRM_MODE_PROP_ENUM)) {
387 printf("\t\tenums:");
388 for (i = 0; i < prop->count_enums; i++)
389 printf(" %s=%"PRIu64, prop->enums[i].name,
390 (uint64_t)prop->enums[i].value);
391 printf("\n");
392 } else if (drm_property_type_is(prop, DRM_MODE_PROP_BITMASK)) {
393 printf("\t\tvalues:");
394 for (i = 0; i < prop->count_enums; i++)
395 printf(" %s=0x%llx", prop->enums[i].name,
396 (1LL << prop->enums[i].value));
397 printf("\n");
398 } else {
399 assert(prop->count_enums == 0);
402 if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB)) {
403 printf("\t\tblobs:\n");
404 for (i = 0; i < prop->count_blobs; i++)
405 dump_blob(dev, prop->blob_ids[i]);
406 printf("\n");
407 } else {
408 assert(prop->count_blobs == 0);
411 printf("\t\tvalue:");
412 if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB))
413 dump_blob(dev, value);
414 else if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE))
415 printf(" %"PRId64"\n", value);
416 else
417 printf(" %"PRIu64"\n", value);
419 if (strcmp(prop->name, "IN_FORMATS") == 0)
420 dump_in_formats(dev, value);
423 static void dump_connectors(struct device *dev)
425 int i, j;
427 printf("Connectors:\n");
428 printf("id\tencoder\tstatus\t\tname\t\tsize (mm)\tmodes\tencoders\n");
429 for (i = 0; i < dev->resources->count_connectors; i++) {
430 struct connector *_connector = &dev->resources->connectors[i];
431 drmModeConnector *connector = _connector->connector;
432 if (!connector)
433 continue;
435 printf("%d\t%d\t%s\t%-15s\t%dx%d\t\t%d\t",
436 connector->connector_id,
437 connector->encoder_id,
438 util_lookup_connector_status_name(connector->connection),
439 _connector->name,
440 connector->mmWidth, connector->mmHeight,
441 connector->count_modes);
443 for (j = 0; j < connector->count_encoders; j++)
444 printf("%s%d", j > 0 ? ", " : "", connector->encoders[j]);
445 printf("\n");
447 if (connector->count_modes) {
448 printf(" modes:\n");
449 printf("\tindex name refresh (Hz) hdisp hss hse htot vdisp "
450 "vss vse vtot\n");
451 for (j = 0; j < connector->count_modes; j++)
452 dump_mode(&connector->modes[j], j);
455 if (_connector->props) {
456 printf(" props:\n");
457 for (j = 0; j < (int)_connector->props->count_props; j++)
458 dump_prop(dev, _connector->props_info[j],
459 _connector->props->props[j],
460 _connector->props->prop_values[j]);
463 printf("\n");
466 static void dump_crtcs(struct device *dev)
468 int i;
469 uint32_t j;
471 printf("CRTCs:\n");
472 printf("id\tfb\tpos\tsize\n");
473 for (i = 0; i < dev->resources->count_crtcs; i++) {
474 struct crtc *_crtc = &dev->resources->crtcs[i];
475 drmModeCrtc *crtc = _crtc->crtc;
476 if (!crtc)
477 continue;
479 printf("%d\t%d\t(%d,%d)\t(%dx%d)\n",
480 crtc->crtc_id,
481 crtc->buffer_id,
482 crtc->x, crtc->y,
483 crtc->width, crtc->height);
484 dump_mode(&crtc->mode, 0);
486 if (_crtc->props) {
487 printf(" props:\n");
488 for (j = 0; j < _crtc->props->count_props; j++)
489 dump_prop(dev, _crtc->props_info[j],
490 _crtc->props->props[j],
491 _crtc->props->prop_values[j]);
492 } else {
493 printf(" no properties found\n");
496 printf("\n");
499 static void dump_framebuffers(struct device *dev)
501 drmModeFB *fb;
502 int i;
504 printf("Frame buffers:\n");
505 printf("id\tsize\tpitch\n");
506 for (i = 0; i < dev->resources->count_fbs; i++) {
507 fb = dev->resources->fbs[i].fb;
508 if (!fb)
509 continue;
511 printf("%u\t(%ux%u)\t%u\n",
512 fb->fb_id,
513 fb->width, fb->height,
514 fb->pitch);
516 printf("\n");
519 static void dump_planes(struct device *dev)
521 unsigned int i, j;
523 printf("Planes:\n");
524 printf("id\tcrtc\tfb\tCRTC x,y\tx,y\tgamma size\tpossible crtcs\n");
526 for (i = 0; i < dev->resources->count_planes; i++) {
527 struct plane *plane = &dev->resources->planes[i];
528 drmModePlane *ovr = plane->plane;
529 if (!ovr)
530 continue;
532 printf("%d\t%d\t%d\t%d,%d\t\t%d,%d\t%-8d\t0x%08x\n",
533 ovr->plane_id, ovr->crtc_id, ovr->fb_id,
534 ovr->crtc_x, ovr->crtc_y, ovr->x, ovr->y,
535 ovr->gamma_size, ovr->possible_crtcs);
537 if (!ovr->count_formats)
538 continue;
540 printf(" formats:");
541 for (j = 0; j < ovr->count_formats; j++)
542 dump_fourcc(ovr->formats[j]);
543 printf("\n");
545 if (plane->props) {
546 printf(" props:\n");
547 for (j = 0; j < plane->props->count_props; j++)
548 dump_prop(dev, plane->props_info[j],
549 plane->props->props[j],
550 plane->props->prop_values[j]);
551 } else {
552 printf(" no properties found\n");
555 printf("\n");
557 return;
560 static void free_resources(struct resources *res)
562 int i;
564 if (!res)
565 return;
567 #define free_resource(_res, type, Type) \
568 do { \
569 if (!(_res)->type##s) \
570 break; \
571 for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \
572 if (!(_res)->type##s[i].type) \
573 break; \
574 drmModeFree##Type((_res)->type##s[i].type); \
576 free((_res)->type##s); \
577 } while (0)
579 #define free_properties(_res, type) \
580 do { \
581 for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \
582 unsigned int j; \
583 for (j = 0; j < res->type##s[i].props->count_props; ++j)\
584 drmModeFreeProperty(res->type##s[i].props_info[j]);\
585 free(res->type##s[i].props_info); \
586 drmModeFreeObjectProperties(res->type##s[i].props); \
588 } while (0)
590 free_properties(res, plane);
591 free_resource(res, plane, Plane);
593 free_properties(res, connector);
594 free_properties(res, crtc);
596 for (i = 0; i < res->count_connectors; i++)
597 free(res->connectors[i].name);
599 free_resource(res, fb, FB);
600 free_resource(res, connector, Connector);
601 free_resource(res, encoder, Encoder);
602 free_resource(res, crtc, Crtc);
604 free(res);
607 static struct resources *get_resources(struct device *dev)
609 drmModeRes *_res;
610 drmModePlaneRes *plane_res;
611 struct resources *res;
612 int i;
614 res = calloc(1, sizeof(*res));
615 if (res == 0)
616 return NULL;
618 drmSetClientCap(dev->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
620 _res = drmModeGetResources(dev->fd);
621 if (!_res) {
622 fprintf(stderr, "drmModeGetResources failed: %s\n",
623 strerror(errno));
624 free(res);
625 return NULL;
628 res->count_crtcs = _res->count_crtcs;
629 res->count_encoders = _res->count_encoders;
630 res->count_connectors = _res->count_connectors;
631 res->count_fbs = _res->count_fbs;
633 res->crtcs = calloc(res->count_crtcs, sizeof(*res->crtcs));
634 res->encoders = calloc(res->count_encoders, sizeof(*res->encoders));
635 res->connectors = calloc(res->count_connectors, sizeof(*res->connectors));
636 res->fbs = calloc(res->count_fbs, sizeof(*res->fbs));
638 if (!res->crtcs || !res->encoders || !res->connectors || !res->fbs) {
639 drmModeFreeResources(_res);
640 goto error;
643 #define get_resource(_res, __res, type, Type) \
644 do { \
645 for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \
646 uint32_t type##id = (__res)->type##s[i]; \
647 (_res)->type##s[i].type = \
648 drmModeGet##Type(dev->fd, type##id); \
649 if (!(_res)->type##s[i].type) \
650 fprintf(stderr, "could not get %s %i: %s\n", \
651 #type, type##id, \
652 strerror(errno)); \
654 } while (0)
656 get_resource(res, _res, crtc, Crtc);
657 get_resource(res, _res, encoder, Encoder);
658 get_resource(res, _res, connector, Connector);
659 get_resource(res, _res, fb, FB);
661 drmModeFreeResources(_res);
663 /* Set the name of all connectors based on the type name and the per-type ID. */
664 for (i = 0; i < res->count_connectors; i++) {
665 struct connector *connector = &res->connectors[i];
666 drmModeConnector *conn = connector->connector;
667 int num;
669 num = asprintf(&connector->name, "%s-%u",
670 drmModeGetConnectorTypeName(conn->connector_type),
671 conn->connector_type_id);
672 if (num < 0)
673 goto error;
676 #define get_properties(_res, type, Type) \
677 do { \
678 for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \
679 struct type *obj = &res->type##s[i]; \
680 unsigned int j; \
681 obj->props = \
682 drmModeObjectGetProperties(dev->fd, obj->type->type##_id, \
683 DRM_MODE_OBJECT_##Type); \
684 if (!obj->props) { \
685 fprintf(stderr, \
686 "could not get %s %i properties: %s\n", \
687 #type, obj->type->type##_id, \
688 strerror(errno)); \
689 continue; \
691 obj->props_info = calloc(obj->props->count_props, \
692 sizeof(*obj->props_info)); \
693 if (!obj->props_info) \
694 continue; \
695 for (j = 0; j < obj->props->count_props; ++j) \
696 obj->props_info[j] = \
697 drmModeGetProperty(dev->fd, obj->props->props[j]); \
699 } while (0)
701 get_properties(res, crtc, CRTC);
702 get_properties(res, connector, CONNECTOR);
704 for (i = 0; i < res->count_crtcs; ++i)
705 res->crtcs[i].mode = &res->crtcs[i].crtc->mode;
707 plane_res = drmModeGetPlaneResources(dev->fd);
708 if (!plane_res) {
709 fprintf(stderr, "drmModeGetPlaneResources failed: %s\n",
710 strerror(errno));
711 return res;
714 res->count_planes = plane_res->count_planes;
716 res->planes = calloc(res->count_planes, sizeof(*res->planes));
717 if (!res->planes) {
718 drmModeFreePlaneResources(plane_res);
719 goto error;
722 get_resource(res, plane_res, plane, Plane);
723 drmModeFreePlaneResources(plane_res);
724 get_properties(res, plane, PLANE);
726 return res;
728 error:
729 free_resources(res);
730 return NULL;
733 static struct crtc *get_crtc_by_id(struct device *dev, uint32_t id)
735 int i;
737 for (i = 0; i < dev->resources->count_crtcs; ++i) {
738 drmModeCrtc *crtc = dev->resources->crtcs[i].crtc;
739 if (crtc && crtc->crtc_id == id)
740 return &dev->resources->crtcs[i];
743 return NULL;
746 static uint32_t get_crtc_mask(struct device *dev, struct crtc *crtc)
748 unsigned int i;
750 for (i = 0; i < (unsigned int)dev->resources->count_crtcs; i++) {
751 if (crtc->crtc->crtc_id == dev->resources->crtcs[i].crtc->crtc_id)
752 return 1 << i;
754 /* Unreachable: crtc->crtc is one of resources->crtcs[] */
755 /* Don't return zero or static analysers will complain */
756 abort();
757 return 0;
760 static drmModeConnector *get_connector_by_name(struct device *dev, const char *name)
762 struct connector *connector;
763 int i;
765 for (i = 0; i < dev->resources->count_connectors; i++) {
766 connector = &dev->resources->connectors[i];
768 if (strcmp(connector->name, name) == 0)
769 return connector->connector;
772 return NULL;
775 static drmModeConnector *get_connector_by_id(struct device *dev, uint32_t id)
777 drmModeConnector *connector;
778 int i;
780 for (i = 0; i < dev->resources->count_connectors; i++) {
781 connector = dev->resources->connectors[i].connector;
782 if (connector && connector->connector_id == id)
783 return connector;
786 return NULL;
789 static drmModeEncoder *get_encoder_by_id(struct device *dev, uint32_t id)
791 drmModeEncoder *encoder;
792 int i;
794 for (i = 0; i < dev->resources->count_encoders; i++) {
795 encoder = dev->resources->encoders[i].encoder;
796 if (encoder && encoder->encoder_id == id)
797 return encoder;
800 return NULL;
803 /* -----------------------------------------------------------------------------
804 * Pipes and planes
808 * Mode setting with the kernel interfaces is a bit of a chore.
809 * First you have to find the connector in question and make sure the
810 * requested mode is available.
811 * Then you need to find the encoder attached to that connector so you
812 * can bind it with a free crtc.
814 struct pipe_arg {
815 const char **cons;
816 uint32_t *con_ids;
817 unsigned int num_cons;
818 uint32_t crtc_id;
819 char mode_str[64];
820 char format_str[8]; /* need to leave room for "_BE" and terminating \0 */
821 float vrefresh;
822 unsigned int fourcc;
823 drmModeModeInfo *mode;
824 struct crtc *crtc;
825 unsigned int fb_id[2], current_fb_id;
826 struct timeval start;
827 unsigned int out_fb_id;
828 struct bo *out_bo;
830 int swap_count;
833 struct plane_arg {
834 uint32_t plane_id; /* the id of plane to use */
835 uint32_t crtc_id; /* the id of CRTC to bind to */
836 bool has_position;
837 int32_t x, y;
838 uint32_t w, h;
839 double scale;
840 unsigned int fb_id;
841 unsigned int old_fb_id;
842 struct bo *bo;
843 struct bo *old_bo;
844 char format_str[8]; /* need to leave room for "_BE" and terminating \0 */
845 unsigned int fourcc;
848 static drmModeModeInfo *
849 connector_find_mode(struct device *dev, uint32_t con_id, const char *mode_str,
850 const float vrefresh)
852 drmModeConnector *connector;
853 drmModeModeInfo *mode;
854 int i;
856 connector = get_connector_by_id(dev, con_id);
857 if (!connector)
858 return NULL;
860 if (strchr(mode_str, ',')) {
861 i = sscanf(mode_str, "%hu,%hu,%hu,%hu,%hu,%hu,%hu,%hu",
862 &user_mode.hdisplay, &user_mode.hsync_start,
863 &user_mode.hsync_end, &user_mode.htotal,
864 &user_mode.vdisplay, &user_mode.vsync_start,
865 &user_mode.vsync_end, &user_mode.vtotal);
866 if (i == 8) {
867 user_mode.clock = roundf(user_mode.htotal * user_mode.vtotal * vrefresh / 1000);
868 user_mode.vrefresh = roundf(vrefresh);
869 snprintf(user_mode.name, sizeof(user_mode.name), "custom%dx%d", user_mode.hdisplay, user_mode.vdisplay);
871 return &user_mode;
875 if (!connector->count_modes)
876 return NULL;
878 /* Pick by Index */
879 if (mode_str[0] == '#') {
880 int index = atoi(mode_str + 1);
882 if (index >= connector->count_modes || index < 0)
883 return NULL;
884 return &connector->modes[index];
887 /* Pick by Name */
888 for (i = 0; i < connector->count_modes; i++) {
889 mode = &connector->modes[i];
890 if (!strcmp(mode->name, mode_str)) {
891 /* If the vertical refresh frequency is not specified
892 * then return the first mode that match with the name.
893 * Else, return the mode that match the name and
894 * the specified vertical refresh frequency.
896 if (vrefresh == 0)
897 return mode;
898 else if (fabs(mode_vrefresh(mode) - vrefresh) < 0.005)
899 return mode;
903 return NULL;
906 static struct crtc *pipe_find_crtc(struct device *dev, struct pipe_arg *pipe)
908 uint32_t possible_crtcs = ~0;
909 uint32_t active_crtcs = 0;
910 unsigned int crtc_idx;
911 unsigned int i;
912 int j;
914 for (i = 0; i < pipe->num_cons; ++i) {
915 uint32_t crtcs_for_connector = 0;
916 drmModeConnector *connector;
917 drmModeEncoder *encoder;
918 struct crtc *crtc;
920 connector = get_connector_by_id(dev, pipe->con_ids[i]);
921 if (!connector)
922 return NULL;
924 for (j = 0; j < connector->count_encoders; ++j) {
925 encoder = get_encoder_by_id(dev, connector->encoders[j]);
926 if (!encoder)
927 continue;
929 crtcs_for_connector |= encoder->possible_crtcs;
930 crtc = get_crtc_by_id(dev, encoder->crtc_id);
931 if (!crtc)
932 continue;
933 active_crtcs |= get_crtc_mask(dev, crtc);
936 possible_crtcs &= crtcs_for_connector;
939 if (!possible_crtcs)
940 return NULL;
942 /* Return the first possible and active CRTC if one exists, or the first
943 * possible CRTC otherwise.
945 if (possible_crtcs & active_crtcs)
946 crtc_idx = ffs(possible_crtcs & active_crtcs);
947 else
948 crtc_idx = ffs(possible_crtcs);
950 return &dev->resources->crtcs[crtc_idx - 1];
953 static int pipe_find_crtc_and_mode(struct device *dev, struct pipe_arg *pipe)
955 drmModeModeInfo *mode = NULL;
956 int i;
958 pipe->mode = NULL;
960 for (i = 0; i < (int)pipe->num_cons; i++) {
961 mode = connector_find_mode(dev, pipe->con_ids[i],
962 pipe->mode_str, pipe->vrefresh);
963 if (mode == NULL) {
964 if (pipe->vrefresh)
965 fprintf(stderr,
966 "failed to find mode "
967 "\"%s-%.2fHz\" for connector %s\n",
968 pipe->mode_str, pipe->vrefresh, pipe->cons[i]);
969 else
970 fprintf(stderr,
971 "failed to find mode \"%s\" for connector %s\n",
972 pipe->mode_str, pipe->cons[i]);
973 return -EINVAL;
977 /* If the CRTC ID was specified, get the corresponding CRTC. Otherwise
978 * locate a CRTC that can be attached to all the connectors.
980 if (pipe->crtc_id != (uint32_t)-1) {
981 pipe->crtc = get_crtc_by_id(dev, pipe->crtc_id);
982 } else {
983 pipe->crtc = pipe_find_crtc(dev, pipe);
984 pipe->crtc_id = pipe->crtc->crtc->crtc_id;
987 if (!pipe->crtc) {
988 fprintf(stderr, "failed to find CRTC for pipe\n");
989 return -EINVAL;
992 pipe->mode = mode;
993 pipe->crtc->mode = mode;
995 return 0;
998 /* -----------------------------------------------------------------------------
999 * Properties
1002 struct property_arg {
1003 uint32_t obj_id;
1004 uint32_t obj_type;
1005 char name[DRM_PROP_NAME_LEN+1];
1006 uint32_t prop_id;
1007 uint64_t value;
1008 bool optional;
1011 static bool set_property(struct device *dev, struct property_arg *p)
1013 drmModeObjectProperties *props = NULL;
1014 drmModePropertyRes **props_info = NULL;
1015 const char *obj_type;
1016 int ret;
1017 int i;
1019 p->obj_type = 0;
1020 p->prop_id = 0;
1022 #define find_object(_res, type, Type) \
1023 do { \
1024 for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \
1025 struct type *obj = &(_res)->type##s[i]; \
1026 if (obj->type->type##_id != p->obj_id) \
1027 continue; \
1028 p->obj_type = DRM_MODE_OBJECT_##Type; \
1029 obj_type = #Type; \
1030 props = obj->props; \
1031 props_info = obj->props_info; \
1033 } while(0) \
1035 find_object(dev->resources, crtc, CRTC);
1036 if (p->obj_type == 0)
1037 find_object(dev->resources, connector, CONNECTOR);
1038 if (p->obj_type == 0)
1039 find_object(dev->resources, plane, PLANE);
1040 if (p->obj_type == 0) {
1041 fprintf(stderr, "Object %i not found, can't set property\n",
1042 p->obj_id);
1043 return false;
1046 if (!props) {
1047 fprintf(stderr, "%s %i has no properties\n",
1048 obj_type, p->obj_id);
1049 return false;
1052 for (i = 0; i < (int)props->count_props; ++i) {
1053 if (!props_info[i])
1054 continue;
1055 if (strcmp(props_info[i]->name, p->name) == 0)
1056 break;
1059 if (i == (int)props->count_props) {
1060 if (!p->optional)
1061 fprintf(stderr, "%s %i has no %s property\n",
1062 obj_type, p->obj_id, p->name);
1063 return false;
1066 p->prop_id = props->props[i];
1068 if (!dev->use_atomic)
1069 ret = drmModeObjectSetProperty(dev->fd, p->obj_id, p->obj_type,
1070 p->prop_id, p->value);
1071 else
1072 ret = drmModeAtomicAddProperty(dev->req, p->obj_id, p->prop_id, p->value);
1074 if (ret < 0)
1075 fprintf(stderr, "failed to set %s %i property %s to %" PRIu64 ": %s\n",
1076 obj_type, p->obj_id, p->name, p->value, strerror(-ret));
1078 return true;
1081 /* -------------------------------------------------------------------------- */
1083 static void
1084 page_flip_handler(int fd, unsigned int frame,
1085 unsigned int sec, unsigned int usec, void *data)
1087 struct pipe_arg *pipe;
1088 unsigned int new_fb_id;
1089 struct timeval end;
1090 double t;
1092 pipe = data;
1093 if (pipe->current_fb_id == pipe->fb_id[0])
1094 new_fb_id = pipe->fb_id[1];
1095 else
1096 new_fb_id = pipe->fb_id[0];
1098 drmModePageFlip(fd, pipe->crtc_id, new_fb_id,
1099 DRM_MODE_PAGE_FLIP_EVENT, pipe);
1100 pipe->current_fb_id = new_fb_id;
1101 pipe->swap_count++;
1102 if (pipe->swap_count == 60) {
1103 gettimeofday(&end, NULL);
1104 t = end.tv_sec + end.tv_usec * 1e-6 -
1105 (pipe->start.tv_sec + pipe->start.tv_usec * 1e-6);
1106 fprintf(stderr, "freq: %.02fHz\n", pipe->swap_count / t);
1107 pipe->swap_count = 0;
1108 pipe->start = end;
1112 static bool format_support(const drmModePlanePtr ovr, uint32_t fmt)
1114 unsigned int i;
1116 for (i = 0; i < ovr->count_formats; ++i) {
1117 if (ovr->formats[i] == fmt)
1118 return true;
1121 return false;
1124 static void add_property(struct device *dev, uint32_t obj_id,
1125 const char *name, uint64_t value)
1127 struct property_arg p;
1129 p.obj_id = obj_id;
1130 strcpy(p.name, name);
1131 p.value = value;
1133 set_property(dev, &p);
1136 static bool add_property_optional(struct device *dev, uint32_t obj_id,
1137 const char *name, uint64_t value)
1139 struct property_arg p;
1141 p.obj_id = obj_id;
1142 strcpy(p.name, name);
1143 p.value = value;
1144 p.optional = true;
1146 return set_property(dev, &p);
1149 static void set_gamma(struct device *dev, unsigned crtc_id, unsigned fourcc)
1151 unsigned blob_id = 0;
1152 const struct util_format_info *info;
1153 /* TODO: support 1024-sized LUTs, when the use-case arises */
1154 struct drm_color_lut gamma_lut[256];
1155 int i, ret;
1157 info = util_format_info_find(fourcc);
1158 if (info->ncolors) {
1159 memset(gamma_lut, 0, sizeof(gamma_lut));
1160 /* TODO: Add index support for more patterns */
1161 util_smpte_fill_lut(info->ncolors, gamma_lut);
1162 drmModeCreatePropertyBlob(dev->fd, gamma_lut, sizeof(gamma_lut), &blob_id);
1163 } else {
1165 * Initialize gamma_lut to a linear table for the legacy API below.
1166 * The modern property API resets to a linear/pass-thru table if blob_id
1167 * is 0, hence no PropertyBlob is created here.
1169 for (i = 0; i < 256; i++) {
1170 gamma_lut[i].red =
1171 gamma_lut[i].green =
1172 gamma_lut[i].blue = i << 8;
1176 add_property_optional(dev, crtc_id, "DEGAMMA_LUT", 0);
1177 add_property_optional(dev, crtc_id, "CTM", 0);
1178 if (!add_property_optional(dev, crtc_id, "GAMMA_LUT", blob_id)) {
1179 /* If we can't add the GAMMA_LUT property, try the legacy API. */
1180 uint16_t r[256], g[256], b[256];
1182 for (i = 0; i < 256; i++) {
1183 r[i] = gamma_lut[i].red;
1184 g[i] = gamma_lut[i].green;
1185 b[i] = gamma_lut[i].blue;
1188 ret = drmModeCrtcSetGamma(dev->fd, crtc_id, 256, r, g, b);
1189 if (ret && errno != ENOSYS)
1190 fprintf(stderr, "failed to set gamma: %s\n", strerror(errno));
1194 static int
1195 bo_fb_create(int fd, unsigned int fourcc, const uint32_t w, const uint32_t h,
1196 enum util_fill_pattern pat, struct bo **out_bo, unsigned int *out_fb_id)
1198 uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
1199 struct bo *bo;
1200 unsigned int fb_id;
1202 bo = bo_create(fd, fourcc, w, h, handles, pitches, offsets, pat);
1204 if (bo == NULL)
1205 return -1;
1207 if (drmModeAddFB2(fd, w, h, fourcc, handles, pitches, offsets, &fb_id, 0)) {
1208 fprintf(stderr, "failed to add fb (%ux%u): %s\n", w, h, strerror(errno));
1209 bo_destroy(bo);
1210 return -1;
1212 *out_bo = bo;
1213 *out_fb_id = fb_id;
1214 return 0;
1217 static int atomic_set_plane(struct device *dev, struct plane_arg *p,
1218 int pattern, bool update)
1220 struct bo *plane_bo;
1221 int crtc_x, crtc_y, crtc_w, crtc_h;
1222 struct crtc *crtc = NULL;
1223 unsigned int old_fb_id;
1225 /* Find an unused plane which can be connected to our CRTC. Find the
1226 * CRTC index first, then iterate over available planes.
1228 crtc = get_crtc_by_id(dev, p->crtc_id);
1229 if (!crtc) {
1230 fprintf(stderr, "CRTC %u not found\n", p->crtc_id);
1231 return -1;
1234 if (!update)
1235 fprintf(stderr, "testing %dx%d@%s on plane %u, crtc %u\n",
1236 p->w, p->h, p->format_str, p->plane_id, p->crtc_id);
1238 plane_bo = p->old_bo;
1239 p->old_bo = p->bo;
1241 if (!plane_bo) {
1242 if (bo_fb_create(dev->fd, p->fourcc, p->w, p->h,
1243 pattern, &plane_bo, &p->fb_id))
1244 return -1;
1247 p->bo = plane_bo;
1249 old_fb_id = p->fb_id;
1250 p->old_fb_id = old_fb_id;
1252 crtc_w = p->w * p->scale;
1253 crtc_h = p->h * p->scale;
1254 if (!p->has_position) {
1255 /* Default to the middle of the screen */
1256 crtc_x = (crtc->mode->hdisplay - crtc_w) / 2;
1257 crtc_y = (crtc->mode->vdisplay - crtc_h) / 2;
1258 } else {
1259 crtc_x = p->x;
1260 crtc_y = p->y;
1263 add_property(dev, p->plane_id, "FB_ID", p->fb_id);
1264 add_property(dev, p->plane_id, "CRTC_ID", p->crtc_id);
1265 add_property(dev, p->plane_id, "SRC_X", 0);
1266 add_property(dev, p->plane_id, "SRC_Y", 0);
1267 add_property(dev, p->plane_id, "SRC_W", p->w << 16);
1268 add_property(dev, p->plane_id, "SRC_H", p->h << 16);
1269 add_property(dev, p->plane_id, "CRTC_X", crtc_x);
1270 add_property(dev, p->plane_id, "CRTC_Y", crtc_y);
1271 add_property(dev, p->plane_id, "CRTC_W", crtc_w);
1272 add_property(dev, p->plane_id, "CRTC_H", crtc_h);
1274 return 0;
1277 static int set_plane(struct device *dev, struct plane_arg *p)
1279 drmModePlane *ovr;
1280 uint32_t plane_id;
1281 int crtc_x, crtc_y, crtc_w, crtc_h;
1282 struct crtc *crtc = NULL;
1283 unsigned int i, crtc_mask;
1285 /* Find an unused plane which can be connected to our CRTC. Find the
1286 * CRTC index first, then iterate over available planes.
1288 crtc = get_crtc_by_id(dev, p->crtc_id);
1289 if (!crtc) {
1290 fprintf(stderr, "CRTC %u not found\n", p->crtc_id);
1291 return -1;
1293 crtc_mask = get_crtc_mask(dev, crtc);
1294 plane_id = p->plane_id;
1296 for (i = 0; i < dev->resources->count_planes; i++) {
1297 ovr = dev->resources->planes[i].plane;
1298 if (!ovr)
1299 continue;
1301 if (plane_id && plane_id != ovr->plane_id)
1302 continue;
1304 if (!format_support(ovr, p->fourcc))
1305 continue;
1307 if ((ovr->possible_crtcs & crtc_mask) &&
1308 (ovr->crtc_id == 0 || ovr->crtc_id == p->crtc_id)) {
1309 plane_id = ovr->plane_id;
1310 break;
1314 if (i == dev->resources->count_planes) {
1315 fprintf(stderr, "no unused plane available for CRTC %u\n",
1316 p->crtc_id);
1317 return -1;
1320 fprintf(stderr, "testing %dx%d@%s overlay plane %u\n",
1321 p->w, p->h, p->format_str, plane_id);
1323 /* just use single plane format for now.. */
1324 if (bo_fb_create(dev->fd, p->fourcc, p->w, p->h,
1325 secondary_fill, &p->bo, &p->fb_id))
1326 return -1;
1328 crtc_w = p->w * p->scale;
1329 crtc_h = p->h * p->scale;
1330 if (!p->has_position) {
1331 /* Default to the middle of the screen */
1332 crtc_x = (crtc->mode->hdisplay - crtc_w) / 2;
1333 crtc_y = (crtc->mode->vdisplay - crtc_h) / 2;
1334 } else {
1335 crtc_x = p->x;
1336 crtc_y = p->y;
1339 /* note src coords (last 4 args) are in Q16 format */
1340 if (drmModeSetPlane(dev->fd, plane_id, p->crtc_id, p->fb_id,
1341 0, crtc_x, crtc_y, crtc_w, crtc_h,
1342 0, 0, p->w << 16, p->h << 16)) {
1343 fprintf(stderr, "failed to enable plane: %s\n",
1344 strerror(errno));
1345 return -1;
1348 ovr->crtc_id = p->crtc_id;
1350 return 0;
1353 static void atomic_set_planes(struct device *dev, struct plane_arg *p,
1354 unsigned int count, bool update)
1356 unsigned int i, pattern = primary_fill;
1358 /* set up planes */
1359 for (i = 0; i < count; i++) {
1360 if (i > 0)
1361 pattern = secondary_fill;
1362 else
1363 set_gamma(dev, p[i].crtc_id, p[i].fourcc);
1365 if (atomic_set_plane(dev, &p[i], pattern, update))
1366 return;
1370 static void
1371 atomic_test_page_flip(struct device *dev, struct pipe_arg *pipe_args,
1372 struct plane_arg *plane_args, unsigned int plane_count)
1374 int ret;
1376 gettimeofday(&pipe_args->start, NULL);
1377 pipe_args->swap_count = 0;
1379 while (true) {
1380 drmModeAtomicFree(dev->req);
1381 dev->req = drmModeAtomicAlloc();
1382 atomic_set_planes(dev, plane_args, plane_count, true);
1384 ret = drmModeAtomicCommit(dev->fd, dev->req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
1385 if (ret) {
1386 fprintf(stderr, "Atomic Commit failed [2]\n");
1387 return;
1390 pipe_args->swap_count++;
1391 if (pipe_args->swap_count == 60) {
1392 struct timeval end;
1393 double t;
1395 gettimeofday(&end, NULL);
1396 t = end.tv_sec + end.tv_usec * 1e-6 -
1397 (pipe_args->start.tv_sec + pipe_args->start.tv_usec * 1e-6);
1398 fprintf(stderr, "freq: %.02fHz\n", pipe_args->swap_count / t);
1399 pipe_args->swap_count = 0;
1400 pipe_args->start = end;
1405 static void atomic_clear_planes(struct device *dev, struct plane_arg *p, unsigned int count)
1407 unsigned int i;
1409 for (i = 0; i < count; i++) {
1410 add_property(dev, p[i].plane_id, "FB_ID", 0);
1411 add_property(dev, p[i].plane_id, "CRTC_ID", 0);
1412 add_property(dev, p[i].plane_id, "SRC_X", 0);
1413 add_property(dev, p[i].plane_id, "SRC_Y", 0);
1414 add_property(dev, p[i].plane_id, "SRC_W", 0);
1415 add_property(dev, p[i].plane_id, "SRC_H", 0);
1416 add_property(dev, p[i].plane_id, "CRTC_X", 0);
1417 add_property(dev, p[i].plane_id, "CRTC_Y", 0);
1418 add_property(dev, p[i].plane_id, "CRTC_W", 0);
1419 add_property(dev, p[i].plane_id, "CRTC_H", 0);
1423 static void atomic_clear_FB(struct device *dev, struct plane_arg *p, unsigned int count)
1425 unsigned int i;
1427 for (i = 0; i < count; i++) {
1428 if (p[i].fb_id) {
1429 drmModeRmFB(dev->fd, p[i].fb_id);
1430 p[i].fb_id = 0;
1432 if (p[i].old_fb_id) {
1433 drmModeRmFB(dev->fd, p[i].old_fb_id);
1434 p[i].old_fb_id = 0;
1436 if (p[i].bo) {
1437 bo_destroy(p[i].bo);
1438 p[i].bo = NULL;
1440 if (p[i].old_bo) {
1441 bo_destroy(p[i].old_bo);
1442 p[i].old_bo = NULL;
1448 static void clear_planes(struct device *dev, struct plane_arg *p, unsigned int count)
1450 unsigned int i;
1452 for (i = 0; i < count; i++) {
1453 if (p[i].fb_id)
1454 drmModeRmFB(dev->fd, p[i].fb_id);
1455 if (p[i].bo)
1456 bo_destroy(p[i].bo);
1460 static int pipe_resolve_connectors(struct device *dev, struct pipe_arg *pipe)
1462 drmModeConnector *connector;
1463 unsigned int i;
1464 uint32_t id;
1465 char *endp;
1467 for (i = 0; i < pipe->num_cons; i++) {
1468 id = strtoul(pipe->cons[i], &endp, 10);
1469 if (endp == pipe->cons[i]) {
1470 connector = get_connector_by_name(dev, pipe->cons[i]);
1471 if (!connector) {
1472 fprintf(stderr, "no connector named '%s'\n",
1473 pipe->cons[i]);
1474 return -ENODEV;
1477 id = connector->connector_id;
1480 pipe->con_ids[i] = id;
1483 return 0;
1486 static bool pipe_has_writeback_connector(struct device *dev, struct pipe_arg *pipes,
1487 unsigned int count)
1489 drmModeConnector *connector;
1490 unsigned int i, j;
1492 for (j = 0; j < count; j++) {
1493 struct pipe_arg *pipe = &pipes[j];
1495 for (i = 0; i < pipe->num_cons; i++) {
1496 connector = get_connector_by_id(dev, pipe->con_ids[i]);
1497 if (connector && connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
1498 return true;
1501 return false;
1504 static int pipe_attempt_connector(struct device *dev, drmModeConnector *con,
1505 struct pipe_arg *pipe)
1507 char *con_str;
1508 int i;
1510 con_str = calloc(8, sizeof(char));
1511 if (!con_str)
1512 return -1;
1514 sprintf(con_str, "%d", con->connector_id);
1515 strcpy(pipe->format_str, "XR24");
1516 pipe->fourcc = util_format_fourcc(pipe->format_str);
1517 pipe->num_cons = 1;
1518 pipe->con_ids = calloc(1, sizeof(*pipe->con_ids));
1519 pipe->cons = calloc(1, sizeof(*pipe->cons));
1521 if (!pipe->con_ids || !pipe->cons)
1522 goto free_con_str;
1524 pipe->con_ids[0] = con->connector_id;
1525 pipe->cons[0] = (const char*)con_str;
1527 pipe->crtc = pipe_find_crtc(dev, pipe);
1528 if (!pipe->crtc)
1529 goto free_all;
1531 pipe->crtc_id = pipe->crtc->crtc->crtc_id;
1533 /* Return the first mode if no preferred. */
1534 pipe->mode = &con->modes[0];
1536 for (i = 0; i < con->count_modes; i++) {
1537 drmModeModeInfo *current_mode = &con->modes[i];
1539 if (current_mode->type & DRM_MODE_TYPE_PREFERRED) {
1540 pipe->mode = current_mode;
1541 break;
1545 sprintf(pipe->mode_str, "%dx%d", pipe->mode->hdisplay, pipe->mode->vdisplay);
1547 return 0;
1549 free_all:
1550 free(pipe->cons);
1551 free(pipe->con_ids);
1552 free_con_str:
1553 free(con_str);
1554 return -1;
1557 static int pipe_find_preferred(struct device *dev, struct pipe_arg **out_pipes)
1559 struct pipe_arg *pipes;
1560 struct resources *res = dev->resources;
1561 drmModeConnector *con = NULL;
1562 int i, connected = 0, attempted = 0;
1564 for (i = 0; i < res->count_connectors; i++) {
1565 con = res->connectors[i].connector;
1566 if (!con || con->connection != DRM_MODE_CONNECTED ||
1567 con->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
1568 continue;
1569 connected++;
1571 if (!connected) {
1572 printf("no connected connector!\n");
1573 return 0;
1576 pipes = calloc(connected, sizeof(struct pipe_arg));
1577 if (!pipes)
1578 return 0;
1580 for (i = 0; i < res->count_connectors && attempted < connected; i++) {
1581 con = res->connectors[i].connector;
1582 if (!con || con->connection != DRM_MODE_CONNECTED)
1583 continue;
1585 if (pipe_attempt_connector(dev, con, &pipes[attempted]) < 0) {
1586 printf("failed fetching preferred mode for connector\n");
1587 continue;
1589 attempted++;
1592 *out_pipes = pipes;
1593 return attempted;
1596 static struct plane *get_primary_plane_by_crtc(struct device *dev, struct crtc *crtc)
1598 unsigned int i;
1600 for (i = 0; i < dev->resources->count_planes; i++) {
1601 struct plane *plane = &dev->resources->planes[i];
1602 drmModePlane *ovr = plane->plane;
1603 if (!ovr)
1604 continue;
1606 // XXX: add is_primary_plane and (?) format checks
1608 if (ovr->possible_crtcs & get_crtc_mask(dev, crtc))
1609 return plane;
1611 return NULL;
1614 static unsigned int set_mode(struct device *dev, struct pipe_arg **pipe_args, unsigned int count)
1616 unsigned int i, j;
1617 int ret, x = 0;
1618 int preferred = count == 0;
1619 struct pipe_arg *pipes;
1621 if (preferred) {
1622 count = pipe_find_preferred(dev, pipe_args);
1623 if (!count) {
1624 fprintf(stderr, "can't find any preferred connector/mode.\n");
1625 return 0;
1628 pipes = *pipe_args;
1629 } else {
1630 pipes = *pipe_args;
1632 for (i = 0; i < count; i++) {
1633 struct pipe_arg *pipe = &pipes[i];
1635 ret = pipe_resolve_connectors(dev, pipe);
1636 if (ret < 0)
1637 return 0;
1639 ret = pipe_find_crtc_and_mode(dev, pipe);
1640 if (ret < 0)
1641 continue;
1645 if (!dev->use_atomic) {
1646 for (i = 0; i < count; i++) {
1647 struct pipe_arg *pipe = &pipes[i];
1649 if (pipe->mode == NULL)
1650 continue;
1652 if (!preferred) {
1653 dev->mode.width += pipe->mode->hdisplay;
1654 if (dev->mode.height < pipe->mode->vdisplay)
1655 dev->mode.height = pipe->mode->vdisplay;
1656 } else {
1657 /* XXX: Use a clone mode, more like atomic. We could do per
1658 * connector bo/fb, so we don't have the stretched image.
1660 if (dev->mode.width < pipe->mode->hdisplay)
1661 dev->mode.width = pipe->mode->hdisplay;
1662 if (dev->mode.height < pipe->mode->vdisplay)
1663 dev->mode.height = pipe->mode->vdisplay;
1667 if (bo_fb_create(dev->fd, pipes[0].fourcc, dev->mode.width, dev->mode.height,
1668 primary_fill, &dev->mode.bo, &dev->mode.fb_id))
1669 return 0;
1672 for (i = 0; i < count; i++) {
1673 struct pipe_arg *pipe = &pipes[i];
1674 uint32_t blob_id;
1676 if (pipe->mode == NULL)
1677 continue;
1679 printf("setting mode %s-%.2fHz on connectors ",
1680 pipe->mode->name, mode_vrefresh(pipe->mode));
1681 for (j = 0; j < pipe->num_cons; ++j) {
1682 printf("%s, ", pipe->cons[j]);
1683 if (dev->use_atomic)
1684 add_property(dev, pipe->con_ids[j], "CRTC_ID", pipe->crtc_id);
1686 printf("crtc %d\n", pipe->crtc_id);
1688 if (!dev->use_atomic) {
1689 ret = drmModeSetCrtc(dev->fd, pipe->crtc_id, dev->mode.fb_id,
1690 x, 0, pipe->con_ids, pipe->num_cons,
1691 pipe->mode);
1693 /* XXX: Actually check if this is needed */
1694 drmModeDirtyFB(dev->fd, dev->mode.fb_id, NULL, 0);
1696 if (!preferred)
1697 x += pipe->mode->hdisplay;
1699 if (ret) {
1700 fprintf(stderr, "failed to set mode: %s\n", strerror(errno));
1701 return 0;
1704 set_gamma(dev, pipe->crtc_id, pipe->fourcc);
1705 } else {
1706 drmModeCreatePropertyBlob(dev->fd, pipe->mode, sizeof(*pipe->mode), &blob_id);
1707 add_property(dev, pipe->crtc_id, "MODE_ID", blob_id);
1708 add_property(dev, pipe->crtc_id, "ACTIVE", 1);
1710 /* By default atomic modeset does not set a primary plane, shrug */
1711 if (preferred) {
1712 struct plane *plane = get_primary_plane_by_crtc(dev, pipe->crtc);
1713 struct plane_arg plane_args = {
1714 .plane_id = plane->plane->plane_id,
1715 .crtc_id = pipe->crtc_id,
1716 .w = pipe->mode->hdisplay,
1717 .h = pipe->mode->vdisplay,
1718 .scale = 1.0,
1719 .format_str = "XR24",
1720 .fourcc = util_format_fourcc(pipe->format_str),
1723 atomic_set_planes(dev, &plane_args, 1, false);
1728 return count;
1731 static void writeback_config(struct device *dev, struct pipe_arg *pipes, unsigned int count)
1733 drmModeConnector *connector;
1734 unsigned int i, j;
1736 for (j = 0; j < count; j++) {
1737 struct pipe_arg *pipe = &pipes[j];
1739 for (i = 0; i < pipe->num_cons; i++) {
1740 connector = get_connector_by_id(dev, pipe->con_ids[i]);
1741 if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) {
1742 if (!pipe->mode) {
1743 fprintf(stderr, "no mode for writeback\n");
1744 return;
1746 bo_fb_create(dev->fd, pipes[j].fourcc,
1747 pipe->mode->hdisplay, pipe->mode->vdisplay,
1748 UTIL_PATTERN_PLAIN,
1749 &pipe->out_bo, &pipe->out_fb_id);
1750 add_property(dev, pipe->con_ids[i], "WRITEBACK_FB_ID",
1751 pipe->out_fb_id);
1752 add_property(dev, pipe->con_ids[i], "WRITEBACK_OUT_FENCE_PTR",
1753 (uintptr_t)(&dev->writeback_fence_fd));
1759 static int poll_writeback_fence(int fd, int timeout)
1761 struct pollfd fds = { fd, POLLIN };
1762 int ret;
1764 do {
1765 ret = poll(&fds, 1, timeout);
1766 if (ret > 0) {
1767 if (fds.revents & (POLLERR | POLLNVAL))
1768 return -EINVAL;
1770 return 0;
1771 } else if (ret == 0) {
1772 return -ETIMEDOUT;
1773 } else {
1774 ret = -errno;
1775 if (ret == -EINTR || ret == -EAGAIN)
1776 continue;
1777 return ret;
1779 } while (1);
1783 static void dump_output_fb(struct device *dev, struct pipe_arg *pipes, char *dump_path,
1784 unsigned int count)
1786 drmModeConnector *connector;
1787 unsigned int i, j;
1789 for (j = 0; j < count; j++) {
1790 struct pipe_arg *pipe = &pipes[j];
1792 for (i = 0; i < pipe->num_cons; i++) {
1793 connector = get_connector_by_id(dev, pipe->con_ids[i]);
1794 if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
1795 bo_dump(pipe->out_bo, dump_path);
1800 static void atomic_clear_mode(struct device *dev, struct pipe_arg *pipes, unsigned int count)
1802 unsigned int i;
1803 unsigned int j;
1805 for (i = 0; i < count; i++) {
1806 struct pipe_arg *pipe = &pipes[i];
1808 if (pipe->mode == NULL)
1809 continue;
1811 for (j = 0; j < pipe->num_cons; ++j)
1812 add_property(dev, pipe->con_ids[j], "CRTC_ID",0);
1814 add_property(dev, pipe->crtc_id, "MODE_ID", 0);
1815 add_property(dev, pipe->crtc_id, "ACTIVE", 0);
1819 static void clear_mode(struct device *dev)
1821 if (dev->mode.fb_id)
1822 drmModeRmFB(dev->fd, dev->mode.fb_id);
1823 if (dev->mode.bo)
1824 bo_destroy(dev->mode.bo);
1827 static void set_planes(struct device *dev, struct plane_arg *p, unsigned int count)
1829 unsigned int i;
1831 /* set up planes/overlays */
1832 for (i = 0; i < count; i++)
1833 if (set_plane(dev, &p[i]))
1834 return;
1837 static void set_cursors(struct device *dev, struct pipe_arg *pipes, unsigned int count)
1839 uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
1840 uint32_t cw = 64;
1841 uint32_t ch = 64;
1842 struct bo *bo;
1843 uint64_t value;
1844 unsigned int i;
1845 int ret;
1847 ret = drmGetCap(dev->fd, DRM_CAP_CURSOR_WIDTH, &value);
1848 if (!ret)
1849 cw = value;
1851 ret = drmGetCap(dev->fd, DRM_CAP_CURSOR_HEIGHT, &value);
1852 if (!ret)
1853 ch = value;
1856 /* create cursor bo.. just using PATTERN_PLAIN as it has
1857 * translucent alpha
1859 bo = bo_create(dev->fd, DRM_FORMAT_ARGB8888, cw, ch, handles, pitches,
1860 offsets, UTIL_PATTERN_PLAIN);
1861 if (bo == NULL)
1862 return;
1864 dev->mode.cursor_bo = bo;
1866 for (i = 0; i < count; i++) {
1867 struct pipe_arg *pipe = &pipes[i];
1868 ret = cursor_init(dev->fd, handles[0],
1869 pipe->crtc_id,
1870 pipe->mode->hdisplay, pipe->mode->vdisplay,
1871 cw, ch);
1872 if (ret) {
1873 fprintf(stderr, "failed to init cursor for CRTC[%u]\n",
1874 pipe->crtc_id);
1875 return;
1879 cursor_start();
1882 static void clear_cursors(struct device *dev)
1884 cursor_stop();
1886 if (dev->mode.cursor_bo)
1887 bo_destroy(dev->mode.cursor_bo);
1890 static void test_page_flip(struct device *dev, struct pipe_arg *pipes, unsigned int count)
1892 unsigned int other_fb_id;
1893 struct bo *other_bo;
1894 drmEventContext evctx;
1895 unsigned int i;
1896 int ret;
1898 if (bo_fb_create(dev->fd, pipes[0].fourcc, dev->mode.width, dev->mode.height,
1899 UTIL_PATTERN_PLAIN, &other_bo, &other_fb_id))
1900 return;
1902 for (i = 0; i < count; i++) {
1903 struct pipe_arg *pipe = &pipes[i];
1905 if (pipe->mode == NULL)
1906 continue;
1908 ret = drmModePageFlip(dev->fd, pipe->crtc_id,
1909 other_fb_id, DRM_MODE_PAGE_FLIP_EVENT,
1910 pipe);
1911 if (ret) {
1912 fprintf(stderr, "failed to page flip: %s\n", strerror(errno));
1913 goto err_rmfb;
1915 gettimeofday(&pipe->start, NULL);
1916 pipe->swap_count = 0;
1917 pipe->fb_id[0] = dev->mode.fb_id;
1918 pipe->fb_id[1] = other_fb_id;
1919 pipe->current_fb_id = other_fb_id;
1922 memset(&evctx, 0, sizeof evctx);
1923 evctx.version = DRM_EVENT_CONTEXT_VERSION;
1924 evctx.vblank_handler = NULL;
1925 evctx.page_flip_handler = page_flip_handler;
1927 while (1) {
1928 #if 0
1929 struct pollfd pfd[2];
1931 pfd[0].fd = 0;
1932 pfd[0].events = POLLIN;
1933 pfd[1].fd = fd;
1934 pfd[1].events = POLLIN;
1936 if (poll(pfd, 2, -1) < 0) {
1937 fprintf(stderr, "poll error\n");
1938 break;
1941 if (pfd[0].revents)
1942 break;
1943 #else
1944 struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 };
1945 fd_set fds;
1947 FD_ZERO(&fds);
1948 FD_SET(0, &fds);
1949 FD_SET(dev->fd, &fds);
1950 ret = select(dev->fd + 1, &fds, NULL, NULL, &timeout);
1952 if (ret <= 0) {
1953 fprintf(stderr, "select timed out or error (ret %d)\n",
1954 ret);
1955 continue;
1956 } else if (FD_ISSET(0, &fds)) {
1957 break;
1959 #endif
1961 drmHandleEvent(dev->fd, &evctx);
1964 err_rmfb:
1965 drmModeRmFB(dev->fd, other_fb_id);
1966 bo_destroy(other_bo);
1969 #define min(a, b) ((a) < (b) ? (a) : (b))
1971 static int parse_connector(struct pipe_arg *pipe, const char *arg)
1973 unsigned int len;
1974 unsigned int i;
1975 const char *p;
1976 char *endp;
1978 pipe->vrefresh = 0;
1979 pipe->crtc_id = (uint32_t)-1;
1980 strcpy(pipe->format_str, "XR24");
1982 /* Count the number of connectors and allocate them. */
1983 pipe->num_cons = 1;
1984 for (p = arg; *p && *p != ':' && *p != '@'; ++p) {
1985 if (*p == ',')
1986 pipe->num_cons++;
1989 pipe->con_ids = calloc(pipe->num_cons, sizeof(*pipe->con_ids));
1990 pipe->cons = calloc(pipe->num_cons, sizeof(*pipe->cons));
1991 if (pipe->con_ids == NULL || pipe->cons == NULL)
1992 return -1;
1994 /* Parse the connectors. */
1995 for (i = 0, p = arg; i < pipe->num_cons; ++i, p = endp + 1) {
1996 endp = strpbrk(p, ",@:");
1997 if (!endp)
1998 break;
2000 pipe->cons[i] = strndup(p, endp - p);
2002 if (*endp != ',')
2003 break;
2006 if (i != pipe->num_cons - 1)
2007 return -1;
2009 /* Parse the remaining parameters. */
2010 if (!endp)
2011 return -1;
2012 if (*endp == '@') {
2013 arg = endp + 1;
2014 pipe->crtc_id = strtoul(arg, &endp, 10);
2016 if (*endp != ':')
2017 return -1;
2019 arg = endp + 1;
2021 /* Search for the vertical refresh or the format. */
2022 p = strpbrk(arg, "-@");
2023 if (p == NULL)
2024 p = arg + strlen(arg);
2025 len = min(sizeof pipe->mode_str - 1, (unsigned int)(p - arg));
2026 strncpy(pipe->mode_str, arg, len);
2027 pipe->mode_str[len] = '\0';
2029 if (*p == '-') {
2030 pipe->vrefresh = strtof(p + 1, &endp);
2031 p = endp;
2034 if (*p == '@') {
2035 len = sizeof(pipe->format_str) - 1;
2036 strncpy(pipe->format_str, p + 1, len);
2037 pipe->format_str[len] = '\0';
2040 pipe->fourcc = util_format_fourcc(pipe->format_str);
2041 if (pipe->fourcc == 0) {
2042 fprintf(stderr, "unknown format %s\n", pipe->format_str);
2043 return -1;
2046 return 0;
2049 static int parse_plane(struct plane_arg *plane, const char *p)
2051 unsigned int len;
2052 char *end;
2054 plane->plane_id = strtoul(p, &end, 10);
2055 if (*end != '@')
2056 return -EINVAL;
2058 p = end + 1;
2059 plane->crtc_id = strtoul(p, &end, 10);
2060 if (*end != ':')
2061 return -EINVAL;
2063 p = end + 1;
2064 plane->w = strtoul(p, &end, 10);
2065 if (*end != 'x')
2066 return -EINVAL;
2068 p = end + 1;
2069 plane->h = strtoul(p, &end, 10);
2071 if (*end == '+' || *end == '-') {
2072 plane->x = strtol(end, &end, 10);
2073 if (*end != '+' && *end != '-')
2074 return -EINVAL;
2075 plane->y = strtol(end, &end, 10);
2077 plane->has_position = true;
2080 if (*end == '*') {
2081 p = end + 1;
2082 plane->scale = strtod(p, &end);
2083 if (plane->scale <= 0.0)
2084 return -EINVAL;
2085 } else {
2086 plane->scale = 1.0;
2089 if (*end == '@') {
2090 len = sizeof(plane->format_str) - 1;
2091 strncpy(plane->format_str, end + 1, len);
2092 plane->format_str[len] = '\0';
2093 } else {
2094 strcpy(plane->format_str, "XR24");
2097 plane->fourcc = util_format_fourcc(plane->format_str);
2098 if (plane->fourcc == 0) {
2099 fprintf(stderr, "unknown format %s\n", plane->format_str);
2100 return -EINVAL;
2103 return 0;
2106 static int parse_property(struct property_arg *p, const char *arg)
2108 if (sscanf(arg, "%d:%32[^:]:%" SCNu64, &p->obj_id, p->name, &p->value) != 3)
2109 return -1;
2111 p->obj_type = 0;
2112 p->name[DRM_PROP_NAME_LEN] = '\0';
2114 return 0;
2117 static void parse_fill_patterns(char *arg)
2119 char *fill = strtok(arg, ",");
2120 if (!fill)
2121 return;
2122 primary_fill = util_pattern_enum(fill);
2123 fill = strtok(NULL, ",");
2124 if (!fill)
2125 return;
2126 secondary_fill = util_pattern_enum(fill);
2129 static void usage(char *name)
2131 fprintf(stderr, "usage: %s [-acDdefMoPpsCvrw]\n", name);
2133 fprintf(stderr, "\n Query options:\n\n");
2134 fprintf(stderr, "\t-c\tlist connectors\n");
2135 fprintf(stderr, "\t-e\tlist encoders\n");
2136 fprintf(stderr, "\t-f\tlist framebuffers\n");
2137 fprintf(stderr, "\t-p\tlist CRTCs and planes (pipes)\n");
2139 fprintf(stderr, "\n Test options:\n\n");
2140 fprintf(stderr, "\t-P <plane_id>@<crtc_id>:<w>x<h>[+<x>+<y>][*<scale>][@<format>]\tset a plane, see 'plane-topology'\n");
2141 fprintf(stderr, "\t-s <connector_id>[,<connector_id>][@<crtc_id>]:mode[@<format>]\tset a mode, see 'mode-topology'\n");
2142 fprintf(stderr, "\t\twhere mode can be specified as:\n");
2143 fprintf(stderr, "\t\t<hdisp>x<vdisp>[-<vrefresh>]\n");
2144 fprintf(stderr, "\t\t<hdisp>,<hss>,<hse>,<htot>,<vdisp>,<vss>,<vse>,<vtot>-<vrefresh>\n");
2145 fprintf(stderr, "\t\t#<mode index>\n");
2146 fprintf(stderr, "\t-C\ttest hw cursor\n");
2147 fprintf(stderr, "\t-v\ttest vsynced page flipping\n");
2148 fprintf(stderr, "\t-r\tset the preferred mode for all connectors\n");
2149 fprintf(stderr, "\t-w <obj_id>:<prop_name>:<value>\tset property, see 'property'\n");
2150 fprintf(stderr, "\t-a \tuse atomic API\n");
2151 fprintf(stderr, "\t-F pattern1,pattern2\tspecify fill patterns\n");
2152 fprintf(stderr, "\t-o <desired file path> \t Dump writeback output buffer to file\n");
2154 fprintf(stderr, "\n Generic options:\n\n");
2155 fprintf(stderr, "\t-d\tdrop master after mode set\n");
2156 fprintf(stderr, "\t-M module\tuse the given driver\n");
2157 fprintf(stderr, "\t-D device\tuse the given device\n");
2159 fprintf(stderr, "\n\tDefault is to dump all info.\n");
2161 fprintf(stderr, "\n");
2162 fprintf(stderr, "Plane Topology is defined as:\n");
2163 fprintf(stderr, "\tplane-topology\t::= plane-id '@' crtc-id ':' width 'x' height ( <plane-offsets> )? ;\n");
2164 fprintf(stderr, "\tplane-offsets\t::= '+' x-offset '+' y-offset ( <plane-scale> )? ;\n");
2165 fprintf(stderr, "\tplane-scale\t::= '*' scale ( <plane-format> )? ;\n");
2166 fprintf(stderr, "\tplane-format\t::= '@' format ;\n");
2168 fprintf(stderr, "\n");
2169 fprintf(stderr, "Mode Topology is defined as:\n");
2170 fprintf(stderr, "\tmode-topology\t::= connector-id ( ',' connector-id )* ( '@' crtc-id )? ':' <mode-selection> ( '@' format )? ;\n");
2171 fprintf(stderr, "\tmode-selection\t::= <indexed-mode> | <named-mode> | <custom-mode> ;\n");
2172 fprintf(stderr, "\tindexed-mode\t::= '#' mode-index ;\n");
2173 fprintf(stderr, "\tnamed-mode\t::= width 'x' height ( '-' vrefresh )? ;\n");
2174 fprintf(stderr, "\tcustom-mode\t::= hdisplay ',' hsyncstart ',' hsyncend ',' htotal ',' vdisplay ',' vsyncstart ',' vsyncend ',' vtotal '-' vrefresh ;\n");
2176 fprintf(stderr, "\n");
2177 fprintf(stderr, "Property is defined as:\n");
2178 fprintf(stderr, "\tproperty\t::= object-id ':' property-name ':' value ;\n");
2179 exit(0);
2182 static char optstr[] = "acdD:efF:M:P:ps:Cvrw:o:";
2184 int main(int argc, char **argv)
2186 struct device dev;
2188 int c;
2189 int encoders = 0, connectors = 0, crtcs = 0, planes = 0, framebuffers = 0;
2190 int drop_master = 0;
2191 int test_vsync = 0;
2192 int test_cursor = 0;
2193 int set_preferred = 0;
2194 int use_atomic = 0;
2195 char *device = NULL;
2196 char *module = NULL;
2197 unsigned int i;
2198 unsigned int count = 0, plane_count = 0;
2199 unsigned int prop_count = 0;
2200 struct pipe_arg *pipe_args = NULL;
2201 struct plane_arg *plane_args = NULL;
2202 struct property_arg *prop_args = NULL;
2203 unsigned int args = 0;
2204 int ret;
2205 char *dump_path = NULL;
2207 memset(&dev, 0, sizeof dev);
2209 opterr = 0;
2210 while ((c = getopt(argc, argv, optstr)) != -1) {
2211 args++;
2213 switch (c) {
2214 case 'a':
2215 use_atomic = 1;
2216 /* Preserve the default behaviour of dumping all information. */
2217 args--;
2218 break;
2219 case 'c':
2220 connectors = 1;
2221 break;
2222 case 'D':
2223 device = optarg;
2224 /* Preserve the default behaviour of dumping all information. */
2225 args--;
2226 break;
2227 case 'd':
2228 drop_master = 1;
2229 break;
2230 case 'e':
2231 encoders = 1;
2232 break;
2233 case 'f':
2234 framebuffers = 1;
2235 break;
2236 case 'F':
2237 parse_fill_patterns(optarg);
2238 break;
2239 case 'M':
2240 module = optarg;
2241 /* Preserve the default behaviour of dumping all information. */
2242 args--;
2243 break;
2244 case 'o':
2245 dump_path = optarg;
2246 break;
2247 case 'P':
2248 plane_args = realloc(plane_args,
2249 (plane_count + 1) * sizeof *plane_args);
2250 if (plane_args == NULL) {
2251 fprintf(stderr, "memory allocation failed\n");
2252 return 1;
2254 memset(&plane_args[plane_count], 0, sizeof(*plane_args));
2256 if (parse_plane(&plane_args[plane_count], optarg) < 0)
2257 usage(argv[0]);
2259 plane_count++;
2260 break;
2261 case 'p':
2262 crtcs = 1;
2263 planes = 1;
2264 break;
2265 case 's':
2266 pipe_args = realloc(pipe_args,
2267 (count + 1) * sizeof *pipe_args);
2268 if (pipe_args == NULL) {
2269 fprintf(stderr, "memory allocation failed\n");
2270 return 1;
2272 memset(&pipe_args[count], 0, sizeof(*pipe_args));
2274 if (parse_connector(&pipe_args[count], optarg) < 0)
2275 usage(argv[0]);
2277 count++;
2278 break;
2279 case 'C':
2280 test_cursor = 1;
2281 break;
2282 case 'v':
2283 test_vsync = 1;
2284 break;
2285 case 'r':
2286 set_preferred = 1;
2287 break;
2288 case 'w':
2289 prop_args = realloc(prop_args,
2290 (prop_count + 1) * sizeof *prop_args);
2291 if (prop_args == NULL) {
2292 fprintf(stderr, "memory allocation failed\n");
2293 return 1;
2295 memset(&prop_args[prop_count], 0, sizeof(*prop_args));
2297 if (parse_property(&prop_args[prop_count], optarg) < 0)
2298 usage(argv[0]);
2300 prop_count++;
2301 break;
2302 default:
2303 usage(argv[0]);
2304 break;
2308 /* Dump all the details when no* arguments are provided. */
2309 if (!args)
2310 encoders = connectors = crtcs = planes = framebuffers = 1;
2312 if (test_vsync && !count && !set_preferred) {
2313 fprintf(stderr, "page flipping requires at least one -s or -r option.\n");
2314 return -1;
2316 if (set_preferred && count) {
2317 fprintf(stderr, "cannot use -r (preferred) when -s (mode) is set\n");
2318 return -1;
2321 dev.fd = util_open(device, module);
2322 if (dev.fd < 0)
2323 return -1;
2325 if (use_atomic) {
2326 ret = drmSetClientCap(dev.fd, DRM_CLIENT_CAP_ATOMIC, 1);
2327 drmSetClientCap(dev.fd, DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1);
2328 if (ret) {
2329 fprintf(stderr, "no atomic modesetting support: %s\n", strerror(errno));
2330 drmClose(dev.fd);
2331 return -1;
2335 dev.use_atomic = use_atomic;
2337 dev.resources = get_resources(&dev);
2338 if (!dev.resources) {
2339 drmClose(dev.fd);
2340 return 1;
2343 #define dump_resource(dev, res) if (res) dump_##res(dev)
2345 dump_resource(&dev, encoders);
2346 dump_resource(&dev, connectors);
2347 dump_resource(&dev, crtcs);
2348 dump_resource(&dev, planes);
2349 dump_resource(&dev, framebuffers);
2351 if (dev.use_atomic)
2352 dev.req = drmModeAtomicAlloc();
2354 for (i = 0; i < prop_count; ++i)
2355 set_property(&dev, &prop_args[i]);
2357 if (dev.use_atomic) {
2358 if (set_preferred || (count && plane_count)) {
2359 uint64_t cap = 0;
2361 ret = drmGetCap(dev.fd, DRM_CAP_DUMB_BUFFER, &cap);
2362 if (ret || cap == 0) {
2363 fprintf(stderr, "driver doesn't support the dumb buffer API\n");
2364 return 1;
2367 if (set_preferred || count)
2368 count = set_mode(&dev, &pipe_args, count);
2370 if (dump_path) {
2371 if (!pipe_has_writeback_connector(&dev, pipe_args, count)) {
2372 fprintf(stderr, "No writeback connector found, can not dump.\n");
2373 return 1;
2376 writeback_config(&dev, pipe_args, count);
2379 if (plane_count)
2380 atomic_set_planes(&dev, plane_args, plane_count, false);
2382 ret = drmModeAtomicCommit(dev.fd, dev.req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
2383 if (ret) {
2384 fprintf(stderr, "Atomic Commit failed [1]\n");
2385 return 1;
2389 * Since only writeback connectors have an output fb, this should only be
2390 * called for writeback.
2392 if (dump_path) {
2393 ret = poll_writeback_fence(dev.writeback_fence_fd, 1000);
2394 if (ret)
2395 fprintf(stderr, "Poll for writeback error: %d. Skipping Dump.\n",
2396 ret);
2397 dump_output_fb(&dev, pipe_args, dump_path, count);
2400 if (test_vsync)
2401 atomic_test_page_flip(&dev, pipe_args, plane_args, plane_count);
2403 if (drop_master)
2404 drmDropMaster(dev.fd);
2406 getchar();
2408 drmModeAtomicFree(dev.req);
2409 dev.req = drmModeAtomicAlloc();
2411 /* XXX: properly teardown the preferred mode/plane state */
2412 if (plane_count)
2413 atomic_clear_planes(&dev, plane_args, plane_count);
2415 if (count)
2416 atomic_clear_mode(&dev, pipe_args, count);
2419 ret = drmModeAtomicCommit(dev.fd, dev.req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
2420 if (ret)
2421 fprintf(stderr, "Atomic Commit failed\n");
2423 if (count && plane_count)
2424 atomic_clear_FB(&dev, plane_args, plane_count);
2426 drmModeAtomicFree(dev.req);
2427 } else {
2428 if (dump_path) {
2429 fprintf(stderr, "writeback / dump is only supported in atomic mode\n");
2430 return 1;
2433 if (set_preferred || count || plane_count) {
2434 uint64_t cap = 0;
2436 ret = drmGetCap(dev.fd, DRM_CAP_DUMB_BUFFER, &cap);
2437 if (ret || cap == 0) {
2438 fprintf(stderr, "driver doesn't support the dumb buffer API\n");
2439 return 1;
2442 if (set_preferred || count)
2443 count = set_mode(&dev, &pipe_args, count);
2445 if (plane_count)
2446 set_planes(&dev, plane_args, plane_count);
2448 if (test_cursor)
2449 set_cursors(&dev, pipe_args, count);
2451 if (test_vsync)
2452 test_page_flip(&dev, pipe_args, count);
2454 if (drop_master)
2455 drmDropMaster(dev.fd);
2457 getchar();
2459 if (test_cursor)
2460 clear_cursors(&dev);
2462 if (plane_count)
2463 clear_planes(&dev, plane_args, plane_count);
2465 if (set_preferred || count)
2466 clear_mode(&dev);
2470 free_resources(dev.resources);
2471 drmClose(dev.fd);
2473 return 0;