OSDN Git Service

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