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