OSDN Git Service

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