OSDN Git Service

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