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