OSDN Git Service

modetest: Don't limit mode set and planes to two instances
[android-x86/external-libdrm.git] / tests / modetest / modetest.c
1 /*
2  * DRM based mode setting test program
3  * Copyright 2008 Tungsten Graphics
4  *   Jakob Bornecrantz <jakob@tungstengraphics.com>
5  * Copyright 2008 Intel Corporation
6  *   Jesse Barnes <jesse.barnes@intel.com>
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a
9  * copy of this software and associated documentation files (the "Software"),
10  * to deal in the Software without restriction, including without limitation
11  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12  * and/or sell copies of the Software, and to permit persons to whom the
13  * Software is furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24  * IN THE SOFTWARE.
25  */
26
27 /*
28  * This fairly simple test program dumps output in a similar format to the
29  * "xrandr" tool everyone knows & loves.  It's necessarily slightly different
30  * since the kernel separates outputs into encoder and connector structures,
31  * each with their own unique ID.  The program also allows test testing of the
32  * memory management and mode setting APIs by allowing the user to specify a
33  * connector and mode to use for mode setting.  If all works as expected, a
34  * blue background should be painted on the monitor attached to the specified
35  * connector after the selected mode is set.
36  *
37  * TODO: use cairo to write the mode info on the selected output once
38  *       the mode has been programmed, along with possible test patterns.
39  */
40 #include "config.h"
41
42 #include <assert.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <stdint.h>
46 #include <inttypes.h>
47 #include <unistd.h>
48 #include <string.h>
49 #include <errno.h>
50 #include <sys/poll.h>
51 #include <sys/time.h>
52
53 #include "xf86drm.h"
54 #include "xf86drmMode.h"
55 #include "drm_fourcc.h"
56 #include "libkms.h"
57
58 #include "buffers.h"
59
60 struct crtc {
61         drmModeCrtc *crtc;
62         drmModeObjectProperties *props;
63         drmModePropertyRes **props_info;
64 };
65
66 struct encoder {
67         drmModeEncoder *encoder;
68 };
69
70 struct connector {
71         drmModeConnector *connector;
72         drmModeObjectProperties *props;
73         drmModePropertyRes **props_info;
74 };
75
76 struct fb {
77         drmModeFB *fb;
78 };
79
80 struct plane {
81         drmModePlane *plane;
82         drmModeObjectProperties *props;
83         drmModePropertyRes **props_info;
84 };
85
86 struct resources {
87         drmModeRes *res;
88         drmModePlaneRes *plane_res;
89
90         struct crtc *crtcs;
91         struct encoder *encoders;
92         struct connector *connectors;
93         struct fb *fbs;
94         struct plane *planes;
95 };
96
97 struct resources *resources;
98 int fd, modes;
99
100 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
101
102 struct type_name {
103         int type;
104         const char *name;
105 };
106
107 #define type_name_fn(res) \
108 const char * res##_str(int type) {                      \
109         unsigned int i;                                 \
110         for (i = 0; i < ARRAY_SIZE(res##_names); i++) { \
111                 if (res##_names[i].type == type)        \
112                         return res##_names[i].name;     \
113         }                                               \
114         return "(invalid)";                             \
115 }
116
117 struct type_name encoder_type_names[] = {
118         { DRM_MODE_ENCODER_NONE, "none" },
119         { DRM_MODE_ENCODER_DAC, "DAC" },
120         { DRM_MODE_ENCODER_TMDS, "TMDS" },
121         { DRM_MODE_ENCODER_LVDS, "LVDS" },
122         { DRM_MODE_ENCODER_TVDAC, "TVDAC" },
123 };
124
125 static type_name_fn(encoder_type)
126
127 struct type_name connector_status_names[] = {
128         { DRM_MODE_CONNECTED, "connected" },
129         { DRM_MODE_DISCONNECTED, "disconnected" },
130         { DRM_MODE_UNKNOWNCONNECTION, "unknown" },
131 };
132
133 static type_name_fn(connector_status)
134
135 struct type_name connector_type_names[] = {
136         { DRM_MODE_CONNECTOR_Unknown, "unknown" },
137         { DRM_MODE_CONNECTOR_VGA, "VGA" },
138         { DRM_MODE_CONNECTOR_DVII, "DVI-I" },
139         { DRM_MODE_CONNECTOR_DVID, "DVI-D" },
140         { DRM_MODE_CONNECTOR_DVIA, "DVI-A" },
141         { DRM_MODE_CONNECTOR_Composite, "composite" },
142         { DRM_MODE_CONNECTOR_SVIDEO, "s-video" },
143         { DRM_MODE_CONNECTOR_LVDS, "LVDS" },
144         { DRM_MODE_CONNECTOR_Component, "component" },
145         { DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN" },
146         { DRM_MODE_CONNECTOR_DisplayPort, "DP" },
147         { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" },
148         { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" },
149         { DRM_MODE_CONNECTOR_TV, "TV" },
150         { DRM_MODE_CONNECTOR_eDP, "eDP" },
151 };
152
153 static type_name_fn(connector_type)
154
155 #define bit_name_fn(res)                                        \
156 const char * res##_str(int type) {                              \
157         unsigned int i;                                         \
158         const char *sep = "";                                   \
159         for (i = 0; i < ARRAY_SIZE(res##_names); i++) {         \
160                 if (type & (1 << i)) {                          \
161                         printf("%s%s", sep, res##_names[i]);    \
162                         sep = ", ";                             \
163                 }                                               \
164         }                                                       \
165         return NULL;                                            \
166 }
167
168 static const char *mode_type_names[] = {
169         "builtin",
170         "clock_c",
171         "crtc_c",
172         "preferred",
173         "default",
174         "userdef",
175         "driver",
176 };
177
178 static bit_name_fn(mode_type)
179
180 static const char *mode_flag_names[] = {
181         "phsync",
182         "nhsync",
183         "pvsync",
184         "nvsync",
185         "interlace",
186         "dblscan",
187         "csync",
188         "pcsync",
189         "ncsync",
190         "hskew",
191         "bcast",
192         "pixmux",
193         "dblclk",
194         "clkdiv2"
195 };
196
197 static bit_name_fn(mode_flag)
198
199 static void dump_encoders(void)
200 {
201         drmModeEncoder *encoder;
202         int i;
203
204         printf("Encoders:\n");
205         printf("id\tcrtc\ttype\tpossible crtcs\tpossible clones\t\n");
206         for (i = 0; i < resources->res->count_encoders; i++) {
207                 encoder = resources->encoders[i].encoder;
208                 if (!encoder)
209                         continue;
210
211                 printf("%d\t%d\t%s\t0x%08x\t0x%08x\n",
212                        encoder->encoder_id,
213                        encoder->crtc_id,
214                        encoder_type_str(encoder->encoder_type),
215                        encoder->possible_crtcs,
216                        encoder->possible_clones);
217         }
218         printf("\n");
219 }
220
221 static void dump_mode(drmModeModeInfo *mode)
222 {
223         printf("  %s %d %d %d %d %d %d %d %d %d",
224                mode->name,
225                mode->vrefresh,
226                mode->hdisplay,
227                mode->hsync_start,
228                mode->hsync_end,
229                mode->htotal,
230                mode->vdisplay,
231                mode->vsync_start,
232                mode->vsync_end,
233                mode->vtotal);
234
235         printf(" flags: ");
236         mode_flag_str(mode->flags);
237         printf("; type: ");
238         mode_type_str(mode->type);
239         printf("\n");
240 }
241
242 static void
243 dump_blob(uint32_t blob_id)
244 {
245         uint32_t i;
246         unsigned char *blob_data;
247         drmModePropertyBlobPtr blob;
248
249         blob = drmModeGetPropertyBlob(fd, blob_id);
250         if (!blob)
251                 return;
252
253         blob_data = blob->data;
254
255         for (i = 0; i < blob->length; i++) {
256                 if (i % 16 == 0)
257                         printf("\n\t\t\t");
258                 printf("%.2hhx", blob_data[i]);
259         }
260         printf("\n");
261
262         drmModeFreePropertyBlob(blob);
263 }
264
265 static void
266 dump_prop(drmModePropertyPtr prop, uint32_t prop_id, uint64_t value)
267 {
268         int i;
269         printf("\t%d", prop_id);
270         if (!prop) {
271                 printf("\n");
272                 return;
273         }
274
275         printf(" %s:\n", prop->name);
276
277         printf("\t\tflags:");
278         if (prop->flags & DRM_MODE_PROP_PENDING)
279                 printf(" pending");
280         if (prop->flags & DRM_MODE_PROP_RANGE)
281                 printf(" range");
282         if (prop->flags & DRM_MODE_PROP_IMMUTABLE)
283                 printf(" immutable");
284         if (prop->flags & DRM_MODE_PROP_ENUM)
285                 printf(" enum");
286         if (prop->flags & DRM_MODE_PROP_BITMASK)
287                 printf(" bitmask");
288         if (prop->flags & DRM_MODE_PROP_BLOB)
289                 printf(" blob");
290         printf("\n");
291
292         if (prop->flags & DRM_MODE_PROP_RANGE) {
293                 printf("\t\tvalues:");
294                 for (i = 0; i < prop->count_values; i++)
295                         printf(" %"PRIu64, prop->values[i]);
296                 printf("\n");
297         }
298
299         if (prop->flags & DRM_MODE_PROP_ENUM) {
300                 printf("\t\tenums:");
301                 for (i = 0; i < prop->count_enums; i++)
302                         printf(" %s=%llu", prop->enums[i].name,
303                                prop->enums[i].value);
304                 printf("\n");
305         } else if (prop->flags & DRM_MODE_PROP_BITMASK) {
306                 printf("\t\tvalues:");
307                 for (i = 0; i < prop->count_enums; i++)
308                         printf(" %s=0x%llx", prop->enums[i].name,
309                                (1LL << prop->enums[i].value));
310                 printf("\n");
311         } else {
312                 assert(prop->count_enums == 0);
313         }
314
315         if (prop->flags & DRM_MODE_PROP_BLOB) {
316                 printf("\t\tblobs:\n");
317                 for (i = 0; i < prop->count_blobs; i++)
318                         dump_blob(prop->blob_ids[i]);
319                 printf("\n");
320         } else {
321                 assert(prop->count_blobs == 0);
322         }
323
324         printf("\t\tvalue:");
325         if (prop->flags & DRM_MODE_PROP_BLOB)
326                 dump_blob(value);
327         else
328                 printf(" %"PRIu64"\n", value);
329 }
330
331 static void dump_connectors(void)
332 {
333         int i, j;
334
335         printf("Connectors:\n");
336         printf("id\tencoder\tstatus\t\ttype\tsize (mm)\tmodes\tencoders\n");
337         for (i = 0; i < resources->res->count_connectors; i++) {
338                 struct connector *_connector = &resources->connectors[i];
339                 drmModeConnector *connector = _connector->connector;
340                 if (!connector)
341                         continue;
342
343                 printf("%d\t%d\t%s\t%s\t%dx%d\t\t%d\t",
344                        connector->connector_id,
345                        connector->encoder_id,
346                        connector_status_str(connector->connection),
347                        connector_type_str(connector->connector_type),
348                        connector->mmWidth, connector->mmHeight,
349                        connector->count_modes);
350
351                 for (j = 0; j < connector->count_encoders; j++)
352                         printf("%s%d", j > 0 ? ", " : "", connector->encoders[j]);
353                 printf("\n");
354
355                 if (connector->count_modes) {
356                         printf("  modes:\n");
357                         printf("\tname refresh (Hz) hdisp hss hse htot vdisp "
358                                "vss vse vtot)\n");
359                         for (j = 0; j < connector->count_modes; j++)
360                                 dump_mode(&connector->modes[j]);
361                 }
362
363                 if (_connector->props) {
364                         printf("  props:\n");
365                         for (j = 0; j < (int)_connector->props->count_props; j++)
366                                 dump_prop(_connector->props_info[j],
367                                           _connector->props->props[j],
368                                           _connector->props->prop_values[j]);
369                 }
370         }
371         printf("\n");
372 }
373
374 static void dump_crtcs(void)
375 {
376         int i;
377         uint32_t j;
378
379         printf("CRTCs:\n");
380         printf("id\tfb\tpos\tsize\n");
381         for (i = 0; i < resources->res->count_crtcs; i++) {
382                 struct crtc *_crtc = &resources->crtcs[i];
383                 drmModeCrtc *crtc = _crtc->crtc;
384                 if (!crtc)
385                         continue;
386
387                 printf("%d\t%d\t(%d,%d)\t(%dx%d)\n",
388                        crtc->crtc_id,
389                        crtc->buffer_id,
390                        crtc->x, crtc->y,
391                        crtc->width, crtc->height);
392                 dump_mode(&crtc->mode);
393
394                 if (_crtc->props) {
395                         printf("  props:\n");
396                         for (j = 0; j < _crtc->props->count_props; j++)
397                                 dump_prop(_crtc->props_info[j],
398                                           _crtc->props->props[j],
399                                           _crtc->props->prop_values[j]);
400                 } else {
401                         printf("  no properties found\n");
402                 }
403         }
404         printf("\n");
405 }
406
407 static void dump_framebuffers(void)
408 {
409         drmModeFB *fb;
410         int i;
411
412         printf("Frame buffers:\n");
413         printf("id\tsize\tpitch\n");
414         for (i = 0; i < resources->res->count_fbs; i++) {
415                 fb = resources->fbs[i].fb;
416                 if (!fb)
417                         continue;
418
419                 printf("%u\t(%ux%u)\t%u\n",
420                        fb->fb_id,
421                        fb->width, fb->height,
422                        fb->pitch);
423         }
424         printf("\n");
425 }
426
427 static void dump_planes(void)
428 {
429         unsigned int i, j;
430
431         printf("Planes:\n");
432         printf("id\tcrtc\tfb\tCRTC x,y\tx,y\tgamma size\tpossible crtcs\n");
433
434         if (!resources->plane_res)
435                 return;
436
437         for (i = 0; i < resources->plane_res->count_planes; i++) {
438                 struct plane *plane = &resources->planes[i];
439                 drmModePlane *ovr = plane->plane;
440                 if (!ovr)
441                         continue;
442
443                 printf("%d\t%d\t%d\t%d,%d\t\t%d,%d\t%-8d\t0x%08x\n",
444                        ovr->plane_id, ovr->crtc_id, ovr->fb_id,
445                        ovr->crtc_x, ovr->crtc_y, ovr->x, ovr->y,
446                        ovr->gamma_size, ovr->possible_crtcs);
447
448                 if (!ovr->count_formats)
449                         continue;
450
451                 printf("  formats:");
452                 for (j = 0; j < ovr->count_formats; j++)
453                         printf(" %4.4s", (char *)&ovr->formats[j]);
454                 printf("\n");
455
456                 if (plane->props) {
457                         printf("  props:\n");
458                         for (j = 0; j < plane->props->count_props; j++)
459                                 dump_prop(plane->props_info[j],
460                                           plane->props->props[j],
461                                           plane->props->prop_values[j]);
462                 } else {
463                         printf("  no properties found\n");
464                 }
465         }
466         printf("\n");
467
468         return;
469 }
470
471 static void free_resources(struct resources *res)
472 {
473         if (!res)
474                 return;
475
476 #define free_resource(_res, __res, type, Type)                                  \
477         do {                                                                    \
478                 int i;                                                          \
479                 if (!(_res)->type##s)                                           \
480                         break;                                                  \
481                 for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) {     \
482                         if (!(_res)->type##s[i].type)                           \
483                                 break;                                          \
484                         drmModeFree##Type((_res)->type##s[i].type);             \
485                 }                                                               \
486                 free((_res)->type##s);                                          \
487         } while (0)
488
489 #define free_properties(_res, __res, type)                                      \
490         do {                                                                    \
491                 int i;                                                          \
492                 for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) {     \
493                         drmModeFreeObjectProperties(res->type##s[i].props);     \
494                         free(res->type##s[i].props_info);                       \
495                 }                                                               \
496         } while (0)
497
498         if (res->res) {
499                 free_properties(res, res, crtc);
500
501                 free_resource(res, res, crtc, Crtc);
502                 free_resource(res, res, encoder, Encoder);
503                 free_resource(res, res, connector, Connector);
504                 free_resource(res, res, fb, FB);
505
506                 drmModeFreeResources(res->res);
507         }
508
509         if (res->plane_res) {
510                 free_properties(res, plane_res, plane);
511
512                 free_resource(res, plane_res, plane, Plane);
513
514                 drmModeFreePlaneResources(res->plane_res);
515         }
516
517         free(res);
518 }
519
520 static struct resources *get_resources(int fd)
521 {
522         struct resources *res;
523
524         res = malloc(sizeof *res);
525         if (res == 0)
526                 return NULL;
527
528         memset(res, 0, sizeof *res);
529
530         res->res = drmModeGetResources(fd);
531         if (!res->res) {
532                 fprintf(stderr, "drmModeGetResources failed: %s\n",
533                         strerror(errno));
534                 goto error;
535         }
536
537         res->crtcs = malloc(res->res->count_crtcs * sizeof *res->crtcs);
538         res->encoders = malloc(res->res->count_encoders * sizeof *res->encoders);
539         res->connectors = malloc(res->res->count_connectors * sizeof *res->connectors);
540         res->fbs = malloc(res->res->count_fbs * sizeof *res->fbs);
541
542         if (!res->crtcs || !res->encoders || !res->connectors || !res->fbs)
543                 goto error;
544
545         memset(res->crtcs , 0, res->res->count_crtcs * sizeof *res->crtcs);
546         memset(res->encoders, 0, res->res->count_encoders * sizeof *res->encoders);
547         memset(res->connectors, 0, res->res->count_connectors * sizeof *res->connectors);
548         memset(res->fbs, 0, res->res->count_fbs * sizeof *res->fbs);
549
550 #define get_resource(_res, __res, type, Type)                                   \
551         do {                                                                    \
552                 int i;                                                          \
553                 for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) {     \
554                         (_res)->type##s[i].type =                               \
555                                 drmModeGet##Type(fd, (_res)->__res->type##s[i]); \
556                         if (!(_res)->type##s[i].type)                           \
557                                 fprintf(stderr, "could not get %s %i: %s\n",    \
558                                         #type, (_res)->__res->type##s[i],       \
559                                         strerror(errno));                       \
560                 }                                                               \
561         } while (0)
562
563         get_resource(res, res, crtc, Crtc);
564         get_resource(res, res, encoder, Encoder);
565         get_resource(res, res, connector, Connector);
566         get_resource(res, res, fb, FB);
567
568 #define get_properties(_res, __res, type, Type)                                 \
569         do {                                                                    \
570                 int i;                                                          \
571                 for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) {     \
572                         struct type *obj = &res->type##s[i];                    \
573                         unsigned int j;                                         \
574                         obj->props =                                            \
575                                 drmModeObjectGetProperties(fd, obj->type->type##_id, \
576                                                            DRM_MODE_OBJECT_##Type); \
577                         if (!obj->props) {                                      \
578                                 fprintf(stderr,                                 \
579                                         "could not get %s %i properties: %s\n", \
580                                         #type, obj->type->type##_id,            \
581                                         strerror(errno));                       \
582                                 continue;                                       \
583                         }                                                       \
584                         obj->props_info = malloc(obj->props->count_props *      \
585                                                  sizeof *obj->props_info);      \
586                         if (!obj->props_info)                                   \
587                                 continue;                                       \
588                         for (j = 0; j < obj->props->count_props; ++j)           \
589                                 obj->props_info[j] =                            \
590                                         drmModeGetProperty(fd, obj->props->props[j]); \
591                 }                                                               \
592         } while (0)
593
594         get_properties(res, res, crtc, CRTC);
595         get_properties(res, res, connector, CONNECTOR);
596
597         res->plane_res = drmModeGetPlaneResources(fd);
598         if (!res->plane_res) {
599                 fprintf(stderr, "drmModeGetPlaneResources failed: %s\n",
600                         strerror(errno));
601                 return res;
602         }
603
604         res->planes = malloc(res->plane_res->count_planes * sizeof *res->planes);
605         if (!res->planes)
606                 goto error;
607
608         memset(res->planes, 0, res->plane_res->count_planes * sizeof *res->planes);
609
610         get_resource(res, plane_res, plane, Plane);
611         get_properties(res, plane_res, plane, PLANE);
612
613         return res;
614
615 error:
616         free_resources(res);
617         return NULL;
618 }
619
620 /* -----------------------------------------------------------------------------
621  * Connectors and planes
622  */
623
624 /*
625  * Mode setting with the kernel interfaces is a bit of a chore.
626  * First you have to find the connector in question and make sure the
627  * requested mode is available.
628  * Then you need to find the encoder attached to that connector so you
629  * can bind it with a free crtc.
630  */
631 struct connector_arg {
632         uint32_t id;
633         char mode_str[64];
634         char format_str[5];
635         unsigned int fourcc;
636         drmModeModeInfo *mode;
637         drmModeEncoder *encoder;
638         int crtc;
639         int pipe;
640         unsigned int fb_id[2], current_fb_id;
641         struct timeval start;
642
643         int swap_count;
644 };
645
646 struct plane_arg {
647         uint32_t con_id;  /* the id of connector to bind to */
648         uint32_t w, h;
649         unsigned int fb_id;
650         char format_str[5]; /* need to leave room for terminating \0 */
651         unsigned int fourcc;
652 };
653
654 static void
655 connector_find_mode(struct connector_arg *c)
656 {
657         drmModeConnector *connector;
658         int i, j;
659
660         /* First, find the connector & mode */
661         c->mode = NULL;
662         for (i = 0; i < resources->res->count_connectors; i++) {
663                 connector = resources->connectors[i].connector;
664                 if (!connector)
665                         continue;
666
667                 if (!connector->count_modes)
668                         continue;
669
670                 if (connector->connector_id != c->id)
671                         continue;
672
673                 for (j = 0; j < connector->count_modes; j++) {
674                         c->mode = &connector->modes[j];
675                         if (!strcmp(c->mode->name, c->mode_str))
676                                 break;
677                 }
678
679                 /* Found it, break out */
680                 if (c->mode)
681                         break;
682         }
683
684         if (!c->mode) {
685                 fprintf(stderr, "failed to find mode \"%s\"\n", c->mode_str);
686                 return;
687         }
688
689         /* Now get the encoder */
690         for (i = 0; i < resources->res->count_encoders; i++) {
691                 c->encoder = resources->encoders[i].encoder;
692                 if (!c->encoder)
693                         continue;
694
695                 if (c->encoder->encoder_id  == connector->encoder_id)
696                         break;
697         }
698
699         if (c->crtc == -1)
700                 c->crtc = c->encoder->crtc_id;
701
702         /* and figure out which crtc index it is: */
703         for (i = 0; i < resources->res->count_crtcs; i++) {
704                 if (c->crtc == (int)resources->res->crtcs[i]) {
705                         c->pipe = i;
706                         break;
707                 }
708         }
709
710 }
711
712 /* -------------------------------------------------------------------------- */
713
714 static void
715 page_flip_handler(int fd, unsigned int frame,
716                   unsigned int sec, unsigned int usec, void *data)
717 {
718         struct connector_arg *c;
719         unsigned int new_fb_id;
720         struct timeval end;
721         double t;
722
723         c = data;
724         if (c->current_fb_id == c->fb_id[0])
725                 new_fb_id = c->fb_id[1];
726         else
727                 new_fb_id = c->fb_id[0];
728
729         drmModePageFlip(fd, c->crtc, new_fb_id,
730                         DRM_MODE_PAGE_FLIP_EVENT, c);
731         c->current_fb_id = new_fb_id;
732         c->swap_count++;
733         if (c->swap_count == 60) {
734                 gettimeofday(&end, NULL);
735                 t = end.tv_sec + end.tv_usec * 1e-6 -
736                         (c->start.tv_sec + c->start.tv_usec * 1e-6);
737                 fprintf(stderr, "freq: %.02fHz\n", c->swap_count / t);
738                 c->swap_count = 0;
739                 c->start = end;
740         }
741 }
742
743 static int
744 set_plane(struct kms_driver *kms, struct connector_arg *c, struct plane_arg *p)
745 {
746         drmModePlane *ovr;
747         uint32_t handles[4], pitches[4], offsets[4] = {0}; /* we only use [0] */
748         uint32_t plane_id = 0;
749         struct kms_bo *plane_bo;
750         uint32_t plane_flags = 0;
751         int crtc_x, crtc_y, crtc_w, crtc_h;
752         unsigned int i;
753
754         /* find an unused plane which can be connected to our crtc */
755         for (i = 0; i < resources->plane_res->count_planes && !plane_id; i++) {
756                 ovr = resources->planes[i].plane;
757                 if (!ovr)
758                         continue;
759
760                 if ((ovr->possible_crtcs & (1 << c->pipe)) && !ovr->crtc_id)
761                         plane_id = ovr->plane_id;
762         }
763
764         fprintf(stderr, "testing %dx%d@%s overlay plane\n",
765                         p->w, p->h, p->format_str);
766
767         if (!plane_id) {
768                 fprintf(stderr, "failed to find plane!\n");
769                 return -1;
770         }
771
772         plane_bo = create_test_buffer(kms, p->fourcc, p->w, p->h, handles,
773                                       pitches, offsets, PATTERN_TILES);
774         if (plane_bo == NULL)
775                 return -1;
776
777         /* just use single plane format for now.. */
778         if (drmModeAddFB2(fd, p->w, p->h, p->fourcc,
779                         handles, pitches, offsets, &p->fb_id, plane_flags)) {
780                 fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
781                 return -1;
782         }
783
784         /* ok, boring.. but for now put in middle of screen: */
785         crtc_x = c->mode->hdisplay / 3;
786         crtc_y = c->mode->vdisplay / 3;
787         crtc_w = crtc_x;
788         crtc_h = crtc_y;
789
790         /* note src coords (last 4 args) are in Q16 format */
791         if (drmModeSetPlane(fd, plane_id, c->crtc, p->fb_id,
792                             plane_flags, crtc_x, crtc_y, crtc_w, crtc_h,
793                             0, 0, p->w << 16, p->h << 16)) {
794                 fprintf(stderr, "failed to enable plane: %s\n",
795                         strerror(errno));
796                 return -1;
797         }
798
799         ovr->crtc_id = c->crtc;
800
801         return 0;
802 }
803
804 static void
805 set_mode(struct connector_arg *c, int count, struct plane_arg *p, int plane_count,
806                 int page_flip)
807 {
808         struct kms_driver *kms;
809         struct kms_bo *bo, *other_bo;
810         unsigned int fb_id, other_fb_id;
811         int i, j, ret, width, height, x;
812         uint32_t handles[4], pitches[4], offsets[4] = {0}; /* we only use [0] */
813         drmEventContext evctx;
814
815         width = 0;
816         height = 0;
817         for (i = 0; i < count; i++) {
818                 connector_find_mode(&c[i]);
819                 if (c[i].mode == NULL)
820                         continue;
821                 width += c[i].mode->hdisplay;
822                 if (height < c[i].mode->vdisplay)
823                         height = c[i].mode->vdisplay;
824         }
825
826         ret = kms_create(fd, &kms);
827         if (ret) {
828                 fprintf(stderr, "failed to create kms driver: %s\n",
829                         strerror(-ret));
830                 return;
831         }
832
833         bo = create_test_buffer(kms, c->fourcc, width, height, handles,
834                                 pitches, offsets, PATTERN_SMPTE);
835         if (bo == NULL)
836                 return;
837
838         ret = drmModeAddFB2(fd, width, height, c->fourcc,
839                             handles, pitches, offsets, &fb_id, 0);
840         if (ret) {
841                 fprintf(stderr, "failed to add fb (%ux%u): %s\n",
842                         width, height, strerror(errno));
843                 return;
844         }
845
846         x = 0;
847         for (i = 0; i < count; i++) {
848                 if (c[i].mode == NULL)
849                         continue;
850
851                 printf("setting mode %s@%s on connector %d, crtc %d\n",
852                        c[i].mode_str, c[i].format_str, c[i].id, c[i].crtc);
853
854                 ret = drmModeSetCrtc(fd, c[i].crtc, fb_id, x, 0,
855                                      &c[i].id, 1, c[i].mode);
856
857                 /* XXX: Actually check if this is needed */
858                 drmModeDirtyFB(fd, fb_id, NULL, 0);
859
860                 x += c[i].mode->hdisplay;
861
862                 if (ret) {
863                         fprintf(stderr, "failed to set mode: %s\n", strerror(errno));
864                         return;
865                 }
866
867                 /* if we have a plane/overlay to show, set that up now: */
868                 for (j = 0; j < plane_count; j++)
869                         if (p[j].con_id == c[i].id)
870                                 if (set_plane(kms, &c[i], &p[j]))
871                                         return;
872         }
873
874         if (!page_flip)
875                 return;
876         
877         other_bo = create_test_buffer(kms, c->fourcc, width, height, handles,
878                                       pitches, offsets, PATTERN_PLAIN);
879         if (other_bo == NULL)
880                 return;
881
882         ret = drmModeAddFB2(fd, width, height, c->fourcc, handles, pitches, offsets,
883                             &other_fb_id, 0);
884         if (ret) {
885                 fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
886                 return;
887         }
888
889         for (i = 0; i < count; i++) {
890                 if (c[i].mode == NULL)
891                         continue;
892
893                 ret = drmModePageFlip(fd, c[i].crtc, other_fb_id,
894                                       DRM_MODE_PAGE_FLIP_EVENT, &c[i]);
895                 if (ret) {
896                         fprintf(stderr, "failed to page flip: %s\n", strerror(errno));
897                         return;
898                 }
899                 gettimeofday(&c[i].start, NULL);
900                 c[i].swap_count = 0;
901                 c[i].fb_id[0] = fb_id;
902                 c[i].fb_id[1] = other_fb_id;
903                 c[i].current_fb_id = other_fb_id;
904         }
905
906         memset(&evctx, 0, sizeof evctx);
907         evctx.version = DRM_EVENT_CONTEXT_VERSION;
908         evctx.vblank_handler = NULL;
909         evctx.page_flip_handler = page_flip_handler;
910         
911         while (1) {
912 #if 0
913                 struct pollfd pfd[2];
914
915                 pfd[0].fd = 0;
916                 pfd[0].events = POLLIN;
917                 pfd[1].fd = fd;
918                 pfd[1].events = POLLIN;
919
920                 if (poll(pfd, 2, -1) < 0) {
921                         fprintf(stderr, "poll error\n");
922                         break;
923                 }
924
925                 if (pfd[0].revents)
926                         break;
927 #else
928                 struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 };
929                 fd_set fds;
930                 int ret;
931
932                 FD_ZERO(&fds);
933                 FD_SET(0, &fds);
934                 FD_SET(fd, &fds);
935                 ret = select(fd + 1, &fds, NULL, NULL, &timeout);
936
937                 if (ret <= 0) {
938                         fprintf(stderr, "select timed out or error (ret %d)\n",
939                                 ret);
940                         continue;
941                 } else if (FD_ISSET(0, &fds)) {
942                         break;
943                 }
944 #endif
945
946                 drmHandleEvent(fd, &evctx);
947         }
948
949         kms_bo_destroy(&bo);
950         kms_bo_destroy(&other_bo);
951         kms_destroy(&kms);
952 }
953
954 #define min(a, b)       ((a) < (b) ? (a) : (b))
955
956 static int parse_connector(struct connector_arg *c, const char *arg)
957 {
958         unsigned int len;
959         const char *p;
960         char *endp;
961
962         c->crtc = -1;
963         strcpy(c->format_str, "XR24");
964
965         c->id = strtoul(arg, &endp, 10);
966         if (*endp == '@') {
967                 arg = endp + 1;
968                 c->crtc = strtoul(arg, &endp, 10);
969         }
970         if (*endp != ':')
971                 return -1;
972
973         arg = endp + 1;
974
975         p = strchrnul(arg, '@');
976         len = min(sizeof c->mode_str - 1, (unsigned int)(p - arg));
977         strncpy(c->mode_str, arg, len);
978         c->mode_str[len] = '\0';
979
980         if (*p == '@') {
981                 strncpy(c->format_str, p + 1, 4);
982                 c->format_str[4] = '\0';
983         }
984
985         c->fourcc = format_fourcc(c->format_str);
986         if (c->fourcc == 0)  {
987                 fprintf(stderr, "unknown format %s\n", c->format_str);
988                 return -1;
989         }
990
991         return 0;
992 }
993
994 static int parse_plane(struct plane_arg *p, const char *arg)
995 {
996         strcpy(p->format_str, "XR24");
997
998         if (sscanf(arg, "%d:%dx%d@%4s", &p->con_id, &p->w, &p->h, p->format_str) != 4 &&
999             sscanf(arg, "%d:%dx%d", &p->con_id, &p->w, &p->h) != 3)
1000                 return -1;
1001
1002         p->fourcc = format_fourcc(p->format_str);
1003         if (p->fourcc == 0) {
1004                 fprintf(stderr, "unknown format %s\n", p->format_str);
1005                 return -1;
1006         }
1007
1008         return 0;
1009 }
1010
1011 static void usage(char *name)
1012 {
1013         fprintf(stderr, "usage: %s [-cdefMmPpsv]\n", name);
1014
1015         fprintf(stderr, "\n Query options:\n\n");
1016         fprintf(stderr, "\t-c\tlist connectors\n");
1017         fprintf(stderr, "\t-e\tlist encoders\n");
1018         fprintf(stderr, "\t-f\tlist framebuffers\n");
1019         fprintf(stderr, "\t-m\tlist modes\n");
1020         fprintf(stderr, "\t-p\tlist CRTCs and planes (pipes)\n");
1021
1022         fprintf(stderr, "\n Test options:\n\n");
1023         fprintf(stderr, "\t-P <connector_id>:<w>x<h>[@<format>]\tset a plane\n");
1024         fprintf(stderr, "\t-s <connector_id>[@<crtc_id>]:<mode>[@<format>]\tset a mode\n");
1025         fprintf(stderr, "\t-v\ttest vsynced page flipping\n");
1026
1027         fprintf(stderr, "\n Generic options:\n\n");
1028         fprintf(stderr, "\t-d\tdrop master after mode set\n");
1029         fprintf(stderr, "\t-M module\tuse the given driver\n");
1030
1031         fprintf(stderr, "\n\tDefault is to dump all info.\n");
1032         exit(0);
1033 }
1034
1035 #define dump_resource(res) if (res) dump_##res()
1036
1037 static int page_flipping_supported(void)
1038 {
1039         /*FIXME: generic ioctl needed? */
1040         return 1;
1041 #if 0
1042         int ret, value;
1043         struct drm_i915_getparam gp;
1044
1045         gp.param = I915_PARAM_HAS_PAGEFLIPPING;
1046         gp.value = &value;
1047
1048         ret = drmCommandWriteRead(fd, DRM_I915_GETPARAM, &gp, sizeof(gp));
1049         if (ret) {
1050                 fprintf(stderr, "drm_i915_getparam: %m\n");
1051                 return 0;
1052         }
1053
1054         return *gp.value;
1055 #endif
1056 }
1057
1058 static char optstr[] = "cdefM:mP:ps:v";
1059
1060 int main(int argc, char **argv)
1061 {
1062         int c;
1063         int encoders = 0, connectors = 0, crtcs = 0, planes = 0, framebuffers = 0;
1064         int drop_master = 0;
1065         int test_vsync = 0;
1066         const char *modules[] = { "i915", "radeon", "nouveau", "vmwgfx", "omapdrm", "exynos", "tilcdc" };
1067         char *module = NULL;
1068         unsigned int i;
1069         int count = 0, plane_count = 0;
1070         struct connector_arg *con_args = NULL;
1071         struct plane_arg *plane_args = NULL;
1072         unsigned int args = 0;
1073         
1074         opterr = 0;
1075         while ((c = getopt(argc, argv, optstr)) != -1) {
1076                 args++;
1077
1078                 switch (c) {
1079                 case 'c':
1080                         connectors = 1;
1081                         break;
1082                 case 'd':
1083                         drop_master = 1;
1084                         break;
1085                 case 'e':
1086                         encoders = 1;
1087                         break;
1088                 case 'f':
1089                         framebuffers = 1;
1090                         break;
1091                 case 'M':
1092                         module = optarg;
1093                         /* Preserve the default behaviour of dumping all information. */
1094                         args--;
1095                         break;
1096                 case 'm':
1097                         modes = 1;
1098                         break;
1099                 case 'P':
1100                         plane_args = realloc(plane_args,
1101                                              (plane_count + 1) * sizeof *plane_args);
1102                         if (plane_args == NULL) {
1103                                 fprintf(stderr, "memory allocation failed\n");
1104                                 return 1;
1105                         }
1106
1107                         if (parse_plane(&plane_args[plane_count], optarg) < 0)
1108                                 usage(argv[0]);
1109
1110                         plane_count++;
1111                         break;
1112                 case 'p':
1113                         crtcs = 1;
1114                         planes = 1;
1115                         break;
1116                 case 's':
1117                         con_args = realloc(con_args,
1118                                            (count + 1) * sizeof *con_args);
1119                         if (con_args == NULL) {
1120                                 fprintf(stderr, "memory allocation failed\n");
1121                                 return 1;
1122                         }
1123
1124                         if (parse_connector(&con_args[count], optarg) < 0)
1125                                 usage(argv[0]);
1126
1127                         count++;                                      
1128                         break;
1129                 case 'v':
1130                         test_vsync = 1;
1131                         break;
1132                 default:
1133                         usage(argv[0]);
1134                         break;
1135                 }
1136         }
1137
1138         if (!args)
1139                 encoders = connectors = crtcs = planes = modes = framebuffers = 1;
1140
1141         if (module) {
1142                 fd = drmOpen(module, NULL);
1143                 if (fd < 0) {
1144                         fprintf(stderr, "failed to open device '%s'.\n", module);
1145                         return 1;
1146                 }
1147         } else {
1148                 for (i = 0; i < ARRAY_SIZE(modules); i++) {
1149                         printf("trying to open device '%s'...", modules[i]);
1150                         fd = drmOpen(modules[i], NULL);
1151                         if (fd < 0) {
1152                                 printf("failed.\n");
1153                         } else {
1154                                 printf("success.\n");
1155                                 break;
1156                         }
1157                 }
1158
1159                 if (fd < 0) {
1160                         fprintf(stderr, "no device found.\n");
1161                         return 1;
1162                 }
1163         }
1164
1165         if (test_vsync && !page_flipping_supported()) {
1166                 fprintf(stderr, "page flipping not supported by drm.\n");
1167                 return -1;
1168         }
1169
1170         resources = get_resources(fd);
1171         if (!resources) {
1172                 drmClose(fd);
1173                 return 1;
1174         }
1175
1176         dump_resource(encoders);
1177         dump_resource(connectors);
1178         dump_resource(crtcs);
1179         dump_resource(planes);
1180         dump_resource(framebuffers);
1181
1182         if (count > 0) {
1183                 set_mode(con_args, count, plane_args, plane_count, test_vsync);
1184                 if (drop_master)
1185                         drmDropMaster(fd);
1186                 getchar();
1187         }
1188
1189         free_resources(resources);
1190
1191         return 0;
1192 }