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