OSDN Git Service

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