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
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.
55 #include <sys/select.h>
60 #include "xf86drmMode.h"
61 #include "drm_fourcc.h"
63 #include "util/common.h"
64 #include "util/format.h"
66 #include "util/pattern.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
;
77 drmModeObjectProperties
*props
;
78 drmModePropertyRes
**props_info
;
79 drmModeModeInfo
*mode
;
83 drmModeEncoder
*encoder
;
87 drmModeConnector
*connector
;
88 drmModeObjectProperties
*props
;
89 drmModePropertyRes
**props_info
;
99 drmModeObjectProperties
*props
;
100 drmModePropertyRes
**props_info
;
106 struct encoder
*encoders
;
108 struct connector
*connectors
;
109 int count_connectors
;
112 struct plane
*planes
;
113 uint32_t count_planes
;
119 struct resources
*resources
;
127 struct bo
*cursor_bo
;
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
;
145 den
= mode
->htotal
* mode
->vtotal
;
147 if (mode
->flags
& DRM_MODE_FLAG_INTERLACE
)
149 if (mode
->flags
& DRM_MODE_FLAG_DBLSCAN
)
154 return num
* 1000.00 / den
;
157 #define bit_name_fn(res) \
158 const char * res##_str(int type) { \
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]); \
170 static const char *mode_type_names
[] = {
180 static bit_name_fn(mode_type
)
182 static const char *mode_flag_names
[] = {
199 static bit_name_fn(mode_flag
)
201 static void dump_fourcc(uint32_t fourcc
)
203 char *name
= drmGetFormatName(fourcc
);
208 static void dump_encoders(struct device
*dev
)
210 drmModeEncoder
*encoder
;
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
;
220 printf("%d\t%d\t%s\t0x%08x\t0x%08x\n",
223 util_lookup_encoder_type_name(encoder
->encoder_type
),
224 encoder
->possible_crtcs
,
225 encoder
->possible_clones
);
230 static void dump_mode(drmModeModeInfo
*mode
, int index
)
232 printf(" #%i %s %.2f %d %d %d %d %d %d %d %d %d",
247 mode_flag_str(mode
->flags
);
249 mode_type_str(mode
->type
);
253 static void dump_blob(struct device
*dev
, uint32_t blob_id
)
256 unsigned char *blob_data
;
257 drmModePropertyBlobPtr blob
;
259 blob
= drmModeGetPropertyBlob(dev
->fd
, blob_id
);
265 blob_data
= blob
->data
;
267 for (i
= 0; i
< blob
->length
; i
++) {
270 printf("%.2hhx", blob_data
[i
]);
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
) {
287 snprintf(mod_string
, sizeof(mod_string
), "%s_%s",
288 vendor_name
, "UNKNOWN_MODIFIER");
290 snprintf(mod_string
, sizeof(mod_string
), "%s_%s",
291 "UNKNOWN_VENDOR", "UNKNOWN_MODIFIER");
292 /* safe, as free is no-op for NULL */
297 if (modifier
== DRM_FORMAT_MOD_LINEAR
) {
298 snprintf(mod_string
, sizeof(mod_string
), "%s", modifier_name
);
304 snprintf(mod_string
, sizeof(mod_string
), "%s_%s",
305 vendor_name
, modifier_name
);
312 static void dump_in_formats(struct device
*dev
, uint32_t blob_id
)
314 drmModeFormatModifierIterator iter
= {0};
315 drmModePropertyBlobPtr blob
;
318 printf("\t\tin_formats blob decoded:\n");
319 blob
= drmModeGetPropertyBlob(dev
->fd
, blob_id
);
325 while (drmModeFormatModifierBlobIterNext(blob
, &iter
)) {
326 if (!fmt
|| fmt
!= iter
.fmt
) {
327 printf("%s\t\t\t", !fmt
? "" : "\n");
333 printf(" %s(0x%"PRIx64
")", modifier_to_string(iter
.mod
), iter
.mod
);
338 drmModeFreePropertyBlob(blob
);
341 static void dump_prop(struct device
*dev
, drmModePropertyPtr prop
,
342 uint32_t prop_id
, uint64_t value
)
345 printf("\t%d", prop_id
);
351 printf(" %s:\n", prop
->name
);
353 printf("\t\tflags:");
354 if (prop
->flags
& DRM_MODE_PROP_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
))
362 if (drm_property_type_is(prop
, DRM_MODE_PROP_ENUM
))
364 if (drm_property_type_is(prop
, DRM_MODE_PROP_BITMASK
))
366 if (drm_property_type_is(prop
, DRM_MODE_PROP_BLOB
))
368 if (drm_property_type_is(prop
, DRM_MODE_PROP_OBJECT
))
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
]));
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
]);
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
);
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
));
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
]);
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
);
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
)
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
;
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
),
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
]);
447 if (connector
->count_modes
) {
449 printf("\tindex name refresh (Hz) hdisp hss hse htot vdisp "
451 for (j
= 0; j
< connector
->count_modes
; j
++)
452 dump_mode(&connector
->modes
[j
], j
);
455 if (_connector
->props
) {
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
]);
466 static void dump_crtcs(struct device
*dev
)
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
;
479 printf("%d\t%d\t(%d,%d)\t(%dx%d)\n",
483 crtc
->width
, crtc
->height
);
484 dump_mode(&crtc
->mode
, 0);
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
]);
493 printf(" no properties found\n");
499 static void dump_framebuffers(struct device
*dev
)
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
;
511 printf("%u\t(%ux%u)\t%u\n",
513 fb
->width
, fb
->height
,
519 static void dump_planes(struct device
*dev
)
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
;
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
)
541 for (j
= 0; j
< ovr
->count_formats
; j
++)
542 dump_fourcc(ovr
->formats
[j
]);
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
]);
552 printf(" no properties found\n");
560 static void free_resources(struct resources
*res
)
567 #define free_resource(_res, type, Type) \
569 if (!(_res)->type##s) \
571 for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \
572 if (!(_res)->type##s[i].type) \
574 drmModeFree##Type((_res)->type##s[i].type); \
576 free((_res)->type##s); \
579 #define free_properties(_res, type) \
581 for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \
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); \
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
);
607 static struct resources
*get_resources(struct device
*dev
)
610 drmModePlaneRes
*plane_res
;
611 struct resources
*res
;
614 res
= calloc(1, sizeof(*res
));
618 drmSetClientCap(dev
->fd
, DRM_CLIENT_CAP_UNIVERSAL_PLANES
, 1);
620 _res
= drmModeGetResources(dev
->fd
);
622 fprintf(stderr
, "drmModeGetResources failed: %s\n",
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
);
643 #define get_resource(_res, __res, type, Type) \
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", \
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
;
669 num
= asprintf(&connector
->name
, "%s-%u",
670 drmModeGetConnectorTypeName(conn
->connector_type
),
671 conn
->connector_type_id
);
676 #define get_properties(_res, type, Type) \
678 for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \
679 struct type *obj = &res->type##s[i]; \
682 drmModeObjectGetProperties(dev->fd, obj->type->type##_id, \
683 DRM_MODE_OBJECT_##Type); \
686 "could not get %s %i properties: %s\n", \
687 #type, obj->type->type##_id, \
691 obj->props_info = calloc(obj->props->count_props, \
692 sizeof(*obj->props_info)); \
693 if (!obj->props_info) \
695 for (j = 0; j < obj->props->count_props; ++j) \
696 obj->props_info[j] = \
697 drmModeGetProperty(dev->fd, obj->props->props[j]); \
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
);
709 fprintf(stderr
, "drmModeGetPlaneResources failed: %s\n",
714 res
->count_planes
= plane_res
->count_planes
;
716 res
->planes
= calloc(res
->count_planes
, sizeof(*res
->planes
));
718 drmModeFreePlaneResources(plane_res
);
722 get_resource(res
, plane_res
, plane
, Plane
);
723 drmModeFreePlaneResources(plane_res
);
724 get_properties(res
, plane
, PLANE
);
733 static struct crtc
*get_crtc_by_id(struct device
*dev
, uint32_t id
)
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
];
746 static uint32_t get_crtc_mask(struct device
*dev
, struct crtc
*crtc
)
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
)
754 /* Unreachable: crtc->crtc is one of resources->crtcs[] */
755 /* Don't return zero or static analysers will complain */
760 static drmModeConnector
*get_connector_by_name(struct device
*dev
, const char *name
)
762 struct connector
*connector
;
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
;
775 static drmModeConnector
*get_connector_by_id(struct device
*dev
, uint32_t id
)
777 drmModeConnector
*connector
;
780 for (i
= 0; i
< dev
->resources
->count_connectors
; i
++) {
781 connector
= dev
->resources
->connectors
[i
].connector
;
782 if (connector
&& connector
->connector_id
== id
)
789 static drmModeEncoder
*get_encoder_by_id(struct device
*dev
, uint32_t id
)
791 drmModeEncoder
*encoder
;
794 for (i
= 0; i
< dev
->resources
->count_encoders
; i
++) {
795 encoder
= dev
->resources
->encoders
[i
].encoder
;
796 if (encoder
&& encoder
->encoder_id
== id
)
803 /* -----------------------------------------------------------------------------
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.
817 unsigned int num_cons
;
820 char format_str
[8]; /* need to leave room for "_BE" and terminating \0 */
823 drmModeModeInfo
*mode
;
825 unsigned int fb_id
[2], current_fb_id
;
826 struct timeval start
;
827 unsigned int out_fb_id
;
834 uint32_t plane_id
; /* the id of plane to use */
835 uint32_t crtc_id
; /* the id of CRTC to bind to */
841 unsigned int old_fb_id
;
844 char format_str
[8]; /* need to leave room for "_BE" and terminating \0 */
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
;
856 connector
= get_connector_by_id(dev
, con_id
);
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
);
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
);
875 if (!connector
->count_modes
)
879 if (mode_str
[0] == '#') {
880 int index
= atoi(mode_str
+ 1);
882 if (index
>= connector
->count_modes
|| index
< 0)
884 return &connector
->modes
[index
];
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.
898 else if (fabs(mode_vrefresh(mode
) - vrefresh
) < 0.005)
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
;
914 for (i
= 0; i
< pipe
->num_cons
; ++i
) {
915 uint32_t crtcs_for_connector
= 0;
916 drmModeConnector
*connector
;
917 drmModeEncoder
*encoder
;
920 connector
= get_connector_by_id(dev
, pipe
->con_ids
[i
]);
924 for (j
= 0; j
< connector
->count_encoders
; ++j
) {
925 encoder
= get_encoder_by_id(dev
, connector
->encoders
[j
]);
929 crtcs_for_connector
|= encoder
->possible_crtcs
;
930 crtc
= get_crtc_by_id(dev
, encoder
->crtc_id
);
933 active_crtcs
|= get_crtc_mask(dev
, crtc
);
936 possible_crtcs
&= crtcs_for_connector
;
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
);
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
;
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
);
966 "failed to find mode "
967 "\"%s-%.2fHz\" for connector %s\n",
968 pipe
->mode_str
, pipe
->vrefresh
, pipe
->cons
[i
]);
971 "failed to find mode \"%s\" for connector %s\n",
972 pipe
->mode_str
, pipe
->cons
[i
]);
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
);
983 pipe
->crtc
= pipe_find_crtc(dev
, pipe
);
984 pipe
->crtc_id
= pipe
->crtc
->crtc
->crtc_id
;
988 fprintf(stderr
, "failed to find CRTC for pipe\n");
993 pipe
->crtc
->mode
= mode
;
998 /* -----------------------------------------------------------------------------
1002 struct property_arg
{
1005 char name
[DRM_PROP_NAME_LEN
+1];
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
;
1022 #define find_object(_res, type, Type) \
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) \
1028 p->obj_type = DRM_MODE_OBJECT_##Type; \
1030 props = obj->props; \
1031 props_info = obj->props_info; \
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",
1047 fprintf(stderr
, "%s %i has no properties\n",
1048 obj_type
, p
->obj_id
);
1052 for (i
= 0; i
< (int)props
->count_props
; ++i
) {
1055 if (strcmp(props_info
[i
]->name
, p
->name
) == 0)
1059 if (i
== (int)props
->count_props
) {
1061 fprintf(stderr
, "%s %i has no %s property\n",
1062 obj_type
, p
->obj_id
, p
->name
);
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
);
1072 ret
= drmModeAtomicAddProperty(dev
->req
, p
->obj_id
, p
->prop_id
, p
->value
);
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
));
1081 /* -------------------------------------------------------------------------- */
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
;
1093 if (pipe
->current_fb_id
== pipe
->fb_id
[0])
1094 new_fb_id
= pipe
->fb_id
[1];
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
;
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;
1112 static bool format_support(const drmModePlanePtr ovr
, uint32_t fmt
)
1116 for (i
= 0; i
< ovr
->count_formats
; ++i
) {
1117 if (ovr
->formats
[i
] == fmt
)
1124 static void add_property(struct device
*dev
, uint32_t obj_id
,
1125 const char *name
, uint64_t value
)
1127 struct property_arg p
;
1130 strcpy(p
.name
, name
);
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
;
1142 strcpy(p
.name
, name
);
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];
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
);
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
++) {
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
));
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};
1202 bo
= bo_create(fd
, fourcc
, w
, h
, handles
, pitches
, offsets
, pat
);
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
));
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
);
1230 fprintf(stderr
, "CRTC %u not found\n", p
->crtc_id
);
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
;
1242 if (bo_fb_create(dev
->fd
, p
->fourcc
, p
->w
, p
->h
,
1243 pattern
, &plane_bo
, &p
->fb_id
))
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;
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
);
1277 static int set_plane(struct device
*dev
, struct plane_arg
*p
)
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
);
1290 fprintf(stderr
, "CRTC %u not found\n", p
->crtc_id
);
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
;
1301 if (plane_id
&& plane_id
!= ovr
->plane_id
)
1304 if (!format_support(ovr
, p
->fourcc
))
1307 if ((ovr
->possible_crtcs
& crtc_mask
) &&
1308 (ovr
->crtc_id
== 0 || ovr
->crtc_id
== p
->crtc_id
)) {
1309 plane_id
= ovr
->plane_id
;
1314 if (i
== dev
->resources
->count_planes
) {
1315 fprintf(stderr
, "no unused plane available for CRTC %u\n",
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
))
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;
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",
1348 ovr
->crtc_id
= p
->crtc_id
;
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
;
1359 for (i
= 0; i
< count
; i
++) {
1361 pattern
= secondary_fill
;
1363 set_gamma(dev
, p
[i
].crtc_id
, p
[i
].fourcc
);
1365 if (atomic_set_plane(dev
, &p
[i
], pattern
, update
))
1371 atomic_test_page_flip(struct device
*dev
, struct pipe_arg
*pipe_args
,
1372 struct plane_arg
*plane_args
, unsigned int plane_count
)
1376 gettimeofday(&pipe_args
->start
, NULL
);
1377 pipe_args
->swap_count
= 0;
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
);
1386 fprintf(stderr
, "Atomic Commit failed [2]\n");
1390 pipe_args
->swap_count
++;
1391 if (pipe_args
->swap_count
== 60) {
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
)
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
)
1427 for (i
= 0; i
< count
; i
++) {
1429 drmModeRmFB(dev
->fd
, p
[i
].fb_id
);
1432 if (p
[i
].old_fb_id
) {
1433 drmModeRmFB(dev
->fd
, p
[i
].old_fb_id
);
1437 bo_destroy(p
[i
].bo
);
1441 bo_destroy(p
[i
].old_bo
);
1448 static void clear_planes(struct device
*dev
, struct plane_arg
*p
, unsigned int count
)
1452 for (i
= 0; i
< count
; i
++) {
1454 drmModeRmFB(dev
->fd
, p
[i
].fb_id
);
1456 bo_destroy(p
[i
].bo
);
1460 static int pipe_resolve_connectors(struct device
*dev
, struct pipe_arg
*pipe
)
1462 drmModeConnector
*connector
;
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
]);
1472 fprintf(stderr
, "no connector named '%s'\n",
1477 id
= connector
->connector_id
;
1480 pipe
->con_ids
[i
] = id
;
1486 static bool pipe_has_writeback_connector(struct device
*dev
, struct pipe_arg
*pipes
,
1489 drmModeConnector
*connector
;
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
)
1504 static int pipe_attempt_connector(struct device
*dev
, drmModeConnector
*con
,
1505 struct pipe_arg
*pipe
)
1510 con_str
= calloc(8, sizeof(char));
1514 sprintf(con_str
, "%d", con
->connector_id
);
1515 strcpy(pipe
->format_str
, "XR24");
1516 pipe
->fourcc
= util_format_fourcc(pipe
->format_str
);
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
)
1524 pipe
->con_ids
[0] = con
->connector_id
;
1525 pipe
->cons
[0] = (const char*)con_str
;
1527 pipe
->crtc
= pipe_find_crtc(dev
, pipe
);
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
;
1545 sprintf(pipe
->mode_str
, "%dx%d", pipe
->mode
->hdisplay
, pipe
->mode
->vdisplay
);
1551 free(pipe
->con_ids
);
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
)
1572 printf("no connected connector!\n");
1576 pipes
= calloc(connected
, sizeof(struct pipe_arg
));
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
)
1585 if (pipe_attempt_connector(dev
, con
, &pipes
[attempted
]) < 0) {
1586 printf("failed fetching preferred mode for connector\n");
1596 static struct plane
*get_primary_plane_by_crtc(struct device
*dev
, struct crtc
*crtc
)
1600 for (i
= 0; i
< dev
->resources
->count_planes
; i
++) {
1601 struct plane
*plane
= &dev
->resources
->planes
[i
];
1602 drmModePlane
*ovr
= plane
->plane
;
1606 // XXX: add is_primary_plane and (?) format checks
1608 if (ovr
->possible_crtcs
& get_crtc_mask(dev
, crtc
))
1614 static unsigned int set_mode(struct device
*dev
, struct pipe_arg
**pipe_args
, unsigned int count
)
1618 int preferred
= count
== 0;
1619 struct pipe_arg
*pipes
;
1622 count
= pipe_find_preferred(dev
, pipe_args
);
1624 fprintf(stderr
, "can't find any preferred connector/mode.\n");
1632 for (i
= 0; i
< count
; i
++) {
1633 struct pipe_arg
*pipe
= &pipes
[i
];
1635 ret
= pipe_resolve_connectors(dev
, pipe
);
1639 ret
= pipe_find_crtc_and_mode(dev
, pipe
);
1645 if (!dev
->use_atomic
) {
1646 for (i
= 0; i
< count
; i
++) {
1647 struct pipe_arg
*pipe
= &pipes
[i
];
1649 if (pipe
->mode
== NULL
)
1653 dev
->mode
.width
+= pipe
->mode
->hdisplay
;
1654 if (dev
->mode
.height
< pipe
->mode
->vdisplay
)
1655 dev
->mode
.height
= pipe
->mode
->vdisplay
;
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
))
1672 for (i
= 0; i
< count
; i
++) {
1673 struct pipe_arg
*pipe
= &pipes
[i
];
1676 if (pipe
->mode
== NULL
)
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
,
1693 /* XXX: Actually check if this is needed */
1694 drmModeDirtyFB(dev
->fd
, dev
->mode
.fb_id
, NULL
, 0);
1697 x
+= pipe
->mode
->hdisplay
;
1700 fprintf(stderr
, "failed to set mode: %s\n", strerror(errno
));
1704 set_gamma(dev
, pipe
->crtc_id
, pipe
->fourcc
);
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 */
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
,
1719 .format_str
= "XR24",
1720 .fourcc
= util_format_fourcc(pipe
->format_str
),
1723 atomic_set_planes(dev
, &plane_args
, 1, false);
1731 static void writeback_config(struct device
*dev
, struct pipe_arg
*pipes
, unsigned int count
)
1733 drmModeConnector
*connector
;
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
) {
1743 fprintf(stderr
, "no mode for writeback\n");
1746 bo_fb_create(dev
->fd
, pipes
[j
].fourcc
,
1747 pipe
->mode
->hdisplay
, pipe
->mode
->vdisplay
,
1749 &pipe
->out_bo
, &pipe
->out_fb_id
);
1750 add_property(dev
, pipe
->con_ids
[i
], "WRITEBACK_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
};
1765 ret
= poll(&fds
, 1, timeout
);
1767 if (fds
.revents
& (POLLERR
| POLLNVAL
))
1771 } else if (ret
== 0) {
1775 if (ret
== -EINTR
|| ret
== -EAGAIN
)
1783 static void dump_output_fb(struct device
*dev
, struct pipe_arg
*pipes
, char *dump_path
,
1786 drmModeConnector
*connector
;
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
)
1805 for (i
= 0; i
< count
; i
++) {
1806 struct pipe_arg
*pipe
= &pipes
[i
];
1808 if (pipe
->mode
== NULL
)
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
);
1824 bo_destroy(dev
->mode
.bo
);
1827 static void set_planes(struct device
*dev
, struct plane_arg
*p
, unsigned int count
)
1831 /* set up planes/overlays */
1832 for (i
= 0; i
< count
; i
++)
1833 if (set_plane(dev
, &p
[i
]))
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};
1847 ret
= drmGetCap(dev
->fd
, DRM_CAP_CURSOR_WIDTH
, &value
);
1851 ret
= drmGetCap(dev
->fd
, DRM_CAP_CURSOR_HEIGHT
, &value
);
1856 /* create cursor bo.. just using PATTERN_PLAIN as it has
1859 bo
= bo_create(dev
->fd
, DRM_FORMAT_ARGB8888
, cw
, ch
, handles
, pitches
,
1860 offsets
, UTIL_PATTERN_PLAIN
);
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],
1870 pipe
->mode
->hdisplay
, pipe
->mode
->vdisplay
,
1873 fprintf(stderr
, "failed to init cursor for CRTC[%u]\n",
1882 static void clear_cursors(struct device
*dev
)
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
;
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
))
1902 for (i
= 0; i
< count
; i
++) {
1903 struct pipe_arg
*pipe
= &pipes
[i
];
1905 if (pipe
->mode
== NULL
)
1908 ret
= drmModePageFlip(dev
->fd
, pipe
->crtc_id
,
1909 other_fb_id
, DRM_MODE_PAGE_FLIP_EVENT
,
1912 fprintf(stderr
, "failed to page flip: %s\n", strerror(errno
));
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
;
1929 struct pollfd pfd
[2];
1932 pfd
[0].events
= POLLIN
;
1934 pfd
[1].events
= POLLIN
;
1936 if (poll(pfd
, 2, -1) < 0) {
1937 fprintf(stderr
, "poll error\n");
1944 struct timeval timeout
= { .tv_sec
= 3, .tv_usec
= 0 };
1949 FD_SET(dev
->fd
, &fds
);
1950 ret
= select(dev
->fd
+ 1, &fds
, NULL
, NULL
, &timeout
);
1953 fprintf(stderr
, "select timed out or error (ret %d)\n",
1956 } else if (FD_ISSET(0, &fds
)) {
1961 drmHandleEvent(dev
->fd
, &evctx
);
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
)
1979 pipe
->crtc_id
= (uint32_t)-1;
1980 strcpy(pipe
->format_str
, "XR24");
1982 /* Count the number of connectors and allocate them. */
1984 for (p
= arg
; *p
&& *p
!= ':' && *p
!= '@'; ++p
) {
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
)
1994 /* Parse the connectors. */
1995 for (i
= 0, p
= arg
; i
< pipe
->num_cons
; ++i
, p
= endp
+ 1) {
1996 endp
= strpbrk(p
, ",@:");
2000 pipe
->cons
[i
] = strndup(p
, endp
- p
);
2006 if (i
!= pipe
->num_cons
- 1)
2009 /* Parse the remaining parameters. */
2014 pipe
->crtc_id
= strtoul(arg
, &endp
, 10);
2021 /* Search for the vertical refresh or the format. */
2022 p
= strpbrk(arg
, "-@");
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';
2030 pipe
->vrefresh
= strtof(p
+ 1, &endp
);
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
);
2049 static int parse_plane(struct plane_arg
*plane
, const char *p
)
2054 plane
->plane_id
= strtoul(p
, &end
, 10);
2059 plane
->crtc_id
= strtoul(p
, &end
, 10);
2064 plane
->w
= strtoul(p
, &end
, 10);
2069 plane
->h
= strtoul(p
, &end
, 10);
2071 if (*end
== '+' || *end
== '-') {
2072 plane
->x
= strtol(end
, &end
, 10);
2073 if (*end
!= '+' && *end
!= '-')
2075 plane
->y
= strtol(end
, &end
, 10);
2077 plane
->has_position
= true;
2082 plane
->scale
= strtod(p
, &end
);
2083 if (plane
->scale
<= 0.0)
2090 len
= sizeof(plane
->format_str
) - 1;
2091 strncpy(plane
->format_str
, end
+ 1, len
);
2092 plane
->format_str
[len
] = '\0';
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
);
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)
2112 p
->name
[DRM_PROP_NAME_LEN
] = '\0';
2117 static void parse_fill_patterns(char *arg
)
2119 char *fill
= strtok(arg
, ",");
2122 primary_fill
= util_pattern_enum(fill
);
2123 fill
= strtok(NULL
, ",");
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");
2182 static char optstr
[] = "acdD:efF:M:P:ps:Cvrw:o:";
2184 int main(int argc
, char **argv
)
2189 int encoders
= 0, connectors
= 0, crtcs
= 0, planes
= 0, framebuffers
= 0;
2190 int drop_master
= 0;
2192 int test_cursor
= 0;
2193 int set_preferred
= 0;
2195 char *device
= NULL
;
2196 char *module
= NULL
;
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;
2205 char *dump_path
= NULL
;
2207 memset(&dev
, 0, sizeof dev
);
2210 while ((c
= getopt(argc
, argv
, optstr
)) != -1) {
2216 /* Preserve the default behaviour of dumping all information. */
2224 /* Preserve the default behaviour of dumping all information. */
2237 parse_fill_patterns(optarg
);
2241 /* Preserve the default behaviour of dumping all information. */
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");
2254 memset(&plane_args
[plane_count
], 0, sizeof(*plane_args
));
2256 if (parse_plane(&plane_args
[plane_count
], optarg
) < 0)
2266 pipe_args
= realloc(pipe_args
,
2267 (count
+ 1) * sizeof *pipe_args
);
2268 if (pipe_args
== NULL
) {
2269 fprintf(stderr
, "memory allocation failed\n");
2272 memset(&pipe_args
[count
], 0, sizeof(*pipe_args
));
2274 if (parse_connector(&pipe_args
[count
], optarg
) < 0)
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");
2295 memset(&prop_args
[prop_count
], 0, sizeof(*prop_args
));
2297 if (parse_property(&prop_args
[prop_count
], optarg
) < 0)
2308 /* Dump all the details when no* arguments are provided. */
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");
2316 if (set_preferred
&& count
) {
2317 fprintf(stderr
, "cannot use -r (preferred) when -s (mode) is set\n");
2321 dev
.fd
= util_open(device
, module
);
2326 ret
= drmSetClientCap(dev
.fd
, DRM_CLIENT_CAP_ATOMIC
, 1);
2327 drmSetClientCap(dev
.fd
, DRM_CLIENT_CAP_WRITEBACK_CONNECTORS
, 1);
2329 fprintf(stderr
, "no atomic modesetting support: %s\n", strerror(errno
));
2335 dev
.use_atomic
= use_atomic
;
2337 dev
.resources
= get_resources(&dev
);
2338 if (!dev
.resources
) {
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
);
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
)) {
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");
2367 if (set_preferred
|| count
)
2368 count
= set_mode(&dev
, &pipe_args
, count
);
2371 if (!pipe_has_writeback_connector(&dev
, pipe_args
, count
)) {
2372 fprintf(stderr
, "No writeback connector found, can not dump.\n");
2376 writeback_config(&dev
, pipe_args
, count
);
2380 atomic_set_planes(&dev
, plane_args
, plane_count
, false);
2382 ret
= drmModeAtomicCommit(dev
.fd
, dev
.req
, DRM_MODE_ATOMIC_ALLOW_MODESET
, NULL
);
2384 fprintf(stderr
, "Atomic Commit failed [1]\n");
2389 * Since only writeback connectors have an output fb, this should only be
2390 * called for writeback.
2393 ret
= poll_writeback_fence(dev
.writeback_fence_fd
, 1000);
2395 fprintf(stderr
, "Poll for writeback error: %d. Skipping Dump.\n",
2397 dump_output_fb(&dev
, pipe_args
, dump_path
, count
);
2401 atomic_test_page_flip(&dev
, pipe_args
, plane_args
, plane_count
);
2404 drmDropMaster(dev
.fd
);
2408 drmModeAtomicFree(dev
.req
);
2409 dev
.req
= drmModeAtomicAlloc();
2411 /* XXX: properly teardown the preferred mode/plane state */
2413 atomic_clear_planes(&dev
, plane_args
, plane_count
);
2416 atomic_clear_mode(&dev
, pipe_args
, count
);
2419 ret
= drmModeAtomicCommit(dev
.fd
, dev
.req
, DRM_MODE_ATOMIC_ALLOW_MODESET
, NULL
);
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
);
2429 fprintf(stderr
, "writeback / dump is only supported in atomic mode\n");
2433 if (set_preferred
|| count
|| plane_count
) {
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");
2442 if (set_preferred
|| count
)
2443 count
= set_mode(&dev
, &pipe_args
, count
);
2446 set_planes(&dev
, plane_args
, plane_count
);
2449 set_cursors(&dev
, pipe_args
, count
);
2452 test_page_flip(&dev
, pipe_args
, count
);
2455 drmDropMaster(dev
.fd
);
2460 clear_cursors(&dev
);
2463 clear_planes(&dev
, plane_args
, plane_count
);
2465 if (set_preferred
|| count
)
2470 free_resources(dev
.resources
);