tests/modetest: Add a forgotten return, needed for opensuse buildservice
[gstreamer-omap: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 drmModeRes *resources;
61 int fd, modes;
62
63 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
64
65 struct type_name {
66         int type;
67         char *name;
68 };
69
70 #define type_name_fn(res) \
71 char * res##_str(int type) {                    \
72         unsigned int i;                                 \
73         for (i = 0; i < ARRAY_SIZE(res##_names); i++) { \
74                 if (res##_names[i].type == type)        \
75                         return res##_names[i].name;     \
76         }                                               \
77         return "(invalid)";                             \
78 }
79
80 struct type_name encoder_type_names[] = {
81         { DRM_MODE_ENCODER_NONE, "none" },
82         { DRM_MODE_ENCODER_DAC, "DAC" },
83         { DRM_MODE_ENCODER_TMDS, "TMDS" },
84         { DRM_MODE_ENCODER_LVDS, "LVDS" },
85         { DRM_MODE_ENCODER_TVDAC, "TVDAC" },
86 };
87
88 type_name_fn(encoder_type)
89
90 struct type_name connector_status_names[] = {
91         { DRM_MODE_CONNECTED, "connected" },
92         { DRM_MODE_DISCONNECTED, "disconnected" },
93         { DRM_MODE_UNKNOWNCONNECTION, "unknown" },
94 };
95
96 type_name_fn(connector_status)
97
98 struct type_name connector_type_names[] = {
99         { DRM_MODE_CONNECTOR_Unknown, "unknown" },
100         { DRM_MODE_CONNECTOR_VGA, "VGA" },
101         { DRM_MODE_CONNECTOR_DVII, "DVI-I" },
102         { DRM_MODE_CONNECTOR_DVID, "DVI-D" },
103         { DRM_MODE_CONNECTOR_DVIA, "DVI-A" },
104         { DRM_MODE_CONNECTOR_Composite, "composite" },
105         { DRM_MODE_CONNECTOR_SVIDEO, "s-video" },
106         { DRM_MODE_CONNECTOR_LVDS, "LVDS" },
107         { DRM_MODE_CONNECTOR_Component, "component" },
108         { DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN" },
109         { DRM_MODE_CONNECTOR_DisplayPort, "displayport" },
110         { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" },
111         { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" },
112         { DRM_MODE_CONNECTOR_TV, "TV" },
113         { DRM_MODE_CONNECTOR_eDP, "embedded displayport" },
114 };
115
116 type_name_fn(connector_type)
117
118 #define bit_name_fn(res)                                        \
119 char * res##_str(int type) {                                    \
120         int i;                                                  \
121         const char *sep = "";                                   \
122         for (i = 0; i < ARRAY_SIZE(res##_names); i++) {         \
123                 if (type & (1 << i)) {                          \
124                         printf("%s%s", sep, res##_names[i]);    \
125                         sep = ", ";                             \
126                 }                                               \
127         }                                                       \
128         return NULL;                                            \
129 }
130
131 static const char *mode_type_names[] = {
132         "builtin",
133         "clock_c",
134         "crtc_c",
135         "preferred",
136         "default",
137         "userdef",
138         "driver",
139 };
140
141 bit_name_fn(mode_type)
142
143 static const char *mode_flag_names[] = {
144         "phsync",
145         "nhsync",
146         "pvsync",
147         "nvsync",
148         "interlace",
149         "dblscan",
150         "csync",
151         "pcsync",
152         "ncsync",
153         "hskew",
154         "bcast",
155         "pixmux",
156         "dblclk",
157         "clkdiv2"
158 };
159
160 bit_name_fn(mode_flag)
161
162 void dump_encoders(void)
163 {
164         drmModeEncoder *encoder;
165         int i;
166
167         printf("Encoders:\n");
168         printf("id\tcrtc\ttype\tpossible crtcs\tpossible clones\t\n");
169         for (i = 0; i < resources->count_encoders; i++) {
170                 encoder = drmModeGetEncoder(fd, resources->encoders[i]);
171
172                 if (!encoder) {
173                         fprintf(stderr, "could not get encoder %i: %s\n",
174                                 resources->encoders[i], strerror(errno));
175                         continue;
176                 }
177                 printf("%d\t%d\t%s\t0x%08x\t0x%08x\n",
178                        encoder->encoder_id,
179                        encoder->crtc_id,
180                        encoder_type_str(encoder->encoder_type),
181                        encoder->possible_crtcs,
182                        encoder->possible_clones);
183                 drmModeFreeEncoder(encoder);
184         }
185         printf("\n");
186 }
187
188 void dump_mode(drmModeModeInfo *mode)
189 {
190         printf("  %s %d %d %d %d %d %d %d %d %d",
191                mode->name,
192                mode->vrefresh,
193                mode->hdisplay,
194                mode->hsync_start,
195                mode->hsync_end,
196                mode->htotal,
197                mode->vdisplay,
198                mode->vsync_start,
199                mode->vsync_end,
200                mode->vtotal);
201
202         printf(" flags: ");
203         mode_flag_str(mode->flags);
204         printf("; type: ");
205         mode_type_str(mode->type);
206         printf("\n");
207 }
208
209 static void
210 dump_blob(uint32_t blob_id)
211 {
212         uint32_t i;
213         unsigned char *blob_data;
214         drmModePropertyBlobPtr blob;
215
216         blob = drmModeGetPropertyBlob(fd, blob_id);
217         if (!blob)
218                 return;
219
220         blob_data = blob->data;
221
222         for (i = 0; i < blob->length; i++) {
223                 if (i % 16 == 0)
224                         printf("\n\t\t\t");
225                 printf("%.2hhx", blob_data[i]);
226         }
227         printf("\n");
228
229         drmModeFreePropertyBlob(blob);
230 }
231
232 static void
233 dump_prop(uint32_t prop_id, uint64_t value)
234 {
235         int i;
236         drmModePropertyPtr prop;
237
238         prop = drmModeGetProperty(fd, prop_id);
239
240         printf("\t%d", prop_id);
241         if (!prop) {
242                 printf("\n");
243                 return;
244         }
245
246         printf(" %s:\n", prop->name);
247
248         printf("\t\tflags:");
249         if (prop->flags & DRM_MODE_PROP_PENDING)
250                 printf(" pending");
251         if (prop->flags & DRM_MODE_PROP_RANGE)
252                 printf(" range");
253         if (prop->flags & DRM_MODE_PROP_IMMUTABLE)
254                 printf(" immutable");
255         if (prop->flags & DRM_MODE_PROP_ENUM)
256                 printf(" enum");
257         if (prop->flags & DRM_MODE_PROP_BITMASK)
258                 printf(" bitmask");
259         if (prop->flags & DRM_MODE_PROP_BLOB)
260                 printf(" blob");
261         printf("\n");
262
263         if (prop->flags & DRM_MODE_PROP_RANGE) {
264                 printf("\t\tvalues:");
265                 for (i = 0; i < prop->count_values; i++)
266                         printf(" %"PRIu64, prop->values[i]);
267                 printf("\n");
268         }
269
270         if (prop->flags & DRM_MODE_PROP_ENUM) {
271                 printf("\t\tenums:");
272                 for (i = 0; i < prop->count_enums; i++)
273                         printf(" %s=%llu", prop->enums[i].name,
274                                prop->enums[i].value);
275                 printf("\n");
276         } else if (prop->flags & DRM_MODE_PROP_BITMASK) {
277                 printf("\t\tvalues:");
278                 for (i = 0; i < prop->count_enums; i++)
279                         printf(" %s=0x%llx", prop->enums[i].name,
280                                (1LL << prop->enums[i].value));
281                 printf("\n");
282         } else {
283                 assert(prop->count_enums == 0);
284         }
285
286         if (prop->flags & DRM_MODE_PROP_BLOB) {
287                 printf("\t\tblobs:\n");
288                 for (i = 0; i < prop->count_blobs; i++)
289                         dump_blob(prop->blob_ids[i]);
290                 printf("\n");
291         } else {
292                 assert(prop->count_blobs == 0);
293         }
294
295         printf("\t\tvalue:");
296         if (prop->flags & DRM_MODE_PROP_BLOB)
297                 dump_blob(value);
298         else
299                 printf(" %"PRIu64"\n", value);
300
301         drmModeFreeProperty(prop);
302 }
303
304 void dump_connectors(void)
305 {
306         drmModeConnector *connector;
307         int i, j;
308
309         printf("Connectors:\n");
310         printf("id\tencoder\tstatus\t\ttype\tsize (mm)\tmodes\tencoders\n");
311         for (i = 0; i < resources->count_connectors; i++) {
312                 connector = drmModeGetConnector(fd, resources->connectors[i]);
313
314                 if (!connector) {
315                         fprintf(stderr, "could not get connector %i: %s\n",
316                                 resources->connectors[i], strerror(errno));
317                         continue;
318                 }
319
320                 printf("%d\t%d\t%s\t%s\t%dx%d\t\t%d\t",
321                        connector->connector_id,
322                        connector->encoder_id,
323                        connector_status_str(connector->connection),
324                        connector_type_str(connector->connector_type),
325                        connector->mmWidth, connector->mmHeight,
326                        connector->count_modes);
327
328                 for (j = 0; j < connector->count_encoders; j++)
329                         printf("%s%d", j > 0 ? ", " : "", connector->encoders[j]);
330                 printf("\n");
331
332                 if (connector->count_modes) {
333                         printf("  modes:\n");
334                         printf("\tname refresh (Hz) hdisp hss hse htot vdisp "
335                                "vss vse vtot)\n");
336                         for (j = 0; j < connector->count_modes; j++)
337                                 dump_mode(&connector->modes[j]);
338
339                         printf("  props:\n");
340                         for (j = 0; j < connector->count_props; j++)
341                                 dump_prop(connector->props[j],
342                                           connector->prop_values[j]);
343                 }
344
345                 drmModeFreeConnector(connector);
346         }
347         printf("\n");
348 }
349
350 void dump_crtcs(void)
351 {
352         drmModeCrtc *crtc;
353         drmModeObjectPropertiesPtr props;
354         int i;
355         uint32_t j;
356
357         printf("CRTCs:\n");
358         printf("id\tfb\tpos\tsize\n");
359         for (i = 0; i < resources->count_crtcs; i++) {
360                 crtc = drmModeGetCrtc(fd, resources->crtcs[i]);
361
362                 if (!crtc) {
363                         fprintf(stderr, "could not get crtc %i: %s\n",
364                                 resources->crtcs[i], strerror(errno));
365                         continue;
366                 }
367                 printf("%d\t%d\t(%d,%d)\t(%dx%d)\n",
368                        crtc->crtc_id,
369                        crtc->buffer_id,
370                        crtc->x, crtc->y,
371                        crtc->width, crtc->height);
372                 dump_mode(&crtc->mode);
373
374                 printf("  props:\n");
375                 props = drmModeObjectGetProperties(fd, crtc->crtc_id,
376                                                    DRM_MODE_OBJECT_CRTC);
377                 if (props) {
378                         for (j = 0; j < props->count_props; j++)
379                                 dump_prop(props->props[j],
380                                           props->prop_values[j]);
381                         drmModeFreeObjectProperties(props);
382                 } else {
383                         printf("\tcould not get crtc properties: %s\n",
384                                strerror(errno));
385                 }
386
387                 drmModeFreeCrtc(crtc);
388         }
389         printf("\n");
390 }
391
392 void dump_framebuffers(void)
393 {
394         drmModeFB *fb;
395         int i;
396
397         printf("Frame buffers:\n");
398         printf("id\tsize\tpitch\n");
399         for (i = 0; i < resources->count_fbs; i++) {
400                 fb = drmModeGetFB(fd, resources->fbs[i]);
401
402                 if (!fb) {
403                         fprintf(stderr, "could not get fb %i: %s\n",
404                                 resources->fbs[i], strerror(errno));
405                         continue;
406                 }
407                 printf("%u\t(%ux%u)\t%u\n",
408                        fb->fb_id,
409                        fb->width, fb->height,
410                        fb->pitch);
411
412                 drmModeFreeFB(fb);
413         }
414         printf("\n");
415 }
416
417 static void dump_planes(void)
418 {
419         drmModeObjectPropertiesPtr props;
420         drmModePlaneRes *plane_resources;
421         drmModePlane *ovr;
422         unsigned int i, j;
423
424         plane_resources = drmModeGetPlaneResources(fd);
425         if (!plane_resources) {
426                 fprintf(stderr, "drmModeGetPlaneResources failed: %s\n",
427                         strerror(errno));
428                 return;
429         }
430
431         printf("Planes:\n");
432         printf("id\tcrtc\tfb\tCRTC x,y\tx,y\tgamma size\n");
433         for (i = 0; i < plane_resources->count_planes; i++) {
434                 ovr = drmModeGetPlane(fd, plane_resources->planes[i]);
435                 if (!ovr) {
436                         fprintf(stderr, "drmModeGetPlane failed: %s\n",
437                                 strerror(errno));
438                         continue;
439                 }
440
441                 printf("%d\t%d\t%d\t%d,%d\t\t%d,%d\t%d\n",
442                        ovr->plane_id, ovr->crtc_id, ovr->fb_id,
443                        ovr->crtc_x, ovr->crtc_y, ovr->x, ovr->y,
444                        ovr->gamma_size);
445
446                 if (!ovr->count_formats)
447                         continue;
448
449                 printf("  formats:");
450                 for (j = 0; j < ovr->count_formats; j++)
451                         printf(" %4.4s", (char *)&ovr->formats[j]);
452                 printf("\n");
453
454                 printf("  props:\n");
455                 props = drmModeObjectGetProperties(fd, ovr->plane_id,
456                                                    DRM_MODE_OBJECT_PLANE);
457                 if (props) {
458                         for (j = 0; j < props->count_props; j++)
459                                 dump_prop(props->props[j],
460                                           props->prop_values[j]);
461                         drmModeFreeObjectProperties(props);
462                 } else {
463                         printf("\tcould not get plane properties: %s\n",
464                                strerror(errno));
465                 }
466
467                 drmModeFreePlane(ovr);
468         }
469         printf("\n");
470
471         drmModeFreePlaneResources(plane_resources);
472         return;
473 }
474
475 /* -----------------------------------------------------------------------------
476  * Connectors and planes
477  */
478
479 /*
480  * Mode setting with the kernel interfaces is a bit of a chore.
481  * First you have to find the connector in question and make sure the
482  * requested mode is available.
483  * Then you need to find the encoder attached to that connector so you
484  * can bind it with a free crtc.
485  */
486 struct connector {
487         uint32_t id;
488         char mode_str[64];
489         char format_str[5];
490         unsigned int fourcc;
491         drmModeModeInfo *mode;
492         drmModeEncoder *encoder;
493         int crtc;
494         int pipe;
495         unsigned int fb_id[2], current_fb_id;
496         struct timeval start;
497
498         int swap_count;
499 };
500
501 struct plane {
502         uint32_t con_id;  /* the id of connector to bind to */
503         uint32_t w, h;
504         unsigned int fb_id;
505         char format_str[5]; /* need to leave room for terminating \0 */
506         unsigned int fourcc;
507 };
508
509 static void
510 connector_find_mode(struct connector *c)
511 {
512         drmModeConnector *connector;
513         int i, j;
514
515         /* First, find the connector & mode */
516         c->mode = NULL;
517         for (i = 0; i < resources->count_connectors; i++) {
518                 connector = drmModeGetConnector(fd, resources->connectors[i]);
519
520                 if (!connector) {
521                         fprintf(stderr, "could not get connector %i: %s\n",
522                                 resources->connectors[i], strerror(errno));
523                         drmModeFreeConnector(connector);
524                         continue;
525                 }
526
527                 if (!connector->count_modes) {
528                         drmModeFreeConnector(connector);
529                         continue;
530                 }
531
532                 if (connector->connector_id != c->id) {
533                         drmModeFreeConnector(connector);
534                         continue;
535                 }
536
537                 for (j = 0; j < connector->count_modes; j++) {
538                         c->mode = &connector->modes[j];
539                         if (!strcmp(c->mode->name, c->mode_str))
540                                 break;
541                 }
542
543                 /* Found it, break out */
544                 if (c->mode)
545                         break;
546
547                 drmModeFreeConnector(connector);
548         }
549
550         if (!c->mode) {
551                 fprintf(stderr, "failed to find mode \"%s\"\n", c->mode_str);
552                 return;
553         }
554
555         /* Now get the encoder */
556         for (i = 0; i < resources->count_encoders; i++) {
557                 c->encoder = drmModeGetEncoder(fd, resources->encoders[i]);
558
559                 if (!c->encoder) {
560                         fprintf(stderr, "could not get encoder %i: %s\n",
561                                 resources->encoders[i], strerror(errno));
562                         drmModeFreeEncoder(c->encoder);
563                         continue;
564                 }
565
566                 if (c->encoder->encoder_id  == connector->encoder_id)
567                         break;
568
569                 drmModeFreeEncoder(c->encoder);
570         }
571
572         if (c->crtc == -1)
573                 c->crtc = c->encoder->crtc_id;
574
575         /* and figure out which crtc index it is: */
576         for (i = 0; i < resources->count_crtcs; i++) {
577                 if (c->crtc == resources->crtcs[i]) {
578                         c->pipe = i;
579                         break;
580                 }
581         }
582
583 }
584
585 /* -------------------------------------------------------------------------- */
586
587 void
588 page_flip_handler(int fd, unsigned int frame,
589                   unsigned int sec, unsigned int usec, void *data)
590 {
591         struct connector *c;
592         unsigned int new_fb_id;
593         struct timeval end;
594         double t;
595
596         c = data;
597         if (c->current_fb_id == c->fb_id[0])
598                 new_fb_id = c->fb_id[1];
599         else
600                 new_fb_id = c->fb_id[0];
601
602         drmModePageFlip(fd, c->crtc, new_fb_id,
603                         DRM_MODE_PAGE_FLIP_EVENT, c);
604         c->current_fb_id = new_fb_id;
605         c->swap_count++;
606         if (c->swap_count == 60) {
607                 gettimeofday(&end, NULL);
608                 t = end.tv_sec + end.tv_usec * 1e-6 -
609                         (c->start.tv_sec + c->start.tv_usec * 1e-6);
610                 fprintf(stderr, "freq: %.02fHz\n", c->swap_count / t);
611                 c->swap_count = 0;
612                 c->start = end;
613         }
614 }
615
616 static int
617 set_plane(struct kms_driver *kms, struct connector *c, struct plane *p)
618 {
619         drmModePlaneRes *plane_resources;
620         drmModePlane *ovr;
621         uint32_t handles[4], pitches[4], offsets[4] = {0}; /* we only use [0] */
622         uint32_t plane_id = 0;
623         struct kms_bo *plane_bo;
624         uint32_t plane_flags = 0;
625         int ret, crtc_x, crtc_y, crtc_w, crtc_h;
626         unsigned int i;
627
628         /* find an unused plane which can be connected to our crtc */
629         plane_resources = drmModeGetPlaneResources(fd);
630         if (!plane_resources) {
631                 fprintf(stderr, "drmModeGetPlaneResources failed: %s\n",
632                         strerror(errno));
633                 return -1;
634         }
635
636         for (i = 0; i < plane_resources->count_planes && !plane_id; i++) {
637                 ovr = drmModeGetPlane(fd, plane_resources->planes[i]);
638                 if (!ovr) {
639                         fprintf(stderr, "drmModeGetPlane failed: %s\n",
640                                 strerror(errno));
641                         return -1;
642                 }
643
644                 if ((ovr->possible_crtcs & (1 << c->pipe)) && !ovr->crtc_id)
645                         plane_id = ovr->plane_id;
646
647                 drmModeFreePlane(ovr);
648         }
649
650         fprintf(stderr, "testing %dx%d@%s overlay plane\n",
651                         p->w, p->h, p->format_str);
652
653         if (!plane_id) {
654                 fprintf(stderr, "failed to find plane!\n");
655                 return -1;
656         }
657
658         plane_bo = create_test_buffer(kms, p->fourcc, p->w, p->h, handles,
659                                       pitches, offsets, PATTERN_TILES);
660         if (plane_bo == NULL)
661                 return -1;
662
663         /* just use single plane format for now.. */
664         if (drmModeAddFB2(fd, p->w, p->h, p->fourcc,
665                         handles, pitches, offsets, &p->fb_id, plane_flags)) {
666                 fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
667                 return -1;
668         }
669
670         /* ok, boring.. but for now put in middle of screen: */
671         crtc_x = c->mode->hdisplay / 3;
672         crtc_y = c->mode->vdisplay / 3;
673         crtc_w = crtc_x;
674         crtc_h = crtc_y;
675
676         /* note src coords (last 4 args) are in Q16 format */
677         if (drmModeSetPlane(fd, plane_id, c->crtc, p->fb_id,
678                             plane_flags, crtc_x, crtc_y, crtc_w, crtc_h,
679                             0, 0, p->w << 16, p->h << 16)) {
680                 fprintf(stderr, "failed to enable plane: %s\n",
681                         strerror(errno));
682                 return -1;
683         }
684
685         return 0;
686 }
687
688 static void
689 set_mode(struct connector *c, int count, struct plane *p, int plane_count,
690                 int page_flip)
691 {
692         struct kms_driver *kms;
693         struct kms_bo *bo, *other_bo;
694         unsigned int fb_id, other_fb_id;
695         int i, j, ret, width, height, x;
696         uint32_t handles[4], pitches[4], offsets[4] = {0}; /* we only use [0] */
697         drmEventContext evctx;
698
699         width = 0;
700         height = 0;
701         for (i = 0; i < count; i++) {
702                 connector_find_mode(&c[i]);
703                 if (c[i].mode == NULL)
704                         continue;
705                 width += c[i].mode->hdisplay;
706                 if (height < c[i].mode->vdisplay)
707                         height = c[i].mode->vdisplay;
708         }
709
710         ret = kms_create(fd, &kms);
711         if (ret) {
712                 fprintf(stderr, "failed to create kms driver: %s\n",
713                         strerror(-ret));
714                 return;
715         }
716
717         bo = create_test_buffer(kms, c->fourcc, width, height, handles,
718                                 pitches, offsets, PATTERN_SMPTE);
719         if (bo == NULL)
720                 return;
721
722         ret = drmModeAddFB2(fd, width, height, c->fourcc,
723                             handles, pitches, offsets, &fb_id, 0);
724         if (ret) {
725                 fprintf(stderr, "failed to add fb (%ux%u): %s\n",
726                         width, height, strerror(errno));
727                 return;
728         }
729
730         x = 0;
731         for (i = 0; i < count; i++) {
732                 if (c[i].mode == NULL)
733                         continue;
734
735                 printf("setting mode %s@%s on connector %d, crtc %d\n",
736                        c[i].mode_str, c[i].format_str, c[i].id, c[i].crtc);
737
738                 ret = drmModeSetCrtc(fd, c[i].crtc, fb_id, x, 0,
739                                      &c[i].id, 1, c[i].mode);
740
741                 /* XXX: Actually check if this is needed */
742                 drmModeDirtyFB(fd, fb_id, NULL, 0);
743
744                 x += c[i].mode->hdisplay;
745
746                 if (ret) {
747                         fprintf(stderr, "failed to set mode: %s\n", strerror(errno));
748                         return;
749                 }
750
751                 /* if we have a plane/overlay to show, set that up now: */
752                 for (j = 0; j < plane_count; j++)
753                         if (p[j].con_id == c[i].id)
754                                 if (set_plane(kms, &c[i], &p[j]))
755                                         return;
756         }
757
758         if (!page_flip)
759                 return;
760         
761         other_bo = create_test_buffer(kms, c->fourcc, width, height, handles,
762                                       pitches, offsets, PATTERN_PLAIN);
763         if (other_bo == NULL)
764                 return;
765
766         ret = drmModeAddFB2(fd, width, height, c->fourcc, handles, pitches, offsets,
767                             &other_fb_id, 0);
768         if (ret) {
769                 fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
770                 return;
771         }
772
773         for (i = 0; i < count; i++) {
774                 if (c[i].mode == NULL)
775                         continue;
776
777                 ret = drmModePageFlip(fd, c[i].crtc, other_fb_id,
778                                       DRM_MODE_PAGE_FLIP_EVENT, &c[i]);
779                 if (ret) {
780                         fprintf(stderr, "failed to page flip: %s\n", strerror(errno));
781                         return;
782                 }
783                 gettimeofday(&c[i].start, NULL);
784                 c[i].swap_count = 0;
785                 c[i].fb_id[0] = fb_id;
786                 c[i].fb_id[1] = other_fb_id;
787                 c[i].current_fb_id = other_fb_id;
788         }
789
790         memset(&evctx, 0, sizeof evctx);
791         evctx.version = DRM_EVENT_CONTEXT_VERSION;
792         evctx.vblank_handler = NULL;
793         evctx.page_flip_handler = page_flip_handler;
794         
795         while (1) {
796 #if 0
797                 struct pollfd pfd[2];
798
799                 pfd[0].fd = 0;
800                 pfd[0].events = POLLIN;
801                 pfd[1].fd = fd;
802                 pfd[1].events = POLLIN;
803
804                 if (poll(pfd, 2, -1) < 0) {
805                         fprintf(stderr, "poll error\n");
806                         break;
807                 }
808
809                 if (pfd[0].revents)
810                         break;
811 #else
812                 struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 };
813                 fd_set fds;
814                 int ret;
815
816                 FD_ZERO(&fds);
817                 FD_SET(0, &fds);
818                 FD_SET(fd, &fds);
819                 ret = select(fd + 1, &fds, NULL, NULL, &timeout);
820
821                 if (ret <= 0) {
822                         fprintf(stderr, "select timed out or error (ret %d)\n",
823                                 ret);
824                         continue;
825                 } else if (FD_ISSET(0, &fds)) {
826                         break;
827                 }
828 #endif
829
830                 drmHandleEvent(fd, &evctx);
831         }
832
833         kms_bo_destroy(&bo);
834         kms_bo_destroy(&other_bo);
835         kms_destroy(&kms);
836 }
837
838 extern char *optarg;
839 extern int optind, opterr, optopt;
840 static char optstr[] = "ecpmfs:P:v";
841
842 #define min(a, b)       ((a) < (b) ? (a) : (b))
843
844 static int parse_connector(struct connector *c, const char *arg)
845 {
846         unsigned int len;
847         const char *p;
848         char *endp;
849
850         c->crtc = -1;
851         strcpy(c->format_str, "XR24");
852
853         c->id = strtoul(arg, &endp, 10);
854         if (*endp == '@') {
855                 arg = endp + 1;
856                 c->crtc = strtoul(arg, &endp, 10);
857         }
858         if (*endp != ':')
859                 return -1;
860
861         arg = endp + 1;
862
863         p = strchrnul(arg, '@');
864         len = min(sizeof c->mode_str - 1, p - arg);
865         strncpy(c->mode_str, arg, len);
866         c->mode_str[len] = '\0';
867
868         if (*p == '@') {
869                 strncpy(c->format_str, p + 1, 4);
870                 c->format_str[4] = '\0';
871         }
872
873         c->fourcc = format_fourcc(c->format_str);
874         if (c->fourcc == 0)  {
875                 fprintf(stderr, "unknown format %s\n", c->format_str);
876                 return -1;
877         }
878
879         return 0;
880 }
881
882 static int parse_plane(struct plane *p, const char *arg)
883 {
884         strcpy(p->format_str, "XR24");
885
886         if (sscanf(arg, "%d:%dx%d@%4s", &p->con_id, &p->w, &p->h, &p->format_str) != 4 &&
887             sscanf(arg, "%d:%dx%d", &p->con_id, &p->w, &p->h) != 3)
888                 return -1;
889
890         p->fourcc = format_fourcc(p->format_str);
891         if (p->fourcc == 0) {
892                 fprintf(stderr, "unknown format %s\n", p->format_str);
893                 return -1;
894         }
895
896         return 0;
897 }
898
899 void usage(char *name)
900 {
901         fprintf(stderr, "usage: %s [-ecpmf]\n", name);
902         fprintf(stderr, "\t-e\tlist encoders\n");
903         fprintf(stderr, "\t-c\tlist connectors\n");
904         fprintf(stderr, "\t-p\tlist CRTCs and planes (pipes)\n");
905         fprintf(stderr, "\t-m\tlist modes\n");
906         fprintf(stderr, "\t-f\tlist framebuffers\n");
907         fprintf(stderr, "\t-v\ttest vsynced page flipping\n");
908         fprintf(stderr, "\t-s <connector_id>[@<crtc_id>]:<mode>[@<format>]\tset a mode\n");
909         fprintf(stderr, "\t-P <connector_id>:<w>x<h>[@<format>]\tset a plane\n");
910         fprintf(stderr, "\n\tDefault is to dump all info.\n");
911         exit(0);
912 }
913
914 #define dump_resource(res) if (res) dump_##res()
915
916 static int page_flipping_supported(void)
917 {
918         /*FIXME: generic ioctl needed? */
919         return 1;
920 #if 0
921         int ret, value;
922         struct drm_i915_getparam gp;
923
924         gp.param = I915_PARAM_HAS_PAGEFLIPPING;
925         gp.value = &value;
926
927         ret = drmCommandWriteRead(fd, DRM_I915_GETPARAM, &gp, sizeof(gp));
928         if (ret) {
929                 fprintf(stderr, "drm_i915_getparam: %m\n");
930                 return 0;
931         }
932
933         return *gp.value;
934 #endif
935 }
936
937 int main(int argc, char **argv)
938 {
939         int c;
940         int encoders = 0, connectors = 0, crtcs = 0, planes = 0, framebuffers = 0;
941         int test_vsync = 0;
942         char *modules[] = { "i915", "radeon", "nouveau", "vmwgfx", "omapdrm", "exynos" };
943         unsigned int i;
944         int count = 0, plane_count = 0;
945         struct connector con_args[2];
946         struct plane plane_args[2] = {0};
947         
948         opterr = 0;
949         while ((c = getopt(argc, argv, optstr)) != -1) {
950                 switch (c) {
951                 case 'e':
952                         encoders = 1;
953                         break;
954                 case 'c':
955                         connectors = 1;
956                         break;
957                 case 'p':
958                         crtcs = 1;
959                         planes = 1;
960                         break;
961                 case 'm':
962                         modes = 1;
963                         break;
964                 case 'f':
965                         framebuffers = 1;
966                         break;
967                 case 'v':
968                         test_vsync = 1;
969                         break;
970                 case 's':
971                         if (parse_connector(&con_args[count], optarg) < 0)
972                                 usage(argv[0]);
973                         count++;                                      
974                         break;
975                 case 'P':
976                         if (parse_plane(&plane_args[plane_count], optarg) < 0)
977                                 usage(argv[0]);
978                         plane_count++;
979                         break;
980                 default:
981                         usage(argv[0]);
982                         break;
983                 }
984         }
985
986         if (argc == 1)
987                 encoders = connectors = crtcs = planes = modes = framebuffers = 1;
988
989         for (i = 0; i < ARRAY_SIZE(modules); i++) {
990                 printf("trying to load module %s...", modules[i]);
991                 fd = drmOpen(modules[i], NULL);
992                 if (fd < 0) {
993                         printf("failed.\n");
994                 } else {
995                         printf("success.\n");
996                         break;
997                 }
998         }
999
1000         if (test_vsync && !page_flipping_supported()) {
1001                 fprintf(stderr, "page flipping not supported by drm.\n");
1002                 return -1;
1003         }
1004
1005         if (i == ARRAY_SIZE(modules)) {
1006                 fprintf(stderr, "failed to load any modules, aborting.\n");
1007                 return -1;
1008         }
1009
1010         resources = drmModeGetResources(fd);
1011         if (!resources) {
1012                 fprintf(stderr, "drmModeGetResources failed: %s\n",
1013                         strerror(errno));
1014                 drmClose(fd);
1015                 return 1;
1016         }
1017
1018         dump_resource(encoders);
1019         dump_resource(connectors);
1020         dump_resource(crtcs);
1021         dump_resource(planes);
1022         dump_resource(framebuffers);
1023
1024         if (count > 0) {
1025                 set_mode(con_args, count, plane_args, plane_count, test_vsync);
1026                 getchar();
1027         }
1028
1029         drmModeFreeResources(resources);
1030
1031         return 0;
1032 }