OSDN Git Service

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