OSDN Git Service

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