OSDN Git Service

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