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