7d436b5661b12c67225773c0a7d20ffa1f91b5f4
[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 #include "config.h"
41
42 #include <assert.h>
43 #include <ctype.h>
44 #include <stdbool.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <stdint.h>
48 #include <inttypes.h>
49 #include <unistd.h>
50 #include <string.h>
51 #include <errno.h>
52 #include <sys/poll.h>
53 #include <sys/time.h>
54
55 #include "xf86drm.h"
56 #include "xf86drmMode.h"
57 #include "drm_fourcc.h"
58 #include "libkms.h"
59
60 #include "buffers.h"
61 #include "cursor.h"
62
63 struct crtc {
64         drmModeCrtc *crtc;
65         drmModeObjectProperties *props;
66         drmModePropertyRes **props_info;
67         drmModeModeInfo *mode;
68 };
69
70 struct encoder {
71         drmModeEncoder *encoder;
72 };
73
74 struct connector {
75         drmModeConnector *connector;
76         drmModeObjectProperties *props;
77         drmModePropertyRes **props_info;
78 };
79
80 struct fb {
81         drmModeFB *fb;
82 };
83
84 struct plane {
85         drmModePlane *plane;
86         drmModeObjectProperties *props;
87         drmModePropertyRes **props_info;
88 };
89
90 struct resources {
91         drmModeRes *res;
92         drmModePlaneRes *plane_res;
93
94         struct crtc *crtcs;
95         struct encoder *encoders;
96         struct connector *connectors;
97         struct fb *fbs;
98         struct plane *planes;
99 };
100
101 struct device {
102         int fd;
103
104         struct resources *resources;
105         struct kms_driver *kms;
106
107         struct {
108                 unsigned int width;
109                 unsigned int height;
110
111                 unsigned int fb_id;
112                 struct kms_bo *bo;
113         } mode;
114 };
115
116 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
117
118 struct type_name {
119         int type;
120         const char *name;
121 };
122
123 #define type_name_fn(res) \
124 const char * res##_str(int type) {                      \
125         unsigned int i;                                 \
126         for (i = 0; i < ARRAY_SIZE(res##_names); i++) { \
127                 if (res##_names[i].type == type)        \
128                         return res##_names[i].name;     \
129         }                                               \
130         return "(invalid)";                             \
131 }
132
133 struct type_name encoder_type_names[] = {
134         { DRM_MODE_ENCODER_NONE, "none" },
135         { DRM_MODE_ENCODER_DAC, "DAC" },
136         { DRM_MODE_ENCODER_TMDS, "TMDS" },
137         { DRM_MODE_ENCODER_LVDS, "LVDS" },
138         { DRM_MODE_ENCODER_TVDAC, "TVDAC" },
139 };
140
141 static type_name_fn(encoder_type)
142
143 struct type_name connector_status_names[] = {
144         { DRM_MODE_CONNECTED, "connected" },
145         { DRM_MODE_DISCONNECTED, "disconnected" },
146         { DRM_MODE_UNKNOWNCONNECTION, "unknown" },
147 };
148
149 static type_name_fn(connector_status)
150
151 struct type_name connector_type_names[] = {
152         { DRM_MODE_CONNECTOR_Unknown, "unknown" },
153         { DRM_MODE_CONNECTOR_VGA, "VGA" },
154         { DRM_MODE_CONNECTOR_DVII, "DVI-I" },
155         { DRM_MODE_CONNECTOR_DVID, "DVI-D" },
156         { DRM_MODE_CONNECTOR_DVIA, "DVI-A" },
157         { DRM_MODE_CONNECTOR_Composite, "composite" },
158         { DRM_MODE_CONNECTOR_SVIDEO, "s-video" },
159         { DRM_MODE_CONNECTOR_LVDS, "LVDS" },
160         { DRM_MODE_CONNECTOR_Component, "component" },
161         { DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN" },
162         { DRM_MODE_CONNECTOR_DisplayPort, "DP" },
163         { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" },
164         { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" },
165         { DRM_MODE_CONNECTOR_TV, "TV" },
166         { DRM_MODE_CONNECTOR_eDP, "eDP" },
167 };
168
169 static type_name_fn(connector_type)
170
171 #define bit_name_fn(res)                                        \
172 const char * res##_str(int type) {                              \
173         unsigned int i;                                         \
174         const char *sep = "";                                   \
175         for (i = 0; i < ARRAY_SIZE(res##_names); i++) {         \
176                 if (type & (1 << i)) {                          \
177                         printf("%s%s", sep, res##_names[i]);    \
178                         sep = ", ";                             \
179                 }                                               \
180         }                                                       \
181         return NULL;                                            \
182 }
183
184 static const char *mode_type_names[] = {
185         "builtin",
186         "clock_c",
187         "crtc_c",
188         "preferred",
189         "default",
190         "userdef",
191         "driver",
192 };
193
194 static bit_name_fn(mode_type)
195
196 static const char *mode_flag_names[] = {
197         "phsync",
198         "nhsync",
199         "pvsync",
200         "nvsync",
201         "interlace",
202         "dblscan",
203         "csync",
204         "pcsync",
205         "ncsync",
206         "hskew",
207         "bcast",
208         "pixmux",
209         "dblclk",
210         "clkdiv2"
211 };
212
213 static bit_name_fn(mode_flag)
214
215 static void dump_encoders(struct device *dev)
216 {
217         drmModeEncoder *encoder;
218         int i;
219
220         printf("Encoders:\n");
221         printf("id\tcrtc\ttype\tpossible crtcs\tpossible clones\t\n");
222         for (i = 0; i < dev->resources->res->count_encoders; i++) {
223                 encoder = dev->resources->encoders[i].encoder;
224                 if (!encoder)
225                         continue;
226
227                 printf("%d\t%d\t%s\t0x%08x\t0x%08x\n",
228                        encoder->encoder_id,
229                        encoder->crtc_id,
230                        encoder_type_str(encoder->encoder_type),
231                        encoder->possible_crtcs,
232                        encoder->possible_clones);
233         }
234         printf("\n");
235 }
236
237 static void dump_mode(drmModeModeInfo *mode)
238 {
239         printf("  %s %d %d %d %d %d %d %d %d %d",
240                mode->name,
241                mode->vrefresh,
242                mode->hdisplay,
243                mode->hsync_start,
244                mode->hsync_end,
245                mode->htotal,
246                mode->vdisplay,
247                mode->vsync_start,
248                mode->vsync_end,
249                mode->vtotal);
250
251         printf(" flags: ");
252         mode_flag_str(mode->flags);
253         printf("; type: ");
254         mode_type_str(mode->type);
255         printf("\n");
256 }
257
258 static void dump_blob(struct device *dev, uint32_t blob_id)
259 {
260         uint32_t i;
261         unsigned char *blob_data;
262         drmModePropertyBlobPtr blob;
263
264         blob = drmModeGetPropertyBlob(dev->fd, blob_id);
265         if (!blob)
266                 return;
267
268         blob_data = blob->data;
269
270         for (i = 0; i < blob->length; i++) {
271                 if (i % 16 == 0)
272                         printf("\n\t\t\t");
273                 printf("%.2hhx", blob_data[i]);
274         }
275         printf("\n");
276
277         drmModeFreePropertyBlob(blob);
278 }
279
280 static void dump_prop(struct device *dev, drmModePropertyPtr prop,
281                       uint32_t prop_id, uint64_t value)
282 {
283         int i;
284         printf("\t%d", prop_id);
285         if (!prop) {
286                 printf("\n");
287                 return;
288         }
289
290         printf(" %s:\n", prop->name);
291
292         printf("\t\tflags:");
293         if (prop->flags & DRM_MODE_PROP_PENDING)
294                 printf(" pending");
295         if (prop->flags & DRM_MODE_PROP_RANGE)
296                 printf(" range");
297         if (prop->flags & DRM_MODE_PROP_IMMUTABLE)
298                 printf(" immutable");
299         if (prop->flags & DRM_MODE_PROP_ENUM)
300                 printf(" enum");
301         if (prop->flags & DRM_MODE_PROP_BITMASK)
302                 printf(" bitmask");
303         if (prop->flags & DRM_MODE_PROP_BLOB)
304                 printf(" blob");
305         printf("\n");
306
307         if (prop->flags & DRM_MODE_PROP_RANGE) {
308                 printf("\t\tvalues:");
309                 for (i = 0; i < prop->count_values; i++)
310                         printf(" %"PRIu64, prop->values[i]);
311                 printf("\n");
312         }
313
314         if (prop->flags & DRM_MODE_PROP_ENUM) {
315                 printf("\t\tenums:");
316                 for (i = 0; i < prop->count_enums; i++)
317                         printf(" %s=%llu", prop->enums[i].name,
318                                prop->enums[i].value);
319                 printf("\n");
320         } else if (prop->flags & DRM_MODE_PROP_BITMASK) {
321                 printf("\t\tvalues:");
322                 for (i = 0; i < prop->count_enums; i++)
323                         printf(" %s=0x%llx", prop->enums[i].name,
324                                (1LL << prop->enums[i].value));
325                 printf("\n");
326         } else {
327                 assert(prop->count_enums == 0);
328         }
329
330         if (prop->flags & DRM_MODE_PROP_BLOB) {
331                 printf("\t\tblobs:\n");
332                 for (i = 0; i < prop->count_blobs; i++)
333                         dump_blob(dev, prop->blob_ids[i]);
334                 printf("\n");
335         } else {
336                 assert(prop->count_blobs == 0);
337         }
338
339         printf("\t\tvalue:");
340         if (prop->flags & DRM_MODE_PROP_BLOB)
341                 dump_blob(dev, value);
342         else
343                 printf(" %"PRIu64"\n", value);
344 }
345
346 static void dump_connectors(struct device *dev)
347 {
348         int i, j;
349
350         printf("Connectors:\n");
351         printf("id\tencoder\tstatus\t\ttype\tsize (mm)\tmodes\tencoders\n");
352         for (i = 0; i < dev->resources->res->count_connectors; i++) {
353                 struct connector *_connector = &dev->resources->connectors[i];
354                 drmModeConnector *connector = _connector->connector;
355                 if (!connector)
356                         continue;
357
358                 printf("%d\t%d\t%s\t%s\t%dx%d\t\t%d\t",
359                        connector->connector_id,
360                        connector->encoder_id,
361                        connector_status_str(connector->connection),
362                        connector_type_str(connector->connector_type),
363                        connector->mmWidth, connector->mmHeight,
364                        connector->count_modes);
365
366                 for (j = 0; j < connector->count_encoders; j++)
367                         printf("%s%d", j > 0 ? ", " : "", connector->encoders[j]);
368                 printf("\n");
369
370                 if (connector->count_modes) {
371                         printf("  modes:\n");
372                         printf("\tname refresh (Hz) hdisp hss hse htot vdisp "
373                                "vss vse vtot)\n");
374                         for (j = 0; j < connector->count_modes; j++)
375                                 dump_mode(&connector->modes[j]);
376                 }
377
378                 if (_connector->props) {
379                         printf("  props:\n");
380                         for (j = 0; j < (int)_connector->props->count_props; j++)
381                                 dump_prop(dev, _connector->props_info[j],
382                                           _connector->props->props[j],
383                                           _connector->props->prop_values[j]);
384                 }
385         }
386         printf("\n");
387 }
388
389 static void dump_crtcs(struct device *dev)
390 {
391         int i;
392         uint32_t j;
393
394         printf("CRTCs:\n");
395         printf("id\tfb\tpos\tsize\n");
396         for (i = 0; i < dev->resources->res->count_crtcs; i++) {
397                 struct crtc *_crtc = &dev->resources->crtcs[i];
398                 drmModeCrtc *crtc = _crtc->crtc;
399                 if (!crtc)
400                         continue;
401
402                 printf("%d\t%d\t(%d,%d)\t(%dx%d)\n",
403                        crtc->crtc_id,
404                        crtc->buffer_id,
405                        crtc->x, crtc->y,
406                        crtc->width, crtc->height);
407                 dump_mode(&crtc->mode);
408
409                 if (_crtc->props) {
410                         printf("  props:\n");
411                         for (j = 0; j < _crtc->props->count_props; j++)
412                                 dump_prop(dev, _crtc->props_info[j],
413                                           _crtc->props->props[j],
414                                           _crtc->props->prop_values[j]);
415                 } else {
416                         printf("  no properties found\n");
417                 }
418         }
419         printf("\n");
420 }
421
422 static void dump_framebuffers(struct device *dev)
423 {
424         drmModeFB *fb;
425         int i;
426
427         printf("Frame buffers:\n");
428         printf("id\tsize\tpitch\n");
429         for (i = 0; i < dev->resources->res->count_fbs; i++) {
430                 fb = dev->resources->fbs[i].fb;
431                 if (!fb)
432                         continue;
433
434                 printf("%u\t(%ux%u)\t%u\n",
435                        fb->fb_id,
436                        fb->width, fb->height,
437                        fb->pitch);
438         }
439         printf("\n");
440 }
441
442 static void dump_planes(struct device *dev)
443 {
444         unsigned int i, j;
445
446         printf("Planes:\n");
447         printf("id\tcrtc\tfb\tCRTC x,y\tx,y\tgamma size\tpossible crtcs\n");
448
449         if (!dev->resources->plane_res)
450                 return;
451
452         for (i = 0; i < dev->resources->plane_res->count_planes; i++) {
453                 struct plane *plane = &dev->resources->planes[i];
454                 drmModePlane *ovr = plane->plane;
455                 if (!ovr)
456                         continue;
457
458                 printf("%d\t%d\t%d\t%d,%d\t\t%d,%d\t%-8d\t0x%08x\n",
459                        ovr->plane_id, ovr->crtc_id, ovr->fb_id,
460                        ovr->crtc_x, ovr->crtc_y, ovr->x, ovr->y,
461                        ovr->gamma_size, ovr->possible_crtcs);
462
463                 if (!ovr->count_formats)
464                         continue;
465
466                 printf("  formats:");
467                 for (j = 0; j < ovr->count_formats; j++)
468                         printf(" %4.4s", (char *)&ovr->formats[j]);
469                 printf("\n");
470
471                 if (plane->props) {
472                         printf("  props:\n");
473                         for (j = 0; j < plane->props->count_props; j++)
474                                 dump_prop(dev, plane->props_info[j],
475                                           plane->props->props[j],
476                                           plane->props->prop_values[j]);
477                 } else {
478                         printf("  no properties found\n");
479                 }
480         }
481         printf("\n");
482
483         return;
484 }
485
486 static void free_resources(struct resources *res)
487 {
488         if (!res)
489                 return;
490
491 #define free_resource(_res, __res, type, Type)                                  \
492         do {                                                                    \
493                 int i;                                                          \
494                 if (!(_res)->type##s)                                           \
495                         break;                                                  \
496                 for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) {     \
497                         if (!(_res)->type##s[i].type)                           \
498                                 break;                                          \
499                         drmModeFree##Type((_res)->type##s[i].type);             \
500                 }                                                               \
501                 free((_res)->type##s);                                          \
502         } while (0)
503
504 #define free_properties(_res, __res, type)                                      \
505         do {                                                                    \
506                 int i;                                                          \
507                 for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) {     \
508                         drmModeFreeObjectProperties(res->type##s[i].props);     \
509                         free(res->type##s[i].props_info);                       \
510                 }                                                               \
511         } while (0)
512
513         if (res->res) {
514                 free_properties(res, res, crtc);
515
516                 free_resource(res, res, crtc, Crtc);
517                 free_resource(res, res, encoder, Encoder);
518                 free_resource(res, res, connector, Connector);
519                 free_resource(res, res, fb, FB);
520
521                 drmModeFreeResources(res->res);
522         }
523
524         if (res->plane_res) {
525                 free_properties(res, plane_res, plane);
526
527                 free_resource(res, plane_res, plane, Plane);
528
529                 drmModeFreePlaneResources(res->plane_res);
530         }
531
532         free(res);
533 }
534
535 static struct resources *get_resources(struct device *dev)
536 {
537         struct resources *res;
538         int i;
539
540         res = malloc(sizeof *res);
541         if (res == 0)
542                 return NULL;
543
544         memset(res, 0, sizeof *res);
545
546         res->res = drmModeGetResources(dev->fd);
547         if (!res->res) {
548                 fprintf(stderr, "drmModeGetResources failed: %s\n",
549                         strerror(errno));
550                 goto error;
551         }
552
553         res->crtcs = malloc(res->res->count_crtcs * sizeof *res->crtcs);
554         res->encoders = malloc(res->res->count_encoders * sizeof *res->encoders);
555         res->connectors = malloc(res->res->count_connectors * sizeof *res->connectors);
556         res->fbs = malloc(res->res->count_fbs * sizeof *res->fbs);
557
558         if (!res->crtcs || !res->encoders || !res->connectors || !res->fbs)
559                 goto error;
560
561         memset(res->crtcs , 0, res->res->count_crtcs * sizeof *res->crtcs);
562         memset(res->encoders, 0, res->res->count_encoders * sizeof *res->encoders);
563         memset(res->connectors, 0, res->res->count_connectors * sizeof *res->connectors);
564         memset(res->fbs, 0, res->res->count_fbs * sizeof *res->fbs);
565
566 #define get_resource(_res, __res, type, Type)                                   \
567         do {                                                                    \
568                 int i;                                                          \
569                 for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) {     \
570                         (_res)->type##s[i].type =                               \
571                                 drmModeGet##Type(dev->fd, (_res)->__res->type##s[i]); \
572                         if (!(_res)->type##s[i].type)                           \
573                                 fprintf(stderr, "could not get %s %i: %s\n",    \
574                                         #type, (_res)->__res->type##s[i],       \
575                                         strerror(errno));                       \
576                 }                                                               \
577         } while (0)
578
579         get_resource(res, res, crtc, Crtc);
580         get_resource(res, res, encoder, Encoder);
581         get_resource(res, res, connector, Connector);
582         get_resource(res, res, fb, FB);
583
584 #define get_properties(_res, __res, type, Type)                                 \
585         do {                                                                    \
586                 int i;                                                          \
587                 for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) {     \
588                         struct type *obj = &res->type##s[i];                    \
589                         unsigned int j;                                         \
590                         obj->props =                                            \
591                                 drmModeObjectGetProperties(dev->fd, obj->type->type##_id, \
592                                                            DRM_MODE_OBJECT_##Type); \
593                         if (!obj->props) {                                      \
594                                 fprintf(stderr,                                 \
595                                         "could not get %s %i properties: %s\n", \
596                                         #type, obj->type->type##_id,            \
597                                         strerror(errno));                       \
598                                 continue;                                       \
599                         }                                                       \
600                         obj->props_info = malloc(obj->props->count_props *      \
601                                                  sizeof *obj->props_info);      \
602                         if (!obj->props_info)                                   \
603                                 continue;                                       \
604                         for (j = 0; j < obj->props->count_props; ++j)           \
605                                 obj->props_info[j] =                            \
606                                         drmModeGetProperty(dev->fd, obj->props->props[j]); \
607                 }                                                               \
608         } while (0)
609
610         get_properties(res, res, crtc, CRTC);
611         get_properties(res, res, connector, CONNECTOR);
612
613         for (i = 0; i < res->res->count_crtcs; ++i)
614                 res->crtcs[i].mode = &res->crtcs[i].crtc->mode;
615
616         res->plane_res = drmModeGetPlaneResources(dev->fd);
617         if (!res->plane_res) {
618                 fprintf(stderr, "drmModeGetPlaneResources failed: %s\n",
619                         strerror(errno));
620                 return res;
621         }
622
623         res->planes = malloc(res->plane_res->count_planes * sizeof *res->planes);
624         if (!res->planes)
625                 goto error;
626
627         memset(res->planes, 0, res->plane_res->count_planes * sizeof *res->planes);
628
629         get_resource(res, plane_res, plane, Plane);
630         get_properties(res, plane_res, plane, PLANE);
631
632         return res;
633
634 error:
635         free_resources(res);
636         return NULL;
637 }
638
639 static int get_crtc_index(struct device *dev, uint32_t id)
640 {
641         int i;
642
643         for (i = 0; i < dev->resources->res->count_crtcs; ++i) {
644                 drmModeCrtc *crtc = dev->resources->crtcs[i].crtc;
645                 if (crtc && crtc->crtc_id == id)
646                         return i;
647         }
648
649         return -1;
650 }
651
652 static drmModeConnector *get_connector_by_id(struct device *dev, uint32_t id)
653 {
654         drmModeConnector *connector;
655         int i;
656
657         for (i = 0; i < dev->resources->res->count_connectors; i++) {
658                 connector = dev->resources->connectors[i].connector;
659                 if (connector && connector->connector_id == id)
660                         return connector;
661         }
662
663         return NULL;
664 }
665
666 static drmModeEncoder *get_encoder_by_id(struct device *dev, uint32_t id)
667 {
668         drmModeEncoder *encoder;
669         int i;
670
671         for (i = 0; i < dev->resources->res->count_encoders; i++) {
672                 encoder = dev->resources->encoders[i].encoder;
673                 if (encoder && encoder->encoder_id == id)
674                         return encoder;
675         }
676
677         return NULL;
678 }
679
680 /* -----------------------------------------------------------------------------
681  * Pipes and planes
682  */
683
684 /*
685  * Mode setting with the kernel interfaces is a bit of a chore.
686  * First you have to find the connector in question and make sure the
687  * requested mode is available.
688  * Then you need to find the encoder attached to that connector so you
689  * can bind it with a free crtc.
690  */
691 struct pipe_arg {
692         uint32_t *con_ids;
693         unsigned int num_cons;
694         uint32_t crtc_id;
695         char mode_str[64];
696         char format_str[5];
697         unsigned int vrefresh;
698         unsigned int fourcc;
699         drmModeModeInfo *mode;
700         struct crtc *crtc;
701         unsigned int fb_id[2], current_fb_id;
702         struct timeval start;
703
704         int swap_count;
705 };
706
707 struct plane_arg {
708         uint32_t crtc_id;  /* the id of CRTC to bind to */
709         bool has_position;
710         int32_t x, y;
711         uint32_t w, h;
712         double scale;
713         unsigned int fb_id;
714         char format_str[5]; /* need to leave room for terminating \0 */
715         unsigned int fourcc;
716 };
717
718 static drmModeModeInfo *
719 connector_find_mode(struct device *dev, uint32_t con_id, const char *mode_str,
720         const unsigned int vrefresh)
721 {
722         drmModeConnector *connector;
723         drmModeModeInfo *mode;
724         int i;
725
726         connector = get_connector_by_id(dev, con_id);
727         if (!connector || !connector->count_modes)
728                 return NULL;
729
730         for (i = 0; i < connector->count_modes; i++) {
731                 mode = &connector->modes[i];
732                 if (!strcmp(mode->name, mode_str)) {
733                         /* If the vertical refresh frequency is not specified then return the
734                          * first mode that match with the name. Else, return the mode that match
735                          * the name and the specified vertical refresh frequency.
736                          */
737                         if (vrefresh == 0)
738                                 return mode;
739                         else if (mode->vrefresh == vrefresh)
740                                 return mode;
741                 }
742         }
743
744         return NULL;
745 }
746
747 static struct crtc *pipe_find_crtc(struct device *dev, struct pipe_arg *pipe)
748 {
749         uint32_t possible_crtcs = ~0;
750         uint32_t active_crtcs = 0;
751         unsigned int crtc_idx;
752         unsigned int i;
753         int j;
754
755         for (i = 0; i < pipe->num_cons; ++i) {
756                 uint32_t crtcs_for_connector = 0;
757                 drmModeConnector *connector;
758                 drmModeEncoder *encoder;
759                 int idx;
760
761                 connector = get_connector_by_id(dev, pipe->con_ids[i]);
762                 if (!connector)
763                         return NULL;
764
765                 for (j = 0; j < connector->count_encoders; ++j) {
766                         encoder = get_encoder_by_id(dev, connector->encoders[j]);
767                         if (!encoder)
768                                 continue;
769
770                         crtcs_for_connector |= encoder->possible_crtcs;
771
772                         idx = get_crtc_index(dev, encoder->crtc_id);
773                         if (idx >= 0)
774                                 active_crtcs |= 1 << idx;
775                 }
776
777                 possible_crtcs &= crtcs_for_connector;
778         }
779
780         if (!possible_crtcs)
781                 return NULL;
782
783         /* Return the first possible and active CRTC if one exists, or the first
784          * possible CRTC otherwise.
785          */
786         if (possible_crtcs & active_crtcs)
787                 crtc_idx = ffs(possible_crtcs & active_crtcs);
788         else
789                 crtc_idx = ffs(possible_crtcs);
790
791         return &dev->resources->crtcs[crtc_idx - 1];
792 }
793
794 static int pipe_find_crtc_and_mode(struct device *dev, struct pipe_arg *pipe)
795 {
796         drmModeModeInfo *mode = NULL;
797         int i;
798
799         pipe->mode = NULL;
800
801         for (i = 0; i < (int)pipe->num_cons; i++) {
802                 mode = connector_find_mode(dev, pipe->con_ids[i],
803                                            pipe->mode_str, pipe->vrefresh);
804                 if (mode == NULL) {
805                         fprintf(stderr,
806                                 "failed to find mode \"%s\" for connector %u\n",
807                                 pipe->mode_str, pipe->con_ids[i]);
808                         return -EINVAL;
809                 }
810         }
811
812         /* If the CRTC ID was specified, get the corresponding CRTC. Otherwise
813          * locate a CRTC that can be attached to all the connectors.
814          */
815         if (pipe->crtc_id != (uint32_t)-1) {
816                 for (i = 0; i < dev->resources->res->count_crtcs; i++) {
817                         struct crtc *crtc = &dev->resources->crtcs[i];
818
819                         if (pipe->crtc_id == crtc->crtc->crtc_id) {
820                                 pipe->crtc = crtc;
821                                 break;
822                         }
823                 }
824         } else {
825                 pipe->crtc = pipe_find_crtc(dev, pipe);
826         }
827
828         if (!pipe->crtc) {
829                 fprintf(stderr, "failed to find CRTC for pipe\n");
830                 return -EINVAL;
831         }
832
833         pipe->mode = mode;
834         pipe->crtc->mode = mode;
835
836         return 0;
837 }
838
839 /* -----------------------------------------------------------------------------
840  * Properties
841  */
842
843 struct property_arg {
844         uint32_t obj_id;
845         uint32_t obj_type;
846         char name[DRM_PROP_NAME_LEN+1];
847         uint32_t prop_id;
848         uint64_t value;
849 };
850
851 static void set_property(struct device *dev, struct property_arg *p)
852 {
853         drmModeObjectProperties *props = NULL;
854         drmModePropertyRes **props_info = NULL;
855         const char *obj_type;
856         int ret;
857         int i;
858
859         p->obj_type = 0;
860         p->prop_id = 0;
861
862 #define find_object(_res, __res, type, Type)                                    \
863         do {                                                                    \
864                 for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) {     \
865                         struct type *obj = &(_res)->type##s[i];                 \
866                         if (obj->type->type##_id != p->obj_id)                  \
867                                 continue;                                       \
868                         p->obj_type = DRM_MODE_OBJECT_##Type;                   \
869                         obj_type = #Type;                                       \
870                         props = obj->props;                                     \
871                         props_info = obj->props_info;                           \
872                 }                                                               \
873         } while(0)                                                              \
874
875         find_object(dev->resources, res, crtc, CRTC);
876         if (p->obj_type == 0)
877                 find_object(dev->resources, res, connector, CONNECTOR);
878         if (p->obj_type == 0)
879                 find_object(dev->resources, plane_res, plane, PLANE);
880         if (p->obj_type == 0) {
881                 fprintf(stderr, "Object %i not found, can't set property\n",
882                         p->obj_id);
883                         return;
884         }
885
886         if (!props) {
887                 fprintf(stderr, "%s %i has no properties\n",
888                         obj_type, p->obj_id);
889                 return;
890         }
891
892         for (i = 0; i < (int)props->count_props; ++i) {
893                 if (!props_info[i])
894                         continue;
895                 if (strcmp(props_info[i]->name, p->name) == 0)
896                         break;
897         }
898
899         if (i == (int)props->count_props) {
900                 fprintf(stderr, "%s %i has no %s property\n",
901                         obj_type, p->obj_id, p->name);
902                 return;
903         }
904
905         p->prop_id = props->props[i];
906
907         ret = drmModeObjectSetProperty(dev->fd, p->obj_id, p->obj_type,
908                                        p->prop_id, p->value);
909         if (ret < 0)
910                 fprintf(stderr, "failed to set %s %i property %s to %" PRIu64 ": %s\n",
911                         obj_type, p->obj_id, p->name, p->value, strerror(errno));
912 }
913
914 /* -------------------------------------------------------------------------- */
915
916 static void
917 page_flip_handler(int fd, unsigned int frame,
918                   unsigned int sec, unsigned int usec, void *data)
919 {
920         struct pipe_arg *pipe;
921         unsigned int new_fb_id;
922         struct timeval end;
923         double t;
924
925         pipe = data;
926         if (pipe->current_fb_id == pipe->fb_id[0])
927                 new_fb_id = pipe->fb_id[1];
928         else
929                 new_fb_id = pipe->fb_id[0];
930
931         drmModePageFlip(fd, pipe->crtc->crtc->crtc_id, new_fb_id,
932                         DRM_MODE_PAGE_FLIP_EVENT, pipe);
933         pipe->current_fb_id = new_fb_id;
934         pipe->swap_count++;
935         if (pipe->swap_count == 60) {
936                 gettimeofday(&end, NULL);
937                 t = end.tv_sec + end.tv_usec * 1e-6 -
938                         (pipe->start.tv_sec + pipe->start.tv_usec * 1e-6);
939                 fprintf(stderr, "freq: %.02fHz\n", pipe->swap_count / t);
940                 pipe->swap_count = 0;
941                 pipe->start = end;
942         }
943 }
944
945 static int set_plane(struct device *dev, struct plane_arg *p)
946 {
947         drmModePlane *ovr;
948         uint32_t handles[4], pitches[4], offsets[4] = {0}; /* we only use [0] */
949         uint32_t plane_id = 0;
950         struct kms_bo *plane_bo;
951         uint32_t plane_flags = 0;
952         int crtc_x, crtc_y, crtc_w, crtc_h;
953         struct crtc *crtc = NULL;
954         unsigned int pipe;
955         unsigned int i;
956
957         /* Find an unused plane which can be connected to our CRTC. Find the
958          * CRTC index first, then iterate over available planes.
959          */
960         for (i = 0; i < (unsigned int)dev->resources->res->count_crtcs; i++) {
961                 if (p->crtc_id == dev->resources->res->crtcs[i]) {
962                         crtc = &dev->resources->crtcs[i];
963                         pipe = i;
964                         break;
965                 }
966         }
967
968         if (!crtc) {
969                 fprintf(stderr, "CRTC %u not found\n", p->crtc_id);
970                 return -1;
971         }
972
973         for (i = 0; i < dev->resources->plane_res->count_planes && !plane_id; i++) {
974                 ovr = dev->resources->planes[i].plane;
975                 if (!ovr)
976                         continue;
977
978                 if ((ovr->possible_crtcs & (1 << pipe)) && !ovr->crtc_id)
979                         plane_id = ovr->plane_id;
980         }
981
982         if (!plane_id) {
983                 fprintf(stderr, "no unused plane available for CRTC %u\n",
984                         crtc->crtc->crtc_id);
985                 return -1;
986         }
987
988         fprintf(stderr, "testing %dx%d@%s overlay plane %u\n",
989                 p->w, p->h, p->format_str, plane_id);
990
991         plane_bo = create_test_buffer(dev->kms, p->fourcc, p->w, p->h, handles,
992                                       pitches, offsets, PATTERN_TILES);
993         if (plane_bo == NULL)
994                 return -1;
995
996         /* just use single plane format for now.. */
997         if (drmModeAddFB2(dev->fd, p->w, p->h, p->fourcc,
998                         handles, pitches, offsets, &p->fb_id, plane_flags)) {
999                 fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
1000                 return -1;
1001         }
1002
1003         crtc_w = p->w * p->scale;
1004         crtc_h = p->h * p->scale;
1005         if (!p->has_position) {
1006                 /* Default to the middle of the screen */
1007                 crtc_x = (crtc->mode->hdisplay - crtc_w) / 2;
1008                 crtc_y = (crtc->mode->vdisplay - crtc_h) / 2;
1009         } else {
1010                 crtc_x = p->x;
1011                 crtc_y = p->y;
1012         }
1013
1014         /* note src coords (last 4 args) are in Q16 format */
1015         if (drmModeSetPlane(dev->fd, plane_id, crtc->crtc->crtc_id, p->fb_id,
1016                             plane_flags, crtc_x, crtc_y, crtc_w, crtc_h,
1017                             0, 0, p->w << 16, p->h << 16)) {
1018                 fprintf(stderr, "failed to enable plane: %s\n",
1019                         strerror(errno));
1020                 return -1;
1021         }
1022
1023         ovr->crtc_id = crtc->crtc->crtc_id;
1024
1025         return 0;
1026 }
1027
1028 static void set_mode(struct device *dev, struct pipe_arg *pipes, unsigned int count)
1029 {
1030         uint32_t handles[4], pitches[4], offsets[4] = {0}; /* we only use [0] */
1031         unsigned int fb_id;
1032         struct kms_bo *bo;
1033         unsigned int i;
1034         unsigned int j;
1035         int ret, x;
1036
1037         dev->mode.width = 0;
1038         dev->mode.height = 0;
1039
1040         for (i = 0; i < count; i++) {
1041                 struct pipe_arg *pipe = &pipes[i];
1042
1043                 ret = pipe_find_crtc_and_mode(dev, pipe);
1044                 if (ret < 0)
1045                         continue;
1046
1047                 dev->mode.width += pipe->mode->hdisplay;
1048                 if (dev->mode.height < pipe->mode->vdisplay)
1049                         dev->mode.height = pipe->mode->vdisplay;
1050         }
1051
1052         bo = create_test_buffer(dev->kms, pipes[0].fourcc,
1053                                 dev->mode.width, dev->mode.height,
1054                                 handles, pitches, offsets, PATTERN_SMPTE);
1055         if (bo == NULL)
1056                 return;
1057
1058         ret = drmModeAddFB2(dev->fd, dev->mode.width, dev->mode.height,
1059                             pipes[0].fourcc, handles, pitches, offsets, &fb_id, 0);
1060         if (ret) {
1061                 fprintf(stderr, "failed to add fb (%ux%u): %s\n",
1062                         dev->mode.width, dev->mode.height, strerror(errno));
1063                 return;
1064         }
1065
1066         x = 0;
1067         for (i = 0; i < count; i++) {
1068                 struct pipe_arg *pipe = &pipes[i];
1069
1070                 if (pipe->mode == NULL)
1071                         continue;
1072
1073                 printf("setting mode %s-%dHz@%s on connectors ",
1074                        pipe->mode_str, pipe->mode->vrefresh, pipe->format_str);
1075                 for (j = 0; j < pipe->num_cons; ++j)
1076                         printf("%u, ", pipe->con_ids[j]);
1077                 printf("crtc %d\n", pipe->crtc->crtc->crtc_id);
1078
1079                 ret = drmModeSetCrtc(dev->fd, pipe->crtc->crtc->crtc_id, fb_id,
1080                                      x, 0, pipe->con_ids, pipe->num_cons,
1081                                      pipe->mode);
1082
1083                 /* XXX: Actually check if this is needed */
1084                 drmModeDirtyFB(dev->fd, fb_id, NULL, 0);
1085
1086                 x += pipe->mode->hdisplay;
1087
1088                 if (ret) {
1089                         fprintf(stderr, "failed to set mode: %s\n", strerror(errno));
1090                         return;
1091                 }
1092         }
1093
1094         dev->mode.bo = bo;
1095         dev->mode.fb_id = fb_id;
1096 }
1097
1098 static void set_planes(struct device *dev, struct plane_arg *p, unsigned int count)
1099 {
1100         unsigned int i;
1101
1102         /* set up planes/overlays */
1103         for (i = 0; i < count; i++)
1104                 if (set_plane(dev, &p[i]))
1105                         return;
1106 }
1107
1108 static void set_cursors(struct device *dev, struct pipe_arg *pipes, unsigned int count)
1109 {
1110         uint32_t handles[4], pitches[4], offsets[4] = {0}; /* we only use [0] */
1111         struct kms_bo *bo;
1112         unsigned int i;
1113         int ret;
1114
1115         /* maybe make cursor width/height configurable some day */
1116         uint32_t cw = 64;
1117         uint32_t ch = 64;
1118
1119         /* create cursor bo.. just using PATTERN_PLAIN as it has
1120          * translucent alpha
1121          */
1122         bo = create_test_buffer(dev->kms, DRM_FORMAT_ARGB8888,
1123                         cw, ch, handles, pitches, offsets, PATTERN_PLAIN);
1124         if (bo == NULL)
1125                 return;
1126
1127         for (i = 0; i < count; i++) {
1128                 struct pipe_arg *pipe = &pipes[i];
1129                 ret = cursor_init(dev->fd, handles[0],
1130                                 pipe->crtc->crtc->crtc_id,
1131                                 pipe->mode->hdisplay, pipe->mode->vdisplay,
1132                                 cw, ch);
1133                 if (ret) {
1134                         fprintf(stderr, "failed to init cursor for CRTC[%u]\n",
1135                                         pipe->crtc_id);
1136                         return;
1137                 }
1138         }
1139
1140         cursor_start();
1141 }
1142
1143 static void clear_cursors(struct device *dev)
1144 {
1145         cursor_stop();
1146 }
1147
1148 static void test_page_flip(struct device *dev, struct pipe_arg *pipes, unsigned int count)
1149 {
1150         uint32_t handles[4], pitches[4], offsets[4] = {0}; /* we only use [0] */
1151         unsigned int other_fb_id;
1152         struct kms_bo *other_bo;
1153         drmEventContext evctx;
1154         unsigned int i;
1155         int ret;
1156
1157         other_bo = create_test_buffer(dev->kms, pipes[0].fourcc,
1158                                       dev->mode.width, dev->mode.height,
1159                                       handles, pitches, offsets, PATTERN_PLAIN);
1160         if (other_bo == NULL)
1161                 return;
1162
1163         ret = drmModeAddFB2(dev->fd, dev->mode.width, dev->mode.height,
1164                             pipes[0].fourcc, handles, pitches, offsets,
1165                             &other_fb_id, 0);
1166         if (ret) {
1167                 fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
1168                 return;
1169         }
1170
1171         for (i = 0; i < count; i++) {
1172                 struct pipe_arg *pipe = &pipes[i];
1173
1174                 if (pipe->mode == NULL)
1175                         continue;
1176
1177                 ret = drmModePageFlip(dev->fd, pipe->crtc->crtc->crtc_id,
1178                                       other_fb_id, DRM_MODE_PAGE_FLIP_EVENT,
1179                                       pipe);
1180                 if (ret) {
1181                         fprintf(stderr, "failed to page flip: %s\n", strerror(errno));
1182                         return;
1183                 }
1184                 gettimeofday(&pipe->start, NULL);
1185                 pipe->swap_count = 0;
1186                 pipe->fb_id[0] = dev->mode.fb_id;
1187                 pipe->fb_id[1] = other_fb_id;
1188                 pipe->current_fb_id = other_fb_id;
1189         }
1190
1191         memset(&evctx, 0, sizeof evctx);
1192         evctx.version = DRM_EVENT_CONTEXT_VERSION;
1193         evctx.vblank_handler = NULL;
1194         evctx.page_flip_handler = page_flip_handler;
1195         
1196         while (1) {
1197 #if 0
1198                 struct pollfd pfd[2];
1199
1200                 pfd[0].fd = 0;
1201                 pfd[0].events = POLLIN;
1202                 pfd[1].fd = fd;
1203                 pfd[1].events = POLLIN;
1204
1205                 if (poll(pfd, 2, -1) < 0) {
1206                         fprintf(stderr, "poll error\n");
1207                         break;
1208                 }
1209
1210                 if (pfd[0].revents)
1211                         break;
1212 #else
1213                 struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 };
1214                 fd_set fds;
1215                 int ret;
1216
1217                 FD_ZERO(&fds);
1218                 FD_SET(0, &fds);
1219                 FD_SET(dev->fd, &fds);
1220                 ret = select(dev->fd + 1, &fds, NULL, NULL, &timeout);
1221
1222                 if (ret <= 0) {
1223                         fprintf(stderr, "select timed out or error (ret %d)\n",
1224                                 ret);
1225                         continue;
1226                 } else if (FD_ISSET(0, &fds)) {
1227                         break;
1228                 }
1229 #endif
1230
1231                 drmHandleEvent(dev->fd, &evctx);
1232         }
1233
1234         kms_bo_destroy(&other_bo);
1235 }
1236
1237 #define min(a, b)       ((a) < (b) ? (a) : (b))
1238
1239 static int parse_connector(struct pipe_arg *pipe, const char *arg)
1240 {
1241         unsigned int len;
1242         unsigned int i;
1243         const char *p;
1244         char *endp;
1245
1246         pipe->vrefresh = 0;
1247         pipe->crtc_id = (uint32_t)-1;
1248         strcpy(pipe->format_str, "XR24");
1249
1250         /* Count the number of connectors and allocate them. */
1251         pipe->num_cons = 1;
1252         for (p = arg; isdigit(*p) || *p == ','; ++p) {
1253                 if (*p == ',')
1254                         pipe->num_cons++;
1255         }
1256
1257         pipe->con_ids = malloc(pipe->num_cons * sizeof *pipe->con_ids);
1258         if (pipe->con_ids == NULL)
1259                 return -1;
1260
1261         /* Parse the connectors. */
1262         for (i = 0, p = arg; i < pipe->num_cons; ++i, p = endp + 1) {
1263                 pipe->con_ids[i] = strtoul(p, &endp, 10);
1264                 if (*endp != ',')
1265                         break;
1266         }
1267
1268         if (i != pipe->num_cons - 1)
1269                 return -1;
1270
1271         /* Parse the remaining parameters. */
1272         if (*endp == '@') {
1273                 arg = endp + 1;
1274                 pipe->crtc_id = strtoul(arg, &endp, 10);
1275         }
1276         if (*endp != ':')
1277                 return -1;
1278
1279         arg = endp + 1;
1280
1281         /* Search for the vertical refresh or the format. */
1282         p = strpbrk(arg, "-@");
1283         if (p == NULL)
1284                 p = arg + strlen(arg);
1285         len = min(sizeof pipe->mode_str - 1, (unsigned int)(p - arg));
1286         strncpy(pipe->mode_str, arg, len);
1287         pipe->mode_str[len] = '\0';
1288
1289         if (*p == '-') {
1290                 pipe->vrefresh = strtoul(p + 1, &endp, 10);
1291                 p = endp;
1292         }
1293
1294         if (*p == '@') {
1295                 strncpy(pipe->format_str, p + 1, 4);
1296                 pipe->format_str[4] = '\0';
1297         }
1298
1299         pipe->fourcc = format_fourcc(pipe->format_str);
1300         if (pipe->fourcc == 0)  {
1301                 fprintf(stderr, "unknown format %s\n", pipe->format_str);
1302                 return -1;
1303         }
1304
1305         return 0;
1306 }
1307
1308 static int parse_plane(struct plane_arg *plane, const char *p)
1309 {
1310         char *end;
1311
1312         memset(plane, 0, sizeof *plane);
1313
1314         plane->crtc_id = strtoul(p, &end, 10);
1315         if (*end != ':')
1316                 return -EINVAL;
1317
1318         p = end + 1;
1319         plane->w = strtoul(p, &end, 10);
1320         if (*end != 'x')
1321                 return -EINVAL;
1322
1323         p = end + 1;
1324         plane->h = strtoul(p, &end, 10);
1325
1326         if (*end == '+' || *end == '-') {
1327                 plane->x = strtol(end, &end, 10);
1328                 if (*end != '+' && *end != '-')
1329                         return -EINVAL;
1330                 plane->y = strtol(end, &end, 10);
1331
1332                 plane->has_position = true;
1333         }
1334
1335         if (*end == '*') {
1336                 p = end + 1;
1337                 plane->scale = strtod(p, &end);
1338                 if (plane->scale <= 0.0)
1339                         return -EINVAL;
1340         } else {
1341                 plane->scale = 1.0;
1342         }
1343
1344         if (*end == '@') {
1345                 p = end + 1;
1346                 if (strlen(p) != 4)
1347                         return -EINVAL;
1348
1349                 strcpy(plane->format_str, p);
1350         } else {
1351                 strcpy(plane->format_str, "XR24");
1352         }
1353
1354         plane->fourcc = format_fourcc(plane->format_str);
1355         if (plane->fourcc == 0) {
1356                 fprintf(stderr, "unknown format %s\n", plane->format_str);
1357                 return -EINVAL;
1358         }
1359
1360         return 0;
1361 }
1362
1363 static int parse_property(struct property_arg *p, const char *arg)
1364 {
1365         if (sscanf(arg, "%d:%32[^:]:%" SCNu64, &p->obj_id, p->name, &p->value) != 3)
1366                 return -1;
1367
1368         p->obj_type = 0;
1369         p->name[DRM_PROP_NAME_LEN] = '\0';
1370
1371         return 0;
1372 }
1373
1374 static void usage(char *name)
1375 {
1376         fprintf(stderr, "usage: %s [-cDdefMPpsCvw]\n", name);
1377
1378         fprintf(stderr, "\n Query options:\n\n");
1379         fprintf(stderr, "\t-c\tlist connectors\n");
1380         fprintf(stderr, "\t-e\tlist encoders\n");
1381         fprintf(stderr, "\t-f\tlist framebuffers\n");
1382         fprintf(stderr, "\t-p\tlist CRTCs and planes (pipes)\n");
1383
1384         fprintf(stderr, "\n Test options:\n\n");
1385         fprintf(stderr, "\t-P <crtc_id>:<w>x<h>[+<x>+<y>][*<scale>][@<format>]\tset a plane\n");
1386         fprintf(stderr, "\t-s <connector_id>[,<connector_id>][@<crtc_id>]:<mode>[-<vrefresh>][@<format>]\tset a mode\n");
1387         fprintf(stderr, "\t-C\ttest hw cursor\n");
1388         fprintf(stderr, "\t-v\ttest vsynced page flipping\n");
1389         fprintf(stderr, "\t-w <obj_id>:<prop_name>:<value>\tset property\n");
1390
1391         fprintf(stderr, "\n Generic options:\n\n");
1392         fprintf(stderr, "\t-d\tdrop master after mode set\n");
1393         fprintf(stderr, "\t-M module\tuse the given driver\n");
1394         fprintf(stderr, "\t-D device\tuse the given device\n");
1395
1396         fprintf(stderr, "\n\tDefault is to dump all info.\n");
1397         exit(0);
1398 }
1399
1400 static int page_flipping_supported(void)
1401 {
1402         /*FIXME: generic ioctl needed? */
1403         return 1;
1404 #if 0
1405         int ret, value;
1406         struct drm_i915_getparam gp;
1407
1408         gp.param = I915_PARAM_HAS_PAGEFLIPPING;
1409         gp.value = &value;
1410
1411         ret = drmCommandWriteRead(fd, DRM_I915_GETPARAM, &gp, sizeof(gp));
1412         if (ret) {
1413                 fprintf(stderr, "drm_i915_getparam: %m\n");
1414                 return 0;
1415         }
1416
1417         return *gp.value;
1418 #endif
1419 }
1420
1421 static int cursor_supported(void)
1422 {
1423         /*FIXME: generic ioctl needed? */
1424         return 1;
1425 }
1426
1427 static char optstr[] = "cdD:efM:P:ps:Cvw:";
1428
1429 int main(int argc, char **argv)
1430 {
1431         struct device dev;
1432
1433         int c;
1434         int encoders = 0, connectors = 0, crtcs = 0, planes = 0, framebuffers = 0;
1435         int drop_master = 0;
1436         int test_vsync = 0;
1437         int test_cursor = 0;
1438         const char *modules[] = { "i915", "radeon", "nouveau", "vmwgfx", "omapdrm", "exynos", "tilcdc", "msm" };
1439         char *device = NULL;
1440         char *module = NULL;
1441         unsigned int i;
1442         int count = 0, plane_count = 0;
1443         unsigned int prop_count = 0;
1444         struct pipe_arg *pipe_args = NULL;
1445         struct plane_arg *plane_args = NULL;
1446         struct property_arg *prop_args = NULL;
1447         unsigned int args = 0;
1448         int ret;
1449
1450         memset(&dev, 0, sizeof dev);
1451
1452         opterr = 0;
1453         while ((c = getopt(argc, argv, optstr)) != -1) {
1454                 args++;
1455
1456                 switch (c) {
1457                 case 'c':
1458                         connectors = 1;
1459                         break;
1460                 case 'D':
1461                         device = optarg;
1462                         args--;
1463                         break;
1464                 case 'd':
1465                         drop_master = 1;
1466                         break;
1467                 case 'e':
1468                         encoders = 1;
1469                         break;
1470                 case 'f':
1471                         framebuffers = 1;
1472                         break;
1473                 case 'M':
1474                         module = optarg;
1475                         /* Preserve the default behaviour of dumping all information. */
1476                         args--;
1477                         break;
1478                 case 'P':
1479                         plane_args = realloc(plane_args,
1480                                              (plane_count + 1) * sizeof *plane_args);
1481                         if (plane_args == NULL) {
1482                                 fprintf(stderr, "memory allocation failed\n");
1483                                 return 1;
1484                         }
1485
1486                         if (parse_plane(&plane_args[plane_count], optarg) < 0)
1487                                 usage(argv[0]);
1488
1489                         plane_count++;
1490                         break;
1491                 case 'p':
1492                         crtcs = 1;
1493                         planes = 1;
1494                         break;
1495                 case 's':
1496                         pipe_args = realloc(pipe_args,
1497                                             (count + 1) * sizeof *pipe_args);
1498                         if (pipe_args == NULL) {
1499                                 fprintf(stderr, "memory allocation failed\n");
1500                                 return 1;
1501                         }
1502
1503                         if (parse_connector(&pipe_args[count], optarg) < 0)
1504                                 usage(argv[0]);
1505
1506                         count++;                                      
1507                         break;
1508                 case 'C':
1509                         test_cursor = 1;
1510                         break;
1511                 case 'v':
1512                         test_vsync = 1;
1513                         break;
1514                 case 'w':
1515                         prop_args = realloc(prop_args,
1516                                            (prop_count + 1) * sizeof *prop_args);
1517                         if (prop_args == NULL) {
1518                                 fprintf(stderr, "memory allocation failed\n");
1519                                 return 1;
1520                         }
1521
1522                         if (parse_property(&prop_args[prop_count], optarg) < 0)
1523                                 usage(argv[0]);
1524
1525                         prop_count++;
1526                         break;
1527                 default:
1528                         usage(argv[0]);
1529                         break;
1530                 }
1531         }
1532
1533         if (!args)
1534                 encoders = connectors = crtcs = planes = framebuffers = 1;
1535
1536         if (module) {
1537                 dev.fd = drmOpen(module, device);
1538                 if (dev.fd < 0) {
1539                         fprintf(stderr, "failed to open device '%s'.\n", module);
1540                         return 1;
1541                 }
1542         } else {
1543                 for (i = 0; i < ARRAY_SIZE(modules); i++) {
1544                         printf("trying to open device '%s'...", modules[i]);
1545                         dev.fd = drmOpen(modules[i], device);
1546                         if (dev.fd < 0) {
1547                                 printf("failed.\n");
1548                         } else {
1549                                 printf("success.\n");
1550                                 break;
1551                         }
1552                 }
1553
1554                 if (dev.fd < 0) {
1555                         fprintf(stderr, "no device found.\n");
1556                         return 1;
1557                 }
1558         }
1559
1560         if (test_vsync && !page_flipping_supported()) {
1561                 fprintf(stderr, "page flipping not supported by drm.\n");
1562                 return -1;
1563         }
1564
1565         if (test_vsync && !count) {
1566                 fprintf(stderr, "page flipping requires at least one -s option.\n");
1567                 return -1;
1568         }
1569
1570         if (test_cursor && !cursor_supported()) {
1571                 fprintf(stderr, "hw cursor not supported by drm.\n");
1572                 return -1;
1573         }
1574
1575         dev.resources = get_resources(&dev);
1576         if (!dev.resources) {
1577                 drmClose(dev.fd);
1578                 return 1;
1579         }
1580
1581 #define dump_resource(dev, res) if (res) dump_##res(dev)
1582
1583         dump_resource(&dev, encoders);
1584         dump_resource(&dev, connectors);
1585         dump_resource(&dev, crtcs);
1586         dump_resource(&dev, planes);
1587         dump_resource(&dev, framebuffers);
1588
1589         for (i = 0; i < prop_count; ++i)
1590                 set_property(&dev, &prop_args[i]);
1591
1592         if (count || plane_count) {
1593                 ret = kms_create(dev.fd, &dev.kms);
1594                 if (ret) {
1595                         fprintf(stderr, "failed to create kms driver: %s\n",
1596                                 strerror(-ret));
1597                         return 1;
1598                 }
1599
1600                 if (count)
1601                         set_mode(&dev, pipe_args, count);
1602
1603                 if (plane_count)
1604                         set_planes(&dev, plane_args, plane_count);
1605
1606                 if (test_cursor)
1607                         set_cursors(&dev, pipe_args, count);
1608
1609                 if (test_vsync)
1610                         test_page_flip(&dev, pipe_args, count);
1611
1612                 if (drop_master)
1613                         drmDropMaster(dev.fd);
1614
1615                 getchar();
1616
1617                 if (test_cursor)
1618                         clear_cursors(&dev);
1619
1620                 kms_bo_destroy(&dev.mode.bo);
1621                 kms_destroy(&dev.kms);
1622         }
1623
1624         free_resources(dev.resources);
1625
1626         return 0;
1627 }