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
;
76 drmModeObjectProperties
*props
;
77 drmModePropertyRes
**props_info
;
78 drmModeModeInfo
*mode
;
82 drmModeEncoder
*encoder
;
86 drmModeConnector
*connector
;
87 drmModeObjectProperties
*props
;
88 drmModePropertyRes
**props_info
;
98 drmModeObjectProperties
*props
;
99 drmModePropertyRes
**props_info
;
105 struct encoder
*encoders
;
107 struct connector
*connectors
;
108 int count_connectors
;
111 struct plane
*planes
;
112 uint32_t count_planes
;
118 struct resources
*resources
;
126 struct bo
*cursor_bo
;
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) { \
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]); \
157 static const char *mode_type_names
[] = {
167 static bit_name_fn(mode_type
)
169 static const char *mode_flag_names
[] = {
186 static bit_name_fn(mode_flag
)
188 static void dump_fourcc(uint32_t fourcc
)
197 static void dump_encoders(struct device
*dev
)
199 drmModeEncoder
*encoder
;
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
;
209 printf("%d\t%d\t%s\t0x%08x\t0x%08x\n",
212 util_lookup_encoder_type_name(encoder
->encoder_type
),
213 encoder
->possible_crtcs
,
214 encoder
->possible_clones
);
219 static void dump_mode(drmModeModeInfo
*mode
, int index
)
221 printf(" #%i %s %.2f %d %d %d %d %d %d %d %d %d",
236 mode_flag_str(mode
->flags
);
238 mode_type_str(mode
->type
);
242 static void dump_blob(struct device
*dev
, uint32_t blob_id
)
245 unsigned char *blob_data
;
246 drmModePropertyBlobPtr blob
;
248 blob
= drmModeGetPropertyBlob(dev
->fd
, blob_id
);
254 blob_data
= blob
->data
;
256 for (i
= 0; i
< blob
->length
; i
++) {
259 printf("%.2hhx", blob_data
[i
]);
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
) {
276 snprintf(mod_string
, sizeof(mod_string
), "%s_%s",
277 vendor_name
, "UNKNOWN_MODIFIER");
279 snprintf(mod_string
, sizeof(mod_string
), "%s_%s",
280 "UNKNOWN_VENDOR", "UNKNOWN_MODIFIER");
281 /* safe, as free is no-op for NULL */
286 if (modifier
== DRM_FORMAT_MOD_LINEAR
) {
287 snprintf(mod_string
, sizeof(mod_string
), "%s", modifier_name
);
293 snprintf(mod_string
, sizeof(mod_string
), "%s_%s",
294 vendor_name
, modifier_name
);
301 static void dump_in_formats(struct device
*dev
, uint32_t blob_id
)
304 drmModePropertyBlobPtr blob
;
305 struct drm_format_modifier_blob
*header
;
307 struct drm_format_modifier
*modifiers
;
309 printf("\t\tin_formats blob decoded:\n");
310 blob
= drmModeGetPropertyBlob(dev
->fd
, blob_id
);
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
++) {
323 dump_fourcc(formats
[i
]);
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
));
333 drmModeFreePropertyBlob(blob
);
336 static void dump_prop(struct device
*dev
, drmModePropertyPtr prop
,
337 uint32_t prop_id
, uint64_t value
)
340 printf("\t%d", prop_id
);
346 printf(" %s:\n", prop
->name
);
348 printf("\t\tflags:");
349 if (prop
->flags
& DRM_MODE_PROP_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
))
357 if (drm_property_type_is(prop
, DRM_MODE_PROP_ENUM
))
359 if (drm_property_type_is(prop
, DRM_MODE_PROP_BITMASK
))
361 if (drm_property_type_is(prop
, DRM_MODE_PROP_BLOB
))
363 if (drm_property_type_is(prop
, DRM_MODE_PROP_OBJECT
))
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
]));
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
]);
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
);
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
));
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
]);
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
);
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
)
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
;
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
),
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
]);
442 if (connector
->count_modes
) {
444 printf("\tindex name refresh (Hz) hdisp hss hse htot vdisp "
446 for (j
= 0; j
< connector
->count_modes
; j
++)
447 dump_mode(&connector
->modes
[j
], j
);
450 if (_connector
->props
) {
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
]);
461 static void dump_crtcs(struct device
*dev
)
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
;
474 printf("%d\t%d\t(%d,%d)\t(%dx%d)\n",
478 crtc
->width
, crtc
->height
);
479 dump_mode(&crtc
->mode
, 0);
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
]);
488 printf(" no properties found\n");
494 static void dump_framebuffers(struct device
*dev
)
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
;
506 printf("%u\t(%ux%u)\t%u\n",
508 fb
->width
, fb
->height
,
514 static void dump_planes(struct device
*dev
)
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
;
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
)
536 for (j
= 0; j
< ovr
->count_formats
; j
++)
537 dump_fourcc(ovr
->formats
[j
]);
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
]);
547 printf(" no properties found\n");
555 static void free_resources(struct resources
*res
)
562 #define free_resource(_res, type, Type) \
564 if (!(_res)->type##s) \
566 for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \
567 if (!(_res)->type##s[i].type) \
569 drmModeFree##Type((_res)->type##s[i].type); \
571 free((_res)->type##s); \
574 #define free_properties(_res, type) \
576 for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \
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); \
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
);
602 static struct resources
*get_resources(struct device
*dev
)
605 drmModePlaneRes
*plane_res
;
606 struct resources
*res
;
609 res
= calloc(1, sizeof(*res
));
613 drmSetClientCap(dev
->fd
, DRM_CLIENT_CAP_UNIVERSAL_PLANES
, 1);
615 _res
= drmModeGetResources(dev
->fd
);
617 fprintf(stderr
, "drmModeGetResources failed: %s\n",
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
);
638 #define get_resource(_res, __res, type, Type) \
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", \
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
;
664 num
= asprintf(&connector
->name
, "%s-%u",
665 util_lookup_connector_type_name(conn
->connector_type
),
666 conn
->connector_type_id
);
671 #define get_properties(_res, type, Type) \
673 for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \
674 struct type *obj = &res->type##s[i]; \
677 drmModeObjectGetProperties(dev->fd, obj->type->type##_id, \
678 DRM_MODE_OBJECT_##Type); \
681 "could not get %s %i properties: %s\n", \
682 #type, obj->type->type##_id, \
686 obj->props_info = calloc(obj->props->count_props, \
687 sizeof(*obj->props_info)); \
688 if (!obj->props_info) \
690 for (j = 0; j < obj->props->count_props; ++j) \
691 obj->props_info[j] = \
692 drmModeGetProperty(dev->fd, obj->props->props[j]); \
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
);
704 fprintf(stderr
, "drmModeGetPlaneResources failed: %s\n",
709 res
->count_planes
= plane_res
->count_planes
;
711 res
->planes
= calloc(res
->count_planes
, sizeof(*res
->planes
));
713 drmModeFreePlaneResources(plane_res
);
717 get_resource(res
, plane_res
, plane
, Plane
);
718 drmModeFreePlaneResources(plane_res
);
719 get_properties(res
, plane
, PLANE
);
728 static struct crtc
*get_crtc_by_id(struct device
*dev
, uint32_t id
)
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
];
741 static uint32_t get_crtc_mask(struct device
*dev
, struct crtc
*crtc
)
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
)
749 /* Unreachable: crtc->crtc is one of resources->crtcs[] */
750 /* Don't return zero or static analysers will complain */
755 static drmModeConnector
*get_connector_by_name(struct device
*dev
, const char *name
)
757 struct connector
*connector
;
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
;
770 static drmModeConnector
*get_connector_by_id(struct device
*dev
, uint32_t id
)
772 drmModeConnector
*connector
;
775 for (i
= 0; i
< dev
->resources
->count_connectors
; i
++) {
776 connector
= dev
->resources
->connectors
[i
].connector
;
777 if (connector
&& connector
->connector_id
== id
)
784 static drmModeEncoder
*get_encoder_by_id(struct device
*dev
, uint32_t id
)
786 drmModeEncoder
*encoder
;
789 for (i
= 0; i
< dev
->resources
->count_encoders
; i
++) {
790 encoder
= dev
->resources
->encoders
[i
].encoder
;
791 if (encoder
&& encoder
->encoder_id
== id
)
798 /* -----------------------------------------------------------------------------
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.
812 unsigned int num_cons
;
818 drmModeModeInfo
*mode
;
820 unsigned int fb_id
[2], current_fb_id
;
821 struct timeval start
;
827 uint32_t plane_id
; /* the id of plane to use */
828 uint32_t crtc_id
; /* the id of CRTC to bind to */
834 unsigned int old_fb_id
;
837 char format_str
[5]; /* need to leave room for terminating \0 */
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
;
849 connector
= get_connector_by_id(dev
, con_id
);
850 if (!connector
|| !connector
->count_modes
)
854 if (mode_str
[0] == '#') {
855 int index
= atoi(mode_str
+ 1);
857 if (index
>= connector
->count_modes
|| index
< 0)
859 return &connector
->modes
[index
];
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.
873 else if (fabs(mode_vrefresh(mode
) - vrefresh
) < 0.005)
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
;
889 for (i
= 0; i
< pipe
->num_cons
; ++i
) {
890 uint32_t crtcs_for_connector
= 0;
891 drmModeConnector
*connector
;
892 drmModeEncoder
*encoder
;
895 connector
= get_connector_by_id(dev
, pipe
->con_ids
[i
]);
899 for (j
= 0; j
< connector
->count_encoders
; ++j
) {
900 encoder
= get_encoder_by_id(dev
, connector
->encoders
[j
]);
904 crtcs_for_connector
|= encoder
->possible_crtcs
;
905 crtc
= get_crtc_by_id(dev
, encoder
->crtc_id
);
908 active_crtcs
|= get_crtc_mask(dev
, crtc
);
911 possible_crtcs
&= crtcs_for_connector
;
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
);
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
;
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
);
941 "failed to find mode "
942 "\"%s-%.2fHz\" for connector %s\n",
943 pipe
->mode_str
, pipe
->vrefresh
, pipe
->cons
[i
]);
946 "failed to find mode \"%s\" for connector %s\n",
947 pipe
->mode_str
, pipe
->cons
[i
]);
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
);
958 pipe
->crtc
= pipe_find_crtc(dev
, pipe
);
959 pipe
->crtc_id
= pipe
->crtc
->crtc
->crtc_id
;
963 fprintf(stderr
, "failed to find CRTC for pipe\n");
968 pipe
->crtc
->mode
= mode
;
973 /* -----------------------------------------------------------------------------
977 struct property_arg
{
980 char name
[DRM_PROP_NAME_LEN
+1];
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
;
997 #define find_object(_res, type, Type) \
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) \
1003 p->obj_type = DRM_MODE_OBJECT_##Type; \
1005 props = obj->props; \
1006 props_info = obj->props_info; \
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",
1022 fprintf(stderr
, "%s %i has no properties\n",
1023 obj_type
, p
->obj_id
);
1027 for (i
= 0; i
< (int)props
->count_props
; ++i
) {
1030 if (strcmp(props_info
[i
]->name
, p
->name
) == 0)
1034 if (i
== (int)props
->count_props
) {
1036 fprintf(stderr
, "%s %i has no %s property\n",
1037 obj_type
, p
->obj_id
, p
->name
);
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
);
1047 ret
= drmModeAtomicAddProperty(dev
->req
, p
->obj_id
, p
->prop_id
, p
->value
);
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
));
1056 /* -------------------------------------------------------------------------- */
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
;
1068 if (pipe
->current_fb_id
== pipe
->fb_id
[0])
1069 new_fb_id
= pipe
->fb_id
[1];
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
;
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;
1087 static bool format_support(const drmModePlanePtr ovr
, uint32_t fmt
)
1091 for (i
= 0; i
< ovr
->count_formats
; ++i
) {
1092 if (ovr
->formats
[i
] == fmt
)
1099 static void add_property(struct device
*dev
, uint32_t obj_id
,
1100 const char *name
, uint64_t value
)
1102 struct property_arg p
;
1105 strcpy(p
.name
, name
);
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
;
1117 strcpy(p
.name
, name
);
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];
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
);
1136 for (i
= 0; i
< 256; i
++) {
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
);
1156 fprintf(stderr
, "failed to set gamma: %s\n", strerror(errno
));
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};
1168 bo
= bo_create(fd
, fourcc
, w
, h
, handles
, pitches
, offsets
, pat
);
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
));
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
);
1196 fprintf(stderr
, "CRTC %u not found\n", p
->crtc_id
);
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
;
1208 if (bo_fb_create(dev
->fd
, p
->fourcc
, p
->w
, p
->h
,
1209 pattern
, &plane_bo
, &p
->fb_id
))
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;
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
);
1243 static int set_plane(struct device
*dev
, struct plane_arg
*p
)
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
);
1256 fprintf(stderr
, "CRTC %u not found\n", p
->crtc_id
);
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
;
1267 if (plane_id
&& plane_id
!= ovr
->plane_id
)
1270 if (!format_support(ovr
, p
->fourcc
))
1273 if ((ovr
->possible_crtcs
& crtc_mask
) &&
1274 (ovr
->crtc_id
== 0 || ovr
->crtc_id
== p
->crtc_id
)) {
1275 plane_id
= ovr
->plane_id
;
1280 if (i
== dev
->resources
->count_planes
) {
1281 fprintf(stderr
, "no unused plane available for CRTC %u\n",
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
))
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;
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",
1314 ovr
->crtc_id
= p
->crtc_id
;
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
;
1325 for (i
= 0; i
< count
; i
++) {
1327 pattern
= secondary_fill
;
1329 set_gamma(dev
, p
[i
].crtc_id
, p
[i
].fourcc
);
1331 if (atomic_set_plane(dev
, &p
[i
], pattern
, update
))
1337 atomic_test_page_flip(struct device
*dev
, struct pipe_arg
*pipe_args
,
1338 struct plane_arg
*plane_args
, unsigned int plane_count
)
1342 gettimeofday(&pipe_args
->start
, NULL
);
1343 pipe_args
->swap_count
= 0;
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
);
1352 fprintf(stderr
, "Atomic Commit failed [2]\n");
1356 pipe_args
->swap_count
++;
1357 if (pipe_args
->swap_count
== 60) {
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
)
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
)
1393 for (i
= 0; i
< count
; i
++) {
1395 drmModeRmFB(dev
->fd
, p
[i
].fb_id
);
1398 if (p
[i
].old_fb_id
) {
1399 drmModeRmFB(dev
->fd
, p
[i
].old_fb_id
);
1403 bo_destroy(p
[i
].bo
);
1407 bo_destroy(p
[i
].old_bo
);
1414 static void clear_planes(struct device
*dev
, struct plane_arg
*p
, unsigned int count
)
1418 for (i
= 0; i
< count
; i
++) {
1420 drmModeRmFB(dev
->fd
, p
[i
].fb_id
);
1422 bo_destroy(p
[i
].bo
);
1426 static int pipe_resolve_connectors(struct device
*dev
, struct pipe_arg
*pipe
)
1428 drmModeConnector
*connector
;
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
]);
1438 fprintf(stderr
, "no connector named '%s'\n",
1443 id
= connector
->connector_id
;
1446 pipe
->con_ids
[i
] = id
;
1452 static int pipe_attempt_connector(struct device
*dev
, drmModeConnector
*con
,
1453 struct pipe_arg
*pipe
)
1458 con_str
= calloc(8, sizeof(char));
1462 sprintf(con_str
, "%d", con
->connector_id
);
1463 strcpy(pipe
->format_str
, "XR24");
1464 pipe
->fourcc
= util_format_fourcc(pipe
->format_str
);
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
)
1472 pipe
->con_ids
[0] = con
->connector_id
;
1473 pipe
->cons
[0] = (const char*)con_str
;
1475 pipe
->crtc
= pipe_find_crtc(dev
, pipe
);
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
;
1493 sprintf(pipe
->mode_str
, "%dx%d", pipe
->mode
->hdisplay
, pipe
->mode
->vdisplay
);
1499 free(pipe
->con_ids
);
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
)
1519 printf("no connected connector!\n");
1523 pipes
= calloc(connected
, sizeof(struct pipe_arg
));
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
)
1532 if (pipe_attempt_connector(dev
, con
, &pipes
[attempted
]) < 0) {
1533 printf("failed fetching preferred mode for connector\n");
1543 static struct plane
*get_primary_plane_by_crtc(struct device
*dev
, struct crtc
*crtc
)
1547 for (i
= 0; i
< dev
->resources
->count_planes
; i
++) {
1548 struct plane
*plane
= &dev
->resources
->planes
[i
];
1549 drmModePlane
*ovr
= plane
->plane
;
1553 // XXX: add is_primary_plane and (?) format checks
1555 if (ovr
->possible_crtcs
& get_crtc_mask(dev
, crtc
))
1561 static void set_mode(struct device
*dev
, struct pipe_arg
*pipes
, unsigned int count
)
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
);
1574 ret
= pipe_find_crtc_and_mode(dev
, pipe
);
1579 struct pipe_arg
*pipe_args
;
1581 count
= pipe_find_preferred(dev
, &pipe_args
);
1583 fprintf(stderr
, "can't find any preferred connector/mode.\n");
1589 if (!dev
->use_atomic
) {
1590 for (i
= 0; i
< count
; i
++) {
1591 struct pipe_arg
*pipe
= &pipes
[i
];
1593 if (pipe
->mode
== NULL
)
1597 dev
->mode
.width
+= pipe
->mode
->hdisplay
;
1598 if (dev
->mode
.height
< pipe
->mode
->vdisplay
)
1599 dev
->mode
.height
= pipe
->mode
->vdisplay
;
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
))
1616 for (i
= 0; i
< count
; i
++) {
1617 struct pipe_arg
*pipe
= &pipes
[i
];
1620 if (pipe
->mode
== NULL
)
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
,
1637 /* XXX: Actually check if this is needed */
1638 drmModeDirtyFB(dev
->fd
, dev
->mode
.fb_id
, NULL
, 0);
1641 x
+= pipe
->mode
->hdisplay
;
1644 fprintf(stderr
, "failed to set mode: %s\n", strerror(errno
));
1648 set_gamma(dev
, pipe
->crtc_id
, pipe
->fourcc
);
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 */
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
,
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
)
1678 for (i
= 0; i
< count
; i
++) {
1679 struct pipe_arg
*pipe
= &pipes
[i
];
1681 if (pipe
->mode
== NULL
)
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
);
1697 bo_destroy(dev
->mode
.bo
);
1700 static void set_planes(struct device
*dev
, struct plane_arg
*p
, unsigned int count
)
1704 /* set up planes/overlays */
1705 for (i
= 0; i
< count
; i
++)
1706 if (set_plane(dev
, &p
[i
]))
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};
1717 /* maybe make cursor width/height configurable some day */
1721 /* create cursor bo.. just using PATTERN_PLAIN as it has
1724 bo
= bo_create(dev
->fd
, DRM_FORMAT_ARGB8888
, cw
, ch
, handles
, pitches
,
1725 offsets
, UTIL_PATTERN_PLAIN
);
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],
1735 pipe
->mode
->hdisplay
, pipe
->mode
->vdisplay
,
1738 fprintf(stderr
, "failed to init cursor for CRTC[%u]\n",
1747 static void clear_cursors(struct device
*dev
)
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
;
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
))
1767 for (i
= 0; i
< count
; i
++) {
1768 struct pipe_arg
*pipe
= &pipes
[i
];
1770 if (pipe
->mode
== NULL
)
1773 ret
= drmModePageFlip(dev
->fd
, pipe
->crtc_id
,
1774 other_fb_id
, DRM_MODE_PAGE_FLIP_EVENT
,
1777 fprintf(stderr
, "failed to page flip: %s\n", strerror(errno
));
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
;
1794 struct pollfd pfd
[2];
1797 pfd
[0].events
= POLLIN
;
1799 pfd
[1].events
= POLLIN
;
1801 if (poll(pfd
, 2, -1) < 0) {
1802 fprintf(stderr
, "poll error\n");
1809 struct timeval timeout
= { .tv_sec
= 3, .tv_usec
= 0 };
1814 FD_SET(dev
->fd
, &fds
);
1815 ret
= select(dev
->fd
+ 1, &fds
, NULL
, NULL
, &timeout
);
1818 fprintf(stderr
, "select timed out or error (ret %d)\n",
1821 } else if (FD_ISSET(0, &fds
)) {
1826 drmHandleEvent(dev
->fd
, &evctx
);
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
)
1844 pipe
->crtc_id
= (uint32_t)-1;
1845 strcpy(pipe
->format_str
, "XR24");
1847 /* Count the number of connectors and allocate them. */
1849 for (p
= arg
; *p
&& *p
!= ':' && *p
!= '@'; ++p
) {
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
)
1859 /* Parse the connectors. */
1860 for (i
= 0, p
= arg
; i
< pipe
->num_cons
; ++i
, p
= endp
+ 1) {
1861 endp
= strpbrk(p
, ",@:");
1865 pipe
->cons
[i
] = strndup(p
, endp
- p
);
1871 if (i
!= pipe
->num_cons
- 1)
1874 /* Parse the remaining parameters. */
1879 pipe
->crtc_id
= strtoul(arg
, &endp
, 10);
1886 /* Search for the vertical refresh or the format. */
1887 p
= strpbrk(arg
, "-@");
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';
1895 pipe
->vrefresh
= strtof(p
+ 1, &endp
);
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
);
1913 static int parse_plane(struct plane_arg
*plane
, const char *p
)
1917 plane
->plane_id
= strtoul(p
, &end
, 10);
1922 plane
->crtc_id
= strtoul(p
, &end
, 10);
1927 plane
->w
= strtoul(p
, &end
, 10);
1932 plane
->h
= strtoul(p
, &end
, 10);
1934 if (*end
== '+' || *end
== '-') {
1935 plane
->x
= strtol(end
, &end
, 10);
1936 if (*end
!= '+' && *end
!= '-')
1938 plane
->y
= strtol(end
, &end
, 10);
1940 plane
->has_position
= true;
1945 plane
->scale
= strtod(p
, &end
);
1946 if (plane
->scale
<= 0.0)
1953 strncpy(plane
->format_str
, end
+ 1, 4);
1954 plane
->format_str
[4] = '\0';
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
);
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)
1974 p
->name
[DRM_PROP_NAME_LEN
] = '\0';
1979 static void parse_fill_patterns(char *arg
)
1981 char *fill
= strtok(arg
, ",");
1984 primary_fill
= util_pattern_enum(fill
);
1985 fill
= strtok(NULL
, ",");
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");
2020 static char optstr
[] = "acdD:efF:M:P:ps:Cvrw:";
2022 int main(int argc
, char **argv
)
2027 int encoders
= 0, connectors
= 0, crtcs
= 0, planes
= 0, framebuffers
= 0;
2028 int drop_master
= 0;
2030 int test_cursor
= 0;
2031 int set_preferred
= 0;
2033 char *device
= NULL
;
2034 char *module
= NULL
;
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;
2044 memset(&dev
, 0, sizeof dev
);
2047 while ((c
= getopt(argc
, argv
, optstr
)) != -1) {
2053 /* Preserve the default behaviour of dumping all information. */
2061 /* Preserve the default behaviour of dumping all information. */
2074 parse_fill_patterns(optarg
);
2078 /* Preserve the default behaviour of dumping all information. */
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");
2088 memset(&plane_args
[plane_count
], 0, sizeof(*plane_args
));
2090 if (parse_plane(&plane_args
[plane_count
], optarg
) < 0)
2100 pipe_args
= realloc(pipe_args
,
2101 (count
+ 1) * sizeof *pipe_args
);
2102 if (pipe_args
== NULL
) {
2103 fprintf(stderr
, "memory allocation failed\n");
2106 memset(&pipe_args
[count
], 0, sizeof(*pipe_args
));
2108 if (parse_connector(&pipe_args
[count
], optarg
) < 0)
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");
2129 memset(&prop_args
[prop_count
], 0, sizeof(*prop_args
));
2131 if (parse_property(&prop_args
[prop_count
], optarg
) < 0)
2142 /* Dump all the details when no* arguments are provided. */
2144 encoders
= connectors
= crtcs
= planes
= framebuffers
= 1;
2146 if (test_vsync
&& !count
) {
2147 fprintf(stderr
, "page flipping requires at least one -s option.\n");
2150 if (set_preferred
&& count
) {
2151 fprintf(stderr
, "cannot use -r (preferred) when -s (mode) is set\n");
2155 if (set_preferred
&& plane_count
) {
2156 fprintf(stderr
, "cannot use -r (preferred) when -P (plane) is set\n");
2160 dev
.fd
= util_open(device
, module
);
2165 ret
= drmSetClientCap(dev
.fd
, DRM_CLIENT_CAP_ATOMIC
, 1);
2167 fprintf(stderr
, "no atomic modesetting support: %s\n", strerror(errno
));
2173 dev
.use_atomic
= use_atomic
;
2175 dev
.resources
= get_resources(&dev
);
2176 if (!dev
.resources
) {
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
)) {
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");
2204 if (set_preferred
|| count
)
2205 set_mode(&dev
, pipe_args
, count
);
2208 atomic_set_planes(&dev
, plane_args
, plane_count
, false);
2210 ret
= drmModeAtomicCommit(dev
.fd
, dev
.req
, DRM_MODE_ATOMIC_ALLOW_MODESET
, NULL
);
2212 fprintf(stderr
, "Atomic Commit failed [1]\n");
2217 atomic_test_page_flip(&dev
, pipe_args
, plane_args
, plane_count
);
2220 drmDropMaster(dev
.fd
);
2224 drmModeAtomicFree(dev
.req
);
2225 dev
.req
= drmModeAtomicAlloc();
2227 /* XXX: properly teardown the preferred mode/plane state */
2229 atomic_clear_planes(&dev
, plane_args
, plane_count
);
2232 atomic_clear_mode(&dev
, pipe_args
, count
);
2234 ret
= drmModeAtomicCommit(dev
.fd
, dev
.req
, DRM_MODE_ATOMIC_ALLOW_MODESET
, NULL
);
2236 fprintf(stderr
, "Atomic Commit failed\n");
2239 atomic_clear_FB(&dev
, plane_args
, plane_count
);
2242 drmModeAtomicFree(dev
.req
);
2244 if (set_preferred
|| count
|| plane_count
) {
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");
2253 if (set_preferred
|| count
)
2254 set_mode(&dev
, pipe_args
, count
);
2257 set_planes(&dev
, plane_args
, plane_count
);
2260 set_cursors(&dev
, pipe_args
, count
);
2263 test_page_flip(&dev
, pipe_args
, count
);
2266 drmDropMaster(dev
.fd
);
2271 clear_cursors(&dev
);
2274 clear_planes(&dev
, plane_args
, plane_count
);
2276 if (set_preferred
|| count
)
2281 free_resources(dev
.resources
);