libdrm: NOTE! Default branch is now main
[drm/libdrm.git] / tests / modetest / modetest.c
blob2c83bd03037c80ec75ba3d4d800acb867dedeee3
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;
74 struct crtc {
75 drmModeCrtc *crtc;
76 drmModeObjectProperties *props;
77 drmModePropertyRes **props_info;
78 drmModeModeInfo *mode;
81 struct encoder {
82 drmModeEncoder *encoder;
85 struct connector {
86 drmModeConnector *connector;
87 drmModeObjectProperties *props;
88 drmModePropertyRes **props_info;
89 char *name;
92 struct fb {
93 drmModeFB *fb;
96 struct plane {
97 drmModePlane *plane;
98 drmModeObjectProperties *props;
99 drmModePropertyRes **props_info;
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;
115 struct device {
116 int fd;
118 struct resources *resources;
120 struct {
121 unsigned int width;
122 unsigned int height;
124 unsigned int fb_id;
125 struct bo *bo;
126 struct bo *cursor_bo;
127 } mode;
129 int use_atomic;
130 drmModeAtomicReq *req;
133 static inline int64_t U642I64(uint64_t val)
135 return (int64_t)*((int64_t *)&val);
138 static float mode_vrefresh(drmModeModeInfo *mode)
140 return mode->clock * 1000.00
141 / (mode->htotal * mode->vtotal);
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 = ", "; \
154 return NULL; \
157 static const char *mode_type_names[] = {
158 "builtin",
159 "clock_c",
160 "crtc_c",
161 "preferred",
162 "default",
163 "userdef",
164 "driver",
167 static bit_name_fn(mode_type)
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"
186 static bit_name_fn(mode_flag)
188 static void dump_fourcc(uint32_t fourcc)
190 printf(" %c%c%c%c",
191 fourcc,
192 fourcc >> 8,
193 fourcc >> 16,
194 fourcc >> 24);
197 static void dump_encoders(struct device *dev)
199 drmModeEncoder *encoder;
200 int i;
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;
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);
216 printf("\n");
219 static void dump_mode(drmModeModeInfo *mode, int index)
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);
235 printf(" flags: ");
236 mode_flag_str(mode->flags);
237 printf("; type: ");
238 mode_type_str(mode->type);
239 printf("\n");
242 static void dump_blob(struct device *dev, uint32_t blob_id)
244 uint32_t i;
245 unsigned char *blob_data;
246 drmModePropertyBlobPtr blob;
248 blob = drmModeGetPropertyBlob(dev->fd, blob_id);
249 if (!blob) {
250 printf("\n");
251 return;
254 blob_data = blob->data;
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]);
261 printf("\n");
263 drmModeFreePropertyBlob(blob);
266 static const char *modifier_to_string(uint64_t modifier)
268 static char mod_string[4096];
270 char *modifier_name = drmGetFormatModifierName(modifier);
271 char *vendor_name = drmGetFormatModifierVendor(modifier);
272 memset(mod_string, 0x00, sizeof(mod_string));
274 if (!modifier_name) {
275 if (vendor_name)
276 snprintf(mod_string, sizeof(mod_string), "%s_%s",
277 vendor_name, "UNKNOWN_MODIFIER");
278 else
279 snprintf(mod_string, sizeof(mod_string), "%s_%s",
280 "UNKNOWN_VENDOR", "UNKNOWN_MODIFIER");
281 /* safe, as free is no-op for NULL */
282 free(vendor_name);
283 return mod_string;
286 if (modifier == DRM_FORMAT_MOD_LINEAR) {
287 snprintf(mod_string, sizeof(mod_string), "%s", modifier_name);
288 free(modifier_name);
289 free(vendor_name);
290 return mod_string;
293 snprintf(mod_string, sizeof(mod_string), "%s_%s",
294 vendor_name, modifier_name);
296 free(modifier_name);
297 free(vendor_name);
298 return mod_string;
301 static void dump_in_formats(struct device *dev, uint32_t blob_id)
303 uint32_t i, j;
304 drmModePropertyBlobPtr blob;
305 struct drm_format_modifier_blob *header;
306 uint32_t *formats;
307 struct drm_format_modifier *modifiers;
309 printf("\t\tin_formats blob decoded:\n");
310 blob = drmModeGetPropertyBlob(dev->fd, blob_id);
311 if (!blob) {
312 printf("\n");
313 return;
316 header = blob->data;
317 formats = (uint32_t *) ((char *) header + header->formats_offset);
318 modifiers = (struct drm_format_modifier *)
319 ((char *) header + header->modifiers_offset);
321 for (i = 0; i < header->count_formats; i++) {
322 printf("\t\t\t");
323 dump_fourcc(formats[i]);
324 printf(": ");
325 for (j = 0; j < header->count_modifiers; j++) {
326 uint64_t mask = 1ULL << i;
327 if (modifiers[j].formats & mask)
328 printf(" %s", modifier_to_string(modifiers[j].modifier));
330 printf("\n");
333 drmModeFreePropertyBlob(blob);
336 static void dump_prop(struct device *dev, drmModePropertyPtr prop,
337 uint32_t prop_id, uint64_t value)
339 int i;
340 printf("\t%d", prop_id);
341 if (!prop) {
342 printf("\n");
343 return;
346 printf(" %s:\n", prop->name);
348 printf("\t\tflags:");
349 if (prop->flags & DRM_MODE_PROP_PENDING)
350 printf(" pending");
351 if (prop->flags & DRM_MODE_PROP_IMMUTABLE)
352 printf(" immutable");
353 if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE))
354 printf(" signed range");
355 if (drm_property_type_is(prop, DRM_MODE_PROP_RANGE))
356 printf(" range");
357 if (drm_property_type_is(prop, DRM_MODE_PROP_ENUM))
358 printf(" enum");
359 if (drm_property_type_is(prop, DRM_MODE_PROP_BITMASK))
360 printf(" bitmask");
361 if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB))
362 printf(" blob");
363 if (drm_property_type_is(prop, DRM_MODE_PROP_OBJECT))
364 printf(" object");
365 printf("\n");
367 if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE)) {
368 printf("\t\tvalues:");
369 for (i = 0; i < prop->count_values; i++)
370 printf(" %"PRId64, U642I64(prop->values[i]));
371 printf("\n");
374 if (drm_property_type_is(prop, DRM_MODE_PROP_RANGE)) {
375 printf("\t\tvalues:");
376 for (i = 0; i < prop->count_values; i++)
377 printf(" %"PRIu64, prop->values[i]);
378 printf("\n");
381 if (drm_property_type_is(prop, DRM_MODE_PROP_ENUM)) {
382 printf("\t\tenums:");
383 for (i = 0; i < prop->count_enums; i++)
384 printf(" %s=%llu", prop->enums[i].name,
385 prop->enums[i].value);
386 printf("\n");
387 } else if (drm_property_type_is(prop, DRM_MODE_PROP_BITMASK)) {
388 printf("\t\tvalues:");
389 for (i = 0; i < prop->count_enums; i++)
390 printf(" %s=0x%llx", prop->enums[i].name,
391 (1LL << prop->enums[i].value));
392 printf("\n");
393 } else {
394 assert(prop->count_enums == 0);
397 if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB)) {
398 printf("\t\tblobs:\n");
399 for (i = 0; i < prop->count_blobs; i++)
400 dump_blob(dev, prop->blob_ids[i]);
401 printf("\n");
402 } else {
403 assert(prop->count_blobs == 0);
406 printf("\t\tvalue:");
407 if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB))
408 dump_blob(dev, value);
409 else if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE))
410 printf(" %"PRId64"\n", value);
411 else
412 printf(" %"PRIu64"\n", value);
414 if (strcmp(prop->name, "IN_FORMATS") == 0)
415 dump_in_formats(dev, value);
418 static void dump_connectors(struct device *dev)
420 int i, j;
422 printf("Connectors:\n");
423 printf("id\tencoder\tstatus\t\tname\t\tsize (mm)\tmodes\tencoders\n");
424 for (i = 0; i < dev->resources->count_connectors; i++) {
425 struct connector *_connector = &dev->resources->connectors[i];
426 drmModeConnector *connector = _connector->connector;
427 if (!connector)
428 continue;
430 printf("%d\t%d\t%s\t%-15s\t%dx%d\t\t%d\t",
431 connector->connector_id,
432 connector->encoder_id,
433 util_lookup_connector_status_name(connector->connection),
434 _connector->name,
435 connector->mmWidth, connector->mmHeight,
436 connector->count_modes);
438 for (j = 0; j < connector->count_encoders; j++)
439 printf("%s%d", j > 0 ? ", " : "", connector->encoders[j]);
440 printf("\n");
442 if (connector->count_modes) {
443 printf(" modes:\n");
444 printf("\tindex name refresh (Hz) hdisp hss hse htot vdisp "
445 "vss vse vtot\n");
446 for (j = 0; j < connector->count_modes; j++)
447 dump_mode(&connector->modes[j], j);
450 if (_connector->props) {
451 printf(" props:\n");
452 for (j = 0; j < (int)_connector->props->count_props; j++)
453 dump_prop(dev, _connector->props_info[j],
454 _connector->props->props[j],
455 _connector->props->prop_values[j]);
458 printf("\n");
461 static void dump_crtcs(struct device *dev)
463 int i;
464 uint32_t j;
466 printf("CRTCs:\n");
467 printf("id\tfb\tpos\tsize\n");
468 for (i = 0; i < dev->resources->count_crtcs; i++) {
469 struct crtc *_crtc = &dev->resources->crtcs[i];
470 drmModeCrtc *crtc = _crtc->crtc;
471 if (!crtc)
472 continue;
474 printf("%d\t%d\t(%d,%d)\t(%dx%d)\n",
475 crtc->crtc_id,
476 crtc->buffer_id,
477 crtc->x, crtc->y,
478 crtc->width, crtc->height);
479 dump_mode(&crtc->mode, 0);
481 if (_crtc->props) {
482 printf(" props:\n");
483 for (j = 0; j < _crtc->props->count_props; j++)
484 dump_prop(dev, _crtc->props_info[j],
485 _crtc->props->props[j],
486 _crtc->props->prop_values[j]);
487 } else {
488 printf(" no properties found\n");
491 printf("\n");
494 static void dump_framebuffers(struct device *dev)
496 drmModeFB *fb;
497 int i;
499 printf("Frame buffers:\n");
500 printf("id\tsize\tpitch\n");
501 for (i = 0; i < dev->resources->count_fbs; i++) {
502 fb = dev->resources->fbs[i].fb;
503 if (!fb)
504 continue;
506 printf("%u\t(%ux%u)\t%u\n",
507 fb->fb_id,
508 fb->width, fb->height,
509 fb->pitch);
511 printf("\n");
514 static void dump_planes(struct device *dev)
516 unsigned int i, j;
518 printf("Planes:\n");
519 printf("id\tcrtc\tfb\tCRTC x,y\tx,y\tgamma size\tpossible crtcs\n");
521 for (i = 0; i < dev->resources->count_planes; i++) {
522 struct plane *plane = &dev->resources->planes[i];
523 drmModePlane *ovr = plane->plane;
524 if (!ovr)
525 continue;
527 printf("%d\t%d\t%d\t%d,%d\t\t%d,%d\t%-8d\t0x%08x\n",
528 ovr->plane_id, ovr->crtc_id, ovr->fb_id,
529 ovr->crtc_x, ovr->crtc_y, ovr->x, ovr->y,
530 ovr->gamma_size, ovr->possible_crtcs);
532 if (!ovr->count_formats)
533 continue;
535 printf(" formats:");
536 for (j = 0; j < ovr->count_formats; j++)
537 dump_fourcc(ovr->formats[j]);
538 printf("\n");
540 if (plane->props) {
541 printf(" props:\n");
542 for (j = 0; j < plane->props->count_props; j++)
543 dump_prop(dev, plane->props_info[j],
544 plane->props->props[j],
545 plane->props->prop_values[j]);
546 } else {
547 printf(" no properties found\n");
550 printf("\n");
552 return;
555 static void free_resources(struct resources *res)
557 int i;
559 if (!res)
560 return;
562 #define free_resource(_res, type, Type) \
563 do { \
564 if (!(_res)->type##s) \
565 break; \
566 for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \
567 if (!(_res)->type##s[i].type) \
568 break; \
569 drmModeFree##Type((_res)->type##s[i].type); \
571 free((_res)->type##s); \
572 } while (0)
574 #define free_properties(_res, type) \
575 do { \
576 for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \
577 unsigned int j; \
578 for (j = 0; j < res->type##s[i].props->count_props; ++j)\
579 drmModeFreeProperty(res->type##s[i].props_info[j]);\
580 free(res->type##s[i].props_info); \
581 drmModeFreeObjectProperties(res->type##s[i].props); \
583 } while (0)
585 free_properties(res, plane);
586 free_resource(res, plane, Plane);
588 free_properties(res, connector);
589 free_properties(res, crtc);
591 for (i = 0; i < res->count_connectors; i++)
592 free(res->connectors[i].name);
594 free_resource(res, fb, FB);
595 free_resource(res, connector, Connector);
596 free_resource(res, encoder, Encoder);
597 free_resource(res, crtc, Crtc);
599 free(res);
602 static struct resources *get_resources(struct device *dev)
604 drmModeRes *_res;
605 drmModePlaneRes *plane_res;
606 struct resources *res;
607 int i;
609 res = calloc(1, sizeof(*res));
610 if (res == 0)
611 return NULL;
613 drmSetClientCap(dev->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
615 _res = drmModeGetResources(dev->fd);
616 if (!_res) {
617 fprintf(stderr, "drmModeGetResources failed: %s\n",
618 strerror(errno));
619 free(res);
620 return NULL;
623 res->count_crtcs = _res->count_crtcs;
624 res->count_encoders = _res->count_encoders;
625 res->count_connectors = _res->count_connectors;
626 res->count_fbs = _res->count_fbs;
628 res->crtcs = calloc(res->count_crtcs, sizeof(*res->crtcs));
629 res->encoders = calloc(res->count_encoders, sizeof(*res->encoders));
630 res->connectors = calloc(res->count_connectors, sizeof(*res->connectors));
631 res->fbs = calloc(res->count_fbs, sizeof(*res->fbs));
633 if (!res->crtcs || !res->encoders || !res->connectors || !res->fbs) {
634 drmModeFreeResources(_res);
635 goto error;
638 #define get_resource(_res, __res, type, Type) \
639 do { \
640 for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \
641 uint32_t type##id = (__res)->type##s[i]; \
642 (_res)->type##s[i].type = \
643 drmModeGet##Type(dev->fd, type##id); \
644 if (!(_res)->type##s[i].type) \
645 fprintf(stderr, "could not get %s %i: %s\n", \
646 #type, type##id, \
647 strerror(errno)); \
649 } while (0)
651 get_resource(res, _res, crtc, Crtc);
652 get_resource(res, _res, encoder, Encoder);
653 get_resource(res, _res, connector, Connector);
654 get_resource(res, _res, fb, FB);
656 drmModeFreeResources(_res);
658 /* Set the name of all connectors based on the type name and the per-type ID. */
659 for (i = 0; i < res->count_connectors; i++) {
660 struct connector *connector = &res->connectors[i];
661 drmModeConnector *conn = connector->connector;
662 int num;
664 num = asprintf(&connector->name, "%s-%u",
665 util_lookup_connector_type_name(conn->connector_type),
666 conn->connector_type_id);
667 if (num < 0)
668 goto error;
671 #define get_properties(_res, type, Type) \
672 do { \
673 for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \
674 struct type *obj = &res->type##s[i]; \
675 unsigned int j; \
676 obj->props = \
677 drmModeObjectGetProperties(dev->fd, obj->type->type##_id, \
678 DRM_MODE_OBJECT_##Type); \
679 if (!obj->props) { \
680 fprintf(stderr, \
681 "could not get %s %i properties: %s\n", \
682 #type, obj->type->type##_id, \
683 strerror(errno)); \
684 continue; \
686 obj->props_info = calloc(obj->props->count_props, \
687 sizeof(*obj->props_info)); \
688 if (!obj->props_info) \
689 continue; \
690 for (j = 0; j < obj->props->count_props; ++j) \
691 obj->props_info[j] = \
692 drmModeGetProperty(dev->fd, obj->props->props[j]); \
694 } while (0)
696 get_properties(res, crtc, CRTC);
697 get_properties(res, connector, CONNECTOR);
699 for (i = 0; i < res->count_crtcs; ++i)
700 res->crtcs[i].mode = &res->crtcs[i].crtc->mode;
702 plane_res = drmModeGetPlaneResources(dev->fd);
703 if (!plane_res) {
704 fprintf(stderr, "drmModeGetPlaneResources failed: %s\n",
705 strerror(errno));
706 return res;
709 res->count_planes = plane_res->count_planes;
711 res->planes = calloc(res->count_planes, sizeof(*res->planes));
712 if (!res->planes) {
713 drmModeFreePlaneResources(plane_res);
714 goto error;
717 get_resource(res, plane_res, plane, Plane);
718 drmModeFreePlaneResources(plane_res);
719 get_properties(res, plane, PLANE);
721 return res;
723 error:
724 free_resources(res);
725 return NULL;
728 static struct crtc *get_crtc_by_id(struct device *dev, uint32_t id)
730 int i;
732 for (i = 0; i < dev->resources->count_crtcs; ++i) {
733 drmModeCrtc *crtc = dev->resources->crtcs[i].crtc;
734 if (crtc && crtc->crtc_id == id)
735 return &dev->resources->crtcs[i];
738 return NULL;
741 static uint32_t get_crtc_mask(struct device *dev, struct crtc *crtc)
743 unsigned int i;
745 for (i = 0; i < (unsigned int)dev->resources->count_crtcs; i++) {
746 if (crtc->crtc->crtc_id == dev->resources->crtcs[i].crtc->crtc_id)
747 return 1 << i;
749 /* Unreachable: crtc->crtc is one of resources->crtcs[] */
750 /* Don't return zero or static analysers will complain */
751 abort();
752 return 0;
755 static drmModeConnector *get_connector_by_name(struct device *dev, const char *name)
757 struct connector *connector;
758 int i;
760 for (i = 0; i < dev->resources->count_connectors; i++) {
761 connector = &dev->resources->connectors[i];
763 if (strcmp(connector->name, name) == 0)
764 return connector->connector;
767 return NULL;
770 static drmModeConnector *get_connector_by_id(struct device *dev, uint32_t id)
772 drmModeConnector *connector;
773 int i;
775 for (i = 0; i < dev->resources->count_connectors; i++) {
776 connector = dev->resources->connectors[i].connector;
777 if (connector && connector->connector_id == id)
778 return connector;
781 return NULL;
784 static drmModeEncoder *get_encoder_by_id(struct device *dev, uint32_t id)
786 drmModeEncoder *encoder;
787 int i;
789 for (i = 0; i < dev->resources->count_encoders; i++) {
790 encoder = dev->resources->encoders[i].encoder;
791 if (encoder && encoder->encoder_id == id)
792 return encoder;
795 return NULL;
798 /* -----------------------------------------------------------------------------
799 * Pipes and planes
803 * Mode setting with the kernel interfaces is a bit of a chore.
804 * First you have to find the connector in question and make sure the
805 * requested mode is available.
806 * Then you need to find the encoder attached to that connector so you
807 * can bind it with a free crtc.
809 struct pipe_arg {
810 const char **cons;
811 uint32_t *con_ids;
812 unsigned int num_cons;
813 uint32_t crtc_id;
814 char mode_str[64];
815 char format_str[5];
816 float vrefresh;
817 unsigned int fourcc;
818 drmModeModeInfo *mode;
819 struct crtc *crtc;
820 unsigned int fb_id[2], current_fb_id;
821 struct timeval start;
823 int swap_count;
826 struct plane_arg {
827 uint32_t plane_id; /* the id of plane to use */
828 uint32_t crtc_id; /* the id of CRTC to bind to */
829 bool has_position;
830 int32_t x, y;
831 uint32_t w, h;
832 double scale;
833 unsigned int fb_id;
834 unsigned int old_fb_id;
835 struct bo *bo;
836 struct bo *old_bo;
837 char format_str[5]; /* need to leave room for terminating \0 */
838 unsigned int fourcc;
841 static drmModeModeInfo *
842 connector_find_mode(struct device *dev, uint32_t con_id, const char *mode_str,
843 const float vrefresh)
845 drmModeConnector *connector;
846 drmModeModeInfo *mode;
847 int i;
849 connector = get_connector_by_id(dev, con_id);
850 if (!connector || !connector->count_modes)
851 return NULL;
853 /* Pick by Index */
854 if (mode_str[0] == '#') {
855 int index = atoi(mode_str + 1);
857 if (index >= connector->count_modes || index < 0)
858 return NULL;
859 return &connector->modes[index];
862 /* Pick by Name */
863 for (i = 0; i < connector->count_modes; i++) {
864 mode = &connector->modes[i];
865 if (!strcmp(mode->name, mode_str)) {
866 /* If the vertical refresh frequency is not specified
867 * then return the first mode that match with the name.
868 * Else, return the mode that match the name and
869 * the specified vertical refresh frequency.
871 if (vrefresh == 0)
872 return mode;
873 else if (fabs(mode_vrefresh(mode) - vrefresh) < 0.005)
874 return mode;
878 return NULL;
881 static struct crtc *pipe_find_crtc(struct device *dev, struct pipe_arg *pipe)
883 uint32_t possible_crtcs = ~0;
884 uint32_t active_crtcs = 0;
885 unsigned int crtc_idx;
886 unsigned int i;
887 int j;
889 for (i = 0; i < pipe->num_cons; ++i) {
890 uint32_t crtcs_for_connector = 0;
891 drmModeConnector *connector;
892 drmModeEncoder *encoder;
893 struct crtc *crtc;
895 connector = get_connector_by_id(dev, pipe->con_ids[i]);
896 if (!connector)
897 return NULL;
899 for (j = 0; j < connector->count_encoders; ++j) {
900 encoder = get_encoder_by_id(dev, connector->encoders[j]);
901 if (!encoder)
902 continue;
904 crtcs_for_connector |= encoder->possible_crtcs;
905 crtc = get_crtc_by_id(dev, encoder->crtc_id);
906 if (!crtc)
907 continue;
908 active_crtcs |= get_crtc_mask(dev, crtc);
911 possible_crtcs &= crtcs_for_connector;
914 if (!possible_crtcs)
915 return NULL;
917 /* Return the first possible and active CRTC if one exists, or the first
918 * possible CRTC otherwise.
920 if (possible_crtcs & active_crtcs)
921 crtc_idx = ffs(possible_crtcs & active_crtcs);
922 else
923 crtc_idx = ffs(possible_crtcs);
925 return &dev->resources->crtcs[crtc_idx - 1];
928 static int pipe_find_crtc_and_mode(struct device *dev, struct pipe_arg *pipe)
930 drmModeModeInfo *mode = NULL;
931 int i;
933 pipe->mode = NULL;
935 for (i = 0; i < (int)pipe->num_cons; i++) {
936 mode = connector_find_mode(dev, pipe->con_ids[i],
937 pipe->mode_str, pipe->vrefresh);
938 if (mode == NULL) {
939 if (pipe->vrefresh)
940 fprintf(stderr,
941 "failed to find mode "
942 "\"%s-%.2fHz\" for connector %s\n",
943 pipe->mode_str, pipe->vrefresh, pipe->cons[i]);
944 else
945 fprintf(stderr,
946 "failed to find mode \"%s\" for connector %s\n",
947 pipe->mode_str, pipe->cons[i]);
948 return -EINVAL;
952 /* If the CRTC ID was specified, get the corresponding CRTC. Otherwise
953 * locate a CRTC that can be attached to all the connectors.
955 if (pipe->crtc_id != (uint32_t)-1) {
956 pipe->crtc = get_crtc_by_id(dev, pipe->crtc_id);
957 } else {
958 pipe->crtc = pipe_find_crtc(dev, pipe);
959 pipe->crtc_id = pipe->crtc->crtc->crtc_id;
962 if (!pipe->crtc) {
963 fprintf(stderr, "failed to find CRTC for pipe\n");
964 return -EINVAL;
967 pipe->mode = mode;
968 pipe->crtc->mode = mode;
970 return 0;
973 /* -----------------------------------------------------------------------------
974 * Properties
977 struct property_arg {
978 uint32_t obj_id;
979 uint32_t obj_type;
980 char name[DRM_PROP_NAME_LEN+1];
981 uint32_t prop_id;
982 uint64_t value;
983 bool optional;
986 static bool set_property(struct device *dev, struct property_arg *p)
988 drmModeObjectProperties *props = NULL;
989 drmModePropertyRes **props_info = NULL;
990 const char *obj_type;
991 int ret;
992 int i;
994 p->obj_type = 0;
995 p->prop_id = 0;
997 #define find_object(_res, type, Type) \
998 do { \
999 for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \
1000 struct type *obj = &(_res)->type##s[i]; \
1001 if (obj->type->type##_id != p->obj_id) \
1002 continue; \
1003 p->obj_type = DRM_MODE_OBJECT_##Type; \
1004 obj_type = #Type; \
1005 props = obj->props; \
1006 props_info = obj->props_info; \
1008 } while(0) \
1010 find_object(dev->resources, crtc, CRTC);
1011 if (p->obj_type == 0)
1012 find_object(dev->resources, connector, CONNECTOR);
1013 if (p->obj_type == 0)
1014 find_object(dev->resources, plane, PLANE);
1015 if (p->obj_type == 0) {
1016 fprintf(stderr, "Object %i not found, can't set property\n",
1017 p->obj_id);
1018 return false;
1021 if (!props) {
1022 fprintf(stderr, "%s %i has no properties\n",
1023 obj_type, p->obj_id);
1024 return false;
1027 for (i = 0; i < (int)props->count_props; ++i) {
1028 if (!props_info[i])
1029 continue;
1030 if (strcmp(props_info[i]->name, p->name) == 0)
1031 break;
1034 if (i == (int)props->count_props) {
1035 if (!p->optional)
1036 fprintf(stderr, "%s %i has no %s property\n",
1037 obj_type, p->obj_id, p->name);
1038 return false;
1041 p->prop_id = props->props[i];
1043 if (!dev->use_atomic)
1044 ret = drmModeObjectSetProperty(dev->fd, p->obj_id, p->obj_type,
1045 p->prop_id, p->value);
1046 else
1047 ret = drmModeAtomicAddProperty(dev->req, p->obj_id, p->prop_id, p->value);
1049 if (ret < 0)
1050 fprintf(stderr, "failed to set %s %i property %s to %" PRIu64 ": %s\n",
1051 obj_type, p->obj_id, p->name, p->value, strerror(errno));
1053 return true;
1056 /* -------------------------------------------------------------------------- */
1058 static void
1059 page_flip_handler(int fd, unsigned int frame,
1060 unsigned int sec, unsigned int usec, void *data)
1062 struct pipe_arg *pipe;
1063 unsigned int new_fb_id;
1064 struct timeval end;
1065 double t;
1067 pipe = data;
1068 if (pipe->current_fb_id == pipe->fb_id[0])
1069 new_fb_id = pipe->fb_id[1];
1070 else
1071 new_fb_id = pipe->fb_id[0];
1073 drmModePageFlip(fd, pipe->crtc_id, new_fb_id,
1074 DRM_MODE_PAGE_FLIP_EVENT, pipe);
1075 pipe->current_fb_id = new_fb_id;
1076 pipe->swap_count++;
1077 if (pipe->swap_count == 60) {
1078 gettimeofday(&end, NULL);
1079 t = end.tv_sec + end.tv_usec * 1e-6 -
1080 (pipe->start.tv_sec + pipe->start.tv_usec * 1e-6);
1081 fprintf(stderr, "freq: %.02fHz\n", pipe->swap_count / t);
1082 pipe->swap_count = 0;
1083 pipe->start = end;
1087 static bool format_support(const drmModePlanePtr ovr, uint32_t fmt)
1089 unsigned int i;
1091 for (i = 0; i < ovr->count_formats; ++i) {
1092 if (ovr->formats[i] == fmt)
1093 return true;
1096 return false;
1099 static void add_property(struct device *dev, uint32_t obj_id,
1100 const char *name, uint64_t value)
1102 struct property_arg p;
1104 p.obj_id = obj_id;
1105 strcpy(p.name, name);
1106 p.value = value;
1108 set_property(dev, &p);
1111 static bool add_property_optional(struct device *dev, uint32_t obj_id,
1112 const char *name, uint64_t value)
1114 struct property_arg p;
1116 p.obj_id = obj_id;
1117 strcpy(p.name, name);
1118 p.value = value;
1119 p.optional = true;
1121 return set_property(dev, &p);
1124 static void set_gamma(struct device *dev, unsigned crtc_id, unsigned fourcc)
1126 unsigned blob_id = 0;
1127 /* TODO: support 1024-sized LUTs, when the use-case arises */
1128 struct drm_color_lut gamma_lut[256];
1129 int i, ret;
1131 if (fourcc == DRM_FORMAT_C8) {
1132 /* TODO: Add C8 support for more patterns */
1133 util_smpte_c8_gamma(256, gamma_lut);
1134 drmModeCreatePropertyBlob(dev->fd, gamma_lut, sizeof(gamma_lut), &blob_id);
1135 } else {
1136 for (i = 0; i < 256; i++) {
1137 gamma_lut[i].red =
1138 gamma_lut[i].green =
1139 gamma_lut[i].blue = i << 8;
1143 add_property_optional(dev, crtc_id, "DEGAMMA_LUT", 0);
1144 add_property_optional(dev, crtc_id, "CTM", 0);
1145 if (!add_property_optional(dev, crtc_id, "GAMMA_LUT", blob_id)) {
1146 uint16_t r[256], g[256], b[256];
1148 for (i = 0; i < 256; i++) {
1149 r[i] = gamma_lut[i].red;
1150 g[i] = gamma_lut[i].green;
1151 b[i] = gamma_lut[i].blue;
1154 ret = drmModeCrtcSetGamma(dev->fd, crtc_id, 256, r, g, b);
1155 if (ret)
1156 fprintf(stderr, "failed to set gamma: %s\n", strerror(errno));
1160 static int
1161 bo_fb_create(int fd, unsigned int fourcc, const uint32_t w, const uint32_t h,
1162 enum util_fill_pattern pat, struct bo **out_bo, unsigned int *out_fb_id)
1164 uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
1165 struct bo *bo;
1166 unsigned int fb_id;
1168 bo = bo_create(fd, fourcc, w, h, handles, pitches, offsets, pat);
1170 if (bo == NULL)
1171 return -1;
1173 if (drmModeAddFB2(fd, w, h, fourcc, handles, pitches, offsets, &fb_id, 0)) {
1174 fprintf(stderr, "failed to add fb (%ux%u): %s\n", w, h, strerror(errno));
1175 bo_destroy(bo);
1176 return -1;
1178 *out_bo = bo;
1179 *out_fb_id = fb_id;
1180 return 0;
1183 static int atomic_set_plane(struct device *dev, struct plane_arg *p,
1184 int pattern, bool update)
1186 struct bo *plane_bo;
1187 int crtc_x, crtc_y, crtc_w, crtc_h;
1188 struct crtc *crtc = NULL;
1189 unsigned int old_fb_id;
1191 /* Find an unused plane which can be connected to our CRTC. Find the
1192 * CRTC index first, then iterate over available planes.
1194 crtc = get_crtc_by_id(dev, p->crtc_id);
1195 if (!crtc) {
1196 fprintf(stderr, "CRTC %u not found\n", p->crtc_id);
1197 return -1;
1200 if (!update)
1201 fprintf(stderr, "testing %dx%d@%s on plane %u, crtc %u\n",
1202 p->w, p->h, p->format_str, p->plane_id, p->crtc_id);
1204 plane_bo = p->old_bo;
1205 p->old_bo = p->bo;
1207 if (!plane_bo) {
1208 if (bo_fb_create(dev->fd, p->fourcc, p->w, p->h,
1209 pattern, &plane_bo, &p->fb_id))
1210 return -1;
1213 p->bo = plane_bo;
1215 old_fb_id = p->fb_id;
1216 p->old_fb_id = old_fb_id;
1218 crtc_w = p->w * p->scale;
1219 crtc_h = p->h * p->scale;
1220 if (!p->has_position) {
1221 /* Default to the middle of the screen */
1222 crtc_x = (crtc->mode->hdisplay - crtc_w) / 2;
1223 crtc_y = (crtc->mode->vdisplay - crtc_h) / 2;
1224 } else {
1225 crtc_x = p->x;
1226 crtc_y = p->y;
1229 add_property(dev, p->plane_id, "FB_ID", p->fb_id);
1230 add_property(dev, p->plane_id, "CRTC_ID", p->crtc_id);
1231 add_property(dev, p->plane_id, "SRC_X", 0);
1232 add_property(dev, p->plane_id, "SRC_Y", 0);
1233 add_property(dev, p->plane_id, "SRC_W", p->w << 16);
1234 add_property(dev, p->plane_id, "SRC_H", p->h << 16);
1235 add_property(dev, p->plane_id, "CRTC_X", crtc_x);
1236 add_property(dev, p->plane_id, "CRTC_Y", crtc_y);
1237 add_property(dev, p->plane_id, "CRTC_W", crtc_w);
1238 add_property(dev, p->plane_id, "CRTC_H", crtc_h);
1240 return 0;
1243 static int set_plane(struct device *dev, struct plane_arg *p)
1245 drmModePlane *ovr;
1246 uint32_t plane_id;
1247 int crtc_x, crtc_y, crtc_w, crtc_h;
1248 struct crtc *crtc = NULL;
1249 unsigned int i, crtc_mask;
1251 /* Find an unused plane which can be connected to our CRTC. Find the
1252 * CRTC index first, then iterate over available planes.
1254 crtc = get_crtc_by_id(dev, p->crtc_id);
1255 if (!crtc) {
1256 fprintf(stderr, "CRTC %u not found\n", p->crtc_id);
1257 return -1;
1259 crtc_mask = get_crtc_mask(dev, crtc);
1260 plane_id = p->plane_id;
1262 for (i = 0; i < dev->resources->count_planes; i++) {
1263 ovr = dev->resources->planes[i].plane;
1264 if (!ovr)
1265 continue;
1267 if (plane_id && plane_id != ovr->plane_id)
1268 continue;
1270 if (!format_support(ovr, p->fourcc))
1271 continue;
1273 if ((ovr->possible_crtcs & crtc_mask) &&
1274 (ovr->crtc_id == 0 || ovr->crtc_id == p->crtc_id)) {
1275 plane_id = ovr->plane_id;
1276 break;
1280 if (i == dev->resources->count_planes) {
1281 fprintf(stderr, "no unused plane available for CRTC %u\n",
1282 p->crtc_id);
1283 return -1;
1286 fprintf(stderr, "testing %dx%d@%s overlay plane %u\n",
1287 p->w, p->h, p->format_str, plane_id);
1289 /* just use single plane format for now.. */
1290 if (bo_fb_create(dev->fd, p->fourcc, p->w, p->h,
1291 secondary_fill, &p->bo, &p->fb_id))
1292 return -1;
1294 crtc_w = p->w * p->scale;
1295 crtc_h = p->h * p->scale;
1296 if (!p->has_position) {
1297 /* Default to the middle of the screen */
1298 crtc_x = (crtc->mode->hdisplay - crtc_w) / 2;
1299 crtc_y = (crtc->mode->vdisplay - crtc_h) / 2;
1300 } else {
1301 crtc_x = p->x;
1302 crtc_y = p->y;
1305 /* note src coords (last 4 args) are in Q16 format */
1306 if (drmModeSetPlane(dev->fd, plane_id, p->crtc_id, p->fb_id,
1307 0, crtc_x, crtc_y, crtc_w, crtc_h,
1308 0, 0, p->w << 16, p->h << 16)) {
1309 fprintf(stderr, "failed to enable plane: %s\n",
1310 strerror(errno));
1311 return -1;
1314 ovr->crtc_id = p->crtc_id;
1316 return 0;
1319 static void atomic_set_planes(struct device *dev, struct plane_arg *p,
1320 unsigned int count, bool update)
1322 unsigned int i, pattern = primary_fill;
1324 /* set up planes */
1325 for (i = 0; i < count; i++) {
1326 if (i > 0)
1327 pattern = secondary_fill;
1328 else
1329 set_gamma(dev, p[i].crtc_id, p[i].fourcc);
1331 if (atomic_set_plane(dev, &p[i], pattern, update))
1332 return;
1336 static void
1337 atomic_test_page_flip(struct device *dev, struct pipe_arg *pipe_args,
1338 struct plane_arg *plane_args, unsigned int plane_count)
1340 int ret;
1342 gettimeofday(&pipe_args->start, NULL);
1343 pipe_args->swap_count = 0;
1345 while (true) {
1346 drmModeAtomicFree(dev->req);
1347 dev->req = drmModeAtomicAlloc();
1348 atomic_set_planes(dev, plane_args, plane_count, true);
1350 ret = drmModeAtomicCommit(dev->fd, dev->req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
1351 if (ret) {
1352 fprintf(stderr, "Atomic Commit failed [2]\n");
1353 return;
1356 pipe_args->swap_count++;
1357 if (pipe_args->swap_count == 60) {
1358 struct timeval end;
1359 double t;
1361 gettimeofday(&end, NULL);
1362 t = end.tv_sec + end.tv_usec * 1e-6 -
1363 (pipe_args->start.tv_sec + pipe_args->start.tv_usec * 1e-6);
1364 fprintf(stderr, "freq: %.02fHz\n", pipe_args->swap_count / t);
1365 pipe_args->swap_count = 0;
1366 pipe_args->start = end;
1371 static void atomic_clear_planes(struct device *dev, struct plane_arg *p, unsigned int count)
1373 unsigned int i;
1375 for (i = 0; i < count; i++) {
1376 add_property(dev, p[i].plane_id, "FB_ID", 0);
1377 add_property(dev, p[i].plane_id, "CRTC_ID", 0);
1378 add_property(dev, p[i].plane_id, "SRC_X", 0);
1379 add_property(dev, p[i].plane_id, "SRC_Y", 0);
1380 add_property(dev, p[i].plane_id, "SRC_W", 0);
1381 add_property(dev, p[i].plane_id, "SRC_H", 0);
1382 add_property(dev, p[i].plane_id, "CRTC_X", 0);
1383 add_property(dev, p[i].plane_id, "CRTC_Y", 0);
1384 add_property(dev, p[i].plane_id, "CRTC_W", 0);
1385 add_property(dev, p[i].plane_id, "CRTC_H", 0);
1389 static void atomic_clear_FB(struct device *dev, struct plane_arg *p, unsigned int count)
1391 unsigned int i;
1393 for (i = 0; i < count; i++) {
1394 if (p[i].fb_id) {
1395 drmModeRmFB(dev->fd, p[i].fb_id);
1396 p[i].fb_id = 0;
1398 if (p[i].old_fb_id) {
1399 drmModeRmFB(dev->fd, p[i].old_fb_id);
1400 p[i].old_fb_id = 0;
1402 if (p[i].bo) {
1403 bo_destroy(p[i].bo);
1404 p[i].bo = NULL;
1406 if (p[i].old_bo) {
1407 bo_destroy(p[i].old_bo);
1408 p[i].old_bo = NULL;
1414 static void clear_planes(struct device *dev, struct plane_arg *p, unsigned int count)
1416 unsigned int i;
1418 for (i = 0; i < count; i++) {
1419 if (p[i].fb_id)
1420 drmModeRmFB(dev->fd, p[i].fb_id);
1421 if (p[i].bo)
1422 bo_destroy(p[i].bo);
1426 static int pipe_resolve_connectors(struct device *dev, struct pipe_arg *pipe)
1428 drmModeConnector *connector;
1429 unsigned int i;
1430 uint32_t id;
1431 char *endp;
1433 for (i = 0; i < pipe->num_cons; i++) {
1434 id = strtoul(pipe->cons[i], &endp, 10);
1435 if (endp == pipe->cons[i]) {
1436 connector = get_connector_by_name(dev, pipe->cons[i]);
1437 if (!connector) {
1438 fprintf(stderr, "no connector named '%s'\n",
1439 pipe->cons[i]);
1440 return -ENODEV;
1443 id = connector->connector_id;
1446 pipe->con_ids[i] = id;
1449 return 0;
1452 static int pipe_attempt_connector(struct device *dev, drmModeConnector *con,
1453 struct pipe_arg *pipe)
1455 char *con_str;
1456 int i;
1458 con_str = calloc(8, sizeof(char));
1459 if (!con_str)
1460 return -1;
1462 sprintf(con_str, "%d", con->connector_id);
1463 strcpy(pipe->format_str, "XR24");
1464 pipe->fourcc = util_format_fourcc(pipe->format_str);
1465 pipe->num_cons = 1;
1466 pipe->con_ids = calloc(1, sizeof(*pipe->con_ids));
1467 pipe->cons = calloc(1, sizeof(*pipe->cons));
1469 if (!pipe->con_ids || !pipe->cons)
1470 goto free_con_str;
1472 pipe->con_ids[0] = con->connector_id;
1473 pipe->cons[0] = (const char*)con_str;
1475 pipe->crtc = pipe_find_crtc(dev, pipe);
1476 if (!pipe->crtc)
1477 goto free_all;
1479 pipe->crtc_id = pipe->crtc->crtc->crtc_id;
1481 /* Return the first mode if no preferred. */
1482 pipe->mode = &con->modes[0];
1484 for (i = 0; i < con->count_modes; i++) {
1485 drmModeModeInfo *current_mode = &con->modes[i];
1487 if (current_mode->type & DRM_MODE_TYPE_PREFERRED) {
1488 pipe->mode = current_mode;
1489 break;
1493 sprintf(pipe->mode_str, "%dx%d", pipe->mode->hdisplay, pipe->mode->vdisplay);
1495 return 0;
1497 free_all:
1498 free(pipe->cons);
1499 free(pipe->con_ids);
1500 free_con_str:
1501 free(con_str);
1502 return -1;
1505 static int pipe_find_preferred(struct device *dev, struct pipe_arg **out_pipes)
1507 struct pipe_arg *pipes;
1508 struct resources *res = dev->resources;
1509 drmModeConnector *con = NULL;
1510 int i, connected = 0, attempted = 0;
1512 for (i = 0; i < res->count_connectors; i++) {
1513 con = res->connectors[i].connector;
1514 if (!con || con->connection != DRM_MODE_CONNECTED)
1515 continue;
1516 connected++;
1518 if (!connected) {
1519 printf("no connected connector!\n");
1520 return 0;
1523 pipes = calloc(connected, sizeof(struct pipe_arg));
1524 if (!pipes)
1525 return 0;
1527 for (i = 0; i < res->count_connectors && attempted < connected; i++) {
1528 con = res->connectors[i].connector;
1529 if (!con || con->connection != DRM_MODE_CONNECTED)
1530 continue;
1532 if (pipe_attempt_connector(dev, con, &pipes[attempted]) < 0) {
1533 printf("failed fetching preferred mode for connector\n");
1534 continue;
1536 attempted++;
1539 *out_pipes = pipes;
1540 return attempted;
1543 static struct plane *get_primary_plane_by_crtc(struct device *dev, struct crtc *crtc)
1545 unsigned int i;
1547 for (i = 0; i < dev->resources->count_planes; i++) {
1548 struct plane *plane = &dev->resources->planes[i];
1549 drmModePlane *ovr = plane->plane;
1550 if (!ovr)
1551 continue;
1553 // XXX: add is_primary_plane and (?) format checks
1555 if (ovr->possible_crtcs & get_crtc_mask(dev, crtc))
1556 return plane;
1558 return NULL;
1561 static void set_mode(struct device *dev, struct pipe_arg *pipes, unsigned int count)
1563 unsigned int i, j;
1564 int ret, x = 0;
1565 int preferred = count == 0;
1567 for (i = 0; i < count; i++) {
1568 struct pipe_arg *pipe = &pipes[i];
1570 ret = pipe_resolve_connectors(dev, pipe);
1571 if (ret < 0)
1572 return;
1574 ret = pipe_find_crtc_and_mode(dev, pipe);
1575 if (ret < 0)
1576 continue;
1578 if (preferred) {
1579 struct pipe_arg *pipe_args;
1581 count = pipe_find_preferred(dev, &pipe_args);
1582 if (!count) {
1583 fprintf(stderr, "can't find any preferred connector/mode.\n");
1584 return;
1586 pipes = pipe_args;
1589 if (!dev->use_atomic) {
1590 for (i = 0; i < count; i++) {
1591 struct pipe_arg *pipe = &pipes[i];
1593 if (pipe->mode == NULL)
1594 continue;
1596 if (!preferred) {
1597 dev->mode.width += pipe->mode->hdisplay;
1598 if (dev->mode.height < pipe->mode->vdisplay)
1599 dev->mode.height = pipe->mode->vdisplay;
1600 } else {
1601 /* XXX: Use a clone mode, more like atomic. We could do per
1602 * connector bo/fb, so we don't have the stretched image.
1604 if (dev->mode.width < pipe->mode->hdisplay)
1605 dev->mode.width = pipe->mode->hdisplay;
1606 if (dev->mode.height < pipe->mode->vdisplay)
1607 dev->mode.height = pipe->mode->vdisplay;
1611 if (bo_fb_create(dev->fd, pipes[0].fourcc, dev->mode.width, dev->mode.height,
1612 primary_fill, &dev->mode.bo, &dev->mode.fb_id))
1613 return;
1616 for (i = 0; i < count; i++) {
1617 struct pipe_arg *pipe = &pipes[i];
1618 uint32_t blob_id;
1620 if (pipe->mode == NULL)
1621 continue;
1623 printf("setting mode %s-%.2fHz on connectors ",
1624 pipe->mode->name, mode_vrefresh(pipe->mode));
1625 for (j = 0; j < pipe->num_cons; ++j) {
1626 printf("%s, ", pipe->cons[j]);
1627 if (dev->use_atomic)
1628 add_property(dev, pipe->con_ids[j], "CRTC_ID", pipe->crtc_id);
1630 printf("crtc %d\n", pipe->crtc_id);
1632 if (!dev->use_atomic) {
1633 ret = drmModeSetCrtc(dev->fd, pipe->crtc_id, dev->mode.fb_id,
1634 x, 0, pipe->con_ids, pipe->num_cons,
1635 pipe->mode);
1637 /* XXX: Actually check if this is needed */
1638 drmModeDirtyFB(dev->fd, dev->mode.fb_id, NULL, 0);
1640 if (!preferred)
1641 x += pipe->mode->hdisplay;
1643 if (ret) {
1644 fprintf(stderr, "failed to set mode: %s\n", strerror(errno));
1645 return;
1648 set_gamma(dev, pipe->crtc_id, pipe->fourcc);
1649 } else {
1650 drmModeCreatePropertyBlob(dev->fd, pipe->mode, sizeof(*pipe->mode), &blob_id);
1651 add_property(dev, pipe->crtc_id, "MODE_ID", blob_id);
1652 add_property(dev, pipe->crtc_id, "ACTIVE", 1);
1654 /* By default atomic modeset does not set a primary plane, shrug */
1655 if (preferred) {
1656 struct plane *plane = get_primary_plane_by_crtc(dev, pipe->crtc);
1657 struct plane_arg plane_args = {
1658 .plane_id = plane->plane->plane_id,
1659 .crtc_id = pipe->crtc_id,
1660 .w = pipe->mode->hdisplay,
1661 .h = pipe->mode->vdisplay,
1662 .scale = 1.0,
1663 .format_str = "XR24",
1664 .fourcc = util_format_fourcc(pipe->format_str),
1667 atomic_set_planes(dev, &plane_args, 1, false);
1673 static void atomic_clear_mode(struct device *dev, struct pipe_arg *pipes, unsigned int count)
1675 unsigned int i;
1676 unsigned int j;
1678 for (i = 0; i < count; i++) {
1679 struct pipe_arg *pipe = &pipes[i];
1681 if (pipe->mode == NULL)
1682 continue;
1684 for (j = 0; j < pipe->num_cons; ++j)
1685 add_property(dev, pipe->con_ids[j], "CRTC_ID",0);
1687 add_property(dev, pipe->crtc_id, "MODE_ID", 0);
1688 add_property(dev, pipe->crtc_id, "ACTIVE", 0);
1692 static void clear_mode(struct device *dev)
1694 if (dev->mode.fb_id)
1695 drmModeRmFB(dev->fd, dev->mode.fb_id);
1696 if (dev->mode.bo)
1697 bo_destroy(dev->mode.bo);
1700 static void set_planes(struct device *dev, struct plane_arg *p, unsigned int count)
1702 unsigned int i;
1704 /* set up planes/overlays */
1705 for (i = 0; i < count; i++)
1706 if (set_plane(dev, &p[i]))
1707 return;
1710 static void set_cursors(struct device *dev, struct pipe_arg *pipes, unsigned int count)
1712 uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
1713 struct bo *bo;
1714 unsigned int i;
1715 int ret;
1717 /* maybe make cursor width/height configurable some day */
1718 uint32_t cw = 64;
1719 uint32_t ch = 64;
1721 /* create cursor bo.. just using PATTERN_PLAIN as it has
1722 * translucent alpha
1724 bo = bo_create(dev->fd, DRM_FORMAT_ARGB8888, cw, ch, handles, pitches,
1725 offsets, UTIL_PATTERN_PLAIN);
1726 if (bo == NULL)
1727 return;
1729 dev->mode.cursor_bo = bo;
1731 for (i = 0; i < count; i++) {
1732 struct pipe_arg *pipe = &pipes[i];
1733 ret = cursor_init(dev->fd, handles[0],
1734 pipe->crtc_id,
1735 pipe->mode->hdisplay, pipe->mode->vdisplay,
1736 cw, ch);
1737 if (ret) {
1738 fprintf(stderr, "failed to init cursor for CRTC[%u]\n",
1739 pipe->crtc_id);
1740 return;
1744 cursor_start();
1747 static void clear_cursors(struct device *dev)
1749 cursor_stop();
1751 if (dev->mode.cursor_bo)
1752 bo_destroy(dev->mode.cursor_bo);
1755 static void test_page_flip(struct device *dev, struct pipe_arg *pipes, unsigned int count)
1757 unsigned int other_fb_id;
1758 struct bo *other_bo;
1759 drmEventContext evctx;
1760 unsigned int i;
1761 int ret;
1763 if (bo_fb_create(dev->fd, pipes[0].fourcc, dev->mode.width, dev->mode.height,
1764 UTIL_PATTERN_PLAIN, &other_bo, &other_fb_id))
1765 return;
1767 for (i = 0; i < count; i++) {
1768 struct pipe_arg *pipe = &pipes[i];
1770 if (pipe->mode == NULL)
1771 continue;
1773 ret = drmModePageFlip(dev->fd, pipe->crtc_id,
1774 other_fb_id, DRM_MODE_PAGE_FLIP_EVENT,
1775 pipe);
1776 if (ret) {
1777 fprintf(stderr, "failed to page flip: %s\n", strerror(errno));
1778 goto err_rmfb;
1780 gettimeofday(&pipe->start, NULL);
1781 pipe->swap_count = 0;
1782 pipe->fb_id[0] = dev->mode.fb_id;
1783 pipe->fb_id[1] = other_fb_id;
1784 pipe->current_fb_id = other_fb_id;
1787 memset(&evctx, 0, sizeof evctx);
1788 evctx.version = DRM_EVENT_CONTEXT_VERSION;
1789 evctx.vblank_handler = NULL;
1790 evctx.page_flip_handler = page_flip_handler;
1792 while (1) {
1793 #if 0
1794 struct pollfd pfd[2];
1796 pfd[0].fd = 0;
1797 pfd[0].events = POLLIN;
1798 pfd[1].fd = fd;
1799 pfd[1].events = POLLIN;
1801 if (poll(pfd, 2, -1) < 0) {
1802 fprintf(stderr, "poll error\n");
1803 break;
1806 if (pfd[0].revents)
1807 break;
1808 #else
1809 struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 };
1810 fd_set fds;
1812 FD_ZERO(&fds);
1813 FD_SET(0, &fds);
1814 FD_SET(dev->fd, &fds);
1815 ret = select(dev->fd + 1, &fds, NULL, NULL, &timeout);
1817 if (ret <= 0) {
1818 fprintf(stderr, "select timed out or error (ret %d)\n",
1819 ret);
1820 continue;
1821 } else if (FD_ISSET(0, &fds)) {
1822 break;
1824 #endif
1826 drmHandleEvent(dev->fd, &evctx);
1829 err_rmfb:
1830 drmModeRmFB(dev->fd, other_fb_id);
1831 bo_destroy(other_bo);
1834 #define min(a, b) ((a) < (b) ? (a) : (b))
1836 static int parse_connector(struct pipe_arg *pipe, const char *arg)
1838 unsigned int len;
1839 unsigned int i;
1840 const char *p;
1841 char *endp;
1843 pipe->vrefresh = 0;
1844 pipe->crtc_id = (uint32_t)-1;
1845 strcpy(pipe->format_str, "XR24");
1847 /* Count the number of connectors and allocate them. */
1848 pipe->num_cons = 1;
1849 for (p = arg; *p && *p != ':' && *p != '@'; ++p) {
1850 if (*p == ',')
1851 pipe->num_cons++;
1854 pipe->con_ids = calloc(pipe->num_cons, sizeof(*pipe->con_ids));
1855 pipe->cons = calloc(pipe->num_cons, sizeof(*pipe->cons));
1856 if (pipe->con_ids == NULL || pipe->cons == NULL)
1857 return -1;
1859 /* Parse the connectors. */
1860 for (i = 0, p = arg; i < pipe->num_cons; ++i, p = endp + 1) {
1861 endp = strpbrk(p, ",@:");
1862 if (!endp)
1863 break;
1865 pipe->cons[i] = strndup(p, endp - p);
1867 if (*endp != ',')
1868 break;
1871 if (i != pipe->num_cons - 1)
1872 return -1;
1874 /* Parse the remaining parameters. */
1875 if (!endp)
1876 return -1;
1877 if (*endp == '@') {
1878 arg = endp + 1;
1879 pipe->crtc_id = strtoul(arg, &endp, 10);
1881 if (*endp != ':')
1882 return -1;
1884 arg = endp + 1;
1886 /* Search for the vertical refresh or the format. */
1887 p = strpbrk(arg, "-@");
1888 if (p == NULL)
1889 p = arg + strlen(arg);
1890 len = min(sizeof pipe->mode_str - 1, (unsigned int)(p - arg));
1891 strncpy(pipe->mode_str, arg, len);
1892 pipe->mode_str[len] = '\0';
1894 if (*p == '-') {
1895 pipe->vrefresh = strtof(p + 1, &endp);
1896 p = endp;
1899 if (*p == '@') {
1900 strncpy(pipe->format_str, p + 1, 4);
1901 pipe->format_str[4] = '\0';
1904 pipe->fourcc = util_format_fourcc(pipe->format_str);
1905 if (pipe->fourcc == 0) {
1906 fprintf(stderr, "unknown format %s\n", pipe->format_str);
1907 return -1;
1910 return 0;
1913 static int parse_plane(struct plane_arg *plane, const char *p)
1915 char *end;
1917 plane->plane_id = strtoul(p, &end, 10);
1918 if (*end != '@')
1919 return -EINVAL;
1921 p = end + 1;
1922 plane->crtc_id = strtoul(p, &end, 10);
1923 if (*end != ':')
1924 return -EINVAL;
1926 p = end + 1;
1927 plane->w = strtoul(p, &end, 10);
1928 if (*end != 'x')
1929 return -EINVAL;
1931 p = end + 1;
1932 plane->h = strtoul(p, &end, 10);
1934 if (*end == '+' || *end == '-') {
1935 plane->x = strtol(end, &end, 10);
1936 if (*end != '+' && *end != '-')
1937 return -EINVAL;
1938 plane->y = strtol(end, &end, 10);
1940 plane->has_position = true;
1943 if (*end == '*') {
1944 p = end + 1;
1945 plane->scale = strtod(p, &end);
1946 if (plane->scale <= 0.0)
1947 return -EINVAL;
1948 } else {
1949 plane->scale = 1.0;
1952 if (*end == '@') {
1953 strncpy(plane->format_str, end + 1, 4);
1954 plane->format_str[4] = '\0';
1955 } else {
1956 strcpy(plane->format_str, "XR24");
1959 plane->fourcc = util_format_fourcc(plane->format_str);
1960 if (plane->fourcc == 0) {
1961 fprintf(stderr, "unknown format %s\n", plane->format_str);
1962 return -EINVAL;
1965 return 0;
1968 static int parse_property(struct property_arg *p, const char *arg)
1970 if (sscanf(arg, "%d:%32[^:]:%" SCNu64, &p->obj_id, p->name, &p->value) != 3)
1971 return -1;
1973 p->obj_type = 0;
1974 p->name[DRM_PROP_NAME_LEN] = '\0';
1976 return 0;
1979 static void parse_fill_patterns(char *arg)
1981 char *fill = strtok(arg, ",");
1982 if (!fill)
1983 return;
1984 primary_fill = util_pattern_enum(fill);
1985 fill = strtok(NULL, ",");
1986 if (!fill)
1987 return;
1988 secondary_fill = util_pattern_enum(fill);
1991 static void usage(char *name)
1993 fprintf(stderr, "usage: %s [-acDdefMPpsCvrw]\n", name);
1995 fprintf(stderr, "\n Query options:\n\n");
1996 fprintf(stderr, "\t-c\tlist connectors\n");
1997 fprintf(stderr, "\t-e\tlist encoders\n");
1998 fprintf(stderr, "\t-f\tlist framebuffers\n");
1999 fprintf(stderr, "\t-p\tlist CRTCs and planes (pipes)\n");
2001 fprintf(stderr, "\n Test options:\n\n");
2002 fprintf(stderr, "\t-P <plane_id>@<crtc_id>:<w>x<h>[+<x>+<y>][*<scale>][@<format>]\tset a plane\n");
2003 fprintf(stderr, "\t-s <connector_id>[,<connector_id>][@<crtc_id>]:[#<mode index>]<mode>[-<vrefresh>][@<format>]\tset a mode\n");
2004 fprintf(stderr, "\t-C\ttest hw cursor\n");
2005 fprintf(stderr, "\t-v\ttest vsynced page flipping\n");
2006 fprintf(stderr, "\t-r\tset the preferred mode for all connectors\n");
2007 fprintf(stderr, "\t-w <obj_id>:<prop_name>:<value>\tset property\n");
2008 fprintf(stderr, "\t-a \tuse atomic API\n");
2009 fprintf(stderr, "\t-F pattern1,pattern2\tspecify fill patterns\n");
2011 fprintf(stderr, "\n Generic options:\n\n");
2012 fprintf(stderr, "\t-d\tdrop master after mode set\n");
2013 fprintf(stderr, "\t-M module\tuse the given driver\n");
2014 fprintf(stderr, "\t-D device\tuse the given device\n");
2016 fprintf(stderr, "\n\tDefault is to dump all info.\n");
2017 exit(0);
2020 static char optstr[] = "acdD:efF:M:P:ps:Cvrw:";
2022 int main(int argc, char **argv)
2024 struct device dev;
2026 int c;
2027 int encoders = 0, connectors = 0, crtcs = 0, planes = 0, framebuffers = 0;
2028 int drop_master = 0;
2029 int test_vsync = 0;
2030 int test_cursor = 0;
2031 int set_preferred = 0;
2032 int use_atomic = 0;
2033 char *device = NULL;
2034 char *module = NULL;
2035 unsigned int i;
2036 unsigned int count = 0, plane_count = 0;
2037 unsigned int prop_count = 0;
2038 struct pipe_arg *pipe_args = NULL;
2039 struct plane_arg *plane_args = NULL;
2040 struct property_arg *prop_args = NULL;
2041 unsigned int args = 0;
2042 int ret;
2044 memset(&dev, 0, sizeof dev);
2046 opterr = 0;
2047 while ((c = getopt(argc, argv, optstr)) != -1) {
2048 args++;
2050 switch (c) {
2051 case 'a':
2052 use_atomic = 1;
2053 /* Preserve the default behaviour of dumping all information. */
2054 args--;
2055 break;
2056 case 'c':
2057 connectors = 1;
2058 break;
2059 case 'D':
2060 device = optarg;
2061 /* Preserve the default behaviour of dumping all information. */
2062 args--;
2063 break;
2064 case 'd':
2065 drop_master = 1;
2066 break;
2067 case 'e':
2068 encoders = 1;
2069 break;
2070 case 'f':
2071 framebuffers = 1;
2072 break;
2073 case 'F':
2074 parse_fill_patterns(optarg);
2075 break;
2076 case 'M':
2077 module = optarg;
2078 /* Preserve the default behaviour of dumping all information. */
2079 args--;
2080 break;
2081 case 'P':
2082 plane_args = realloc(plane_args,
2083 (plane_count + 1) * sizeof *plane_args);
2084 if (plane_args == NULL) {
2085 fprintf(stderr, "memory allocation failed\n");
2086 return 1;
2088 memset(&plane_args[plane_count], 0, sizeof(*plane_args));
2090 if (parse_plane(&plane_args[plane_count], optarg) < 0)
2091 usage(argv[0]);
2093 plane_count++;
2094 break;
2095 case 'p':
2096 crtcs = 1;
2097 planes = 1;
2098 break;
2099 case 's':
2100 pipe_args = realloc(pipe_args,
2101 (count + 1) * sizeof *pipe_args);
2102 if (pipe_args == NULL) {
2103 fprintf(stderr, "memory allocation failed\n");
2104 return 1;
2106 memset(&pipe_args[count], 0, sizeof(*pipe_args));
2108 if (parse_connector(&pipe_args[count], optarg) < 0)
2109 usage(argv[0]);
2111 count++;
2112 break;
2113 case 'C':
2114 test_cursor = 1;
2115 break;
2116 case 'v':
2117 test_vsync = 1;
2118 break;
2119 case 'r':
2120 set_preferred = 1;
2121 break;
2122 case 'w':
2123 prop_args = realloc(prop_args,
2124 (prop_count + 1) * sizeof *prop_args);
2125 if (prop_args == NULL) {
2126 fprintf(stderr, "memory allocation failed\n");
2127 return 1;
2129 memset(&prop_args[prop_count], 0, sizeof(*prop_args));
2131 if (parse_property(&prop_args[prop_count], optarg) < 0)
2132 usage(argv[0]);
2134 prop_count++;
2135 break;
2136 default:
2137 usage(argv[0]);
2138 break;
2142 /* Dump all the details when no* arguments are provided. */
2143 if (!args)
2144 encoders = connectors = crtcs = planes = framebuffers = 1;
2146 if (test_vsync && !count) {
2147 fprintf(stderr, "page flipping requires at least one -s option.\n");
2148 return -1;
2150 if (set_preferred && count) {
2151 fprintf(stderr, "cannot use -r (preferred) when -s (mode) is set\n");
2152 return -1;
2155 if (set_preferred && plane_count) {
2156 fprintf(stderr, "cannot use -r (preferred) when -P (plane) is set\n");
2157 return -1;
2160 dev.fd = util_open(device, module);
2161 if (dev.fd < 0)
2162 return -1;
2164 if (use_atomic) {
2165 ret = drmSetClientCap(dev.fd, DRM_CLIENT_CAP_ATOMIC, 1);
2166 if (ret) {
2167 fprintf(stderr, "no atomic modesetting support: %s\n", strerror(errno));
2168 drmClose(dev.fd);
2169 return -1;
2173 dev.use_atomic = use_atomic;
2175 dev.resources = get_resources(&dev);
2176 if (!dev.resources) {
2177 drmClose(dev.fd);
2178 return 1;
2181 #define dump_resource(dev, res) if (res) dump_##res(dev)
2183 dump_resource(&dev, encoders);
2184 dump_resource(&dev, connectors);
2185 dump_resource(&dev, crtcs);
2186 dump_resource(&dev, planes);
2187 dump_resource(&dev, framebuffers);
2189 for (i = 0; i < prop_count; ++i)
2190 set_property(&dev, &prop_args[i]);
2192 if (dev.use_atomic) {
2193 dev.req = drmModeAtomicAlloc();
2195 if (set_preferred || (count && plane_count)) {
2196 uint64_t cap = 0;
2198 ret = drmGetCap(dev.fd, DRM_CAP_DUMB_BUFFER, &cap);
2199 if (ret || cap == 0) {
2200 fprintf(stderr, "driver doesn't support the dumb buffer API\n");
2201 return 1;
2204 if (set_preferred || count)
2205 set_mode(&dev, pipe_args, count);
2207 if (plane_count)
2208 atomic_set_planes(&dev, plane_args, plane_count, false);
2210 ret = drmModeAtomicCommit(dev.fd, dev.req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
2211 if (ret) {
2212 fprintf(stderr, "Atomic Commit failed [1]\n");
2213 return 1;
2216 if (test_vsync)
2217 atomic_test_page_flip(&dev, pipe_args, plane_args, plane_count);
2219 if (drop_master)
2220 drmDropMaster(dev.fd);
2222 getchar();
2224 drmModeAtomicFree(dev.req);
2225 dev.req = drmModeAtomicAlloc();
2227 /* XXX: properly teardown the preferred mode/plane state */
2228 if (plane_count)
2229 atomic_clear_planes(&dev, plane_args, plane_count);
2231 if (count)
2232 atomic_clear_mode(&dev, pipe_args, count);
2234 ret = drmModeAtomicCommit(dev.fd, dev.req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
2235 if (ret)
2236 fprintf(stderr, "Atomic Commit failed\n");
2238 if (plane_count)
2239 atomic_clear_FB(&dev, plane_args, plane_count);
2242 drmModeAtomicFree(dev.req);
2243 } else {
2244 if (set_preferred || count || plane_count) {
2245 uint64_t cap = 0;
2247 ret = drmGetCap(dev.fd, DRM_CAP_DUMB_BUFFER, &cap);
2248 if (ret || cap == 0) {
2249 fprintf(stderr, "driver doesn't support the dumb buffer API\n");
2250 return 1;
2253 if (set_preferred || count)
2254 set_mode(&dev, pipe_args, count);
2256 if (plane_count)
2257 set_planes(&dev, plane_args, plane_count);
2259 if (test_cursor)
2260 set_cursors(&dev, pipe_args, count);
2262 if (test_vsync)
2263 test_page_flip(&dev, pipe_args, count);
2265 if (drop_master)
2266 drmDropMaster(dev.fd);
2268 getchar();
2270 if (test_cursor)
2271 clear_cursors(&dev);
2273 if (plane_count)
2274 clear_planes(&dev, plane_args, plane_count);
2276 if (set_preferred || count)
2277 clear_mode(&dev);
2281 free_resources(dev.resources);
2282 drmClose(dev.fd);
2284 return 0;