modetest.c revision 2ee35494
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 <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 74struct crtc { 75 drmModeCrtc *crtc; 76 drmModeObjectProperties *props; 77 drmModePropertyRes **props_info; 78 drmModeModeInfo *mode; 79}; 80 81struct encoder { 82 drmModeEncoder *encoder; 83}; 84 85struct connector { 86 drmModeConnector *connector; 87 drmModeObjectProperties *props; 88 drmModePropertyRes **props_info; 89 char *name; 90}; 91 92struct fb { 93 drmModeFB *fb; 94}; 95 96struct plane { 97 drmModePlane *plane; 98 drmModeObjectProperties *props; 99 drmModePropertyRes **props_info; 100}; 101 102struct 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 113struct 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 128static inline int64_t U642I64(uint64_t val) 129{ 130 return (int64_t)*((int64_t *)&val); 131} 132 133#define bit_name_fn(res) \ 134const 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 146static const char *mode_type_names[] = { 147 "builtin", 148 "clock_c", 149 "crtc_c", 150 "preferred", 151 "default", 152 "userdef", 153 "driver", 154}; 155 156static bit_name_fn(mode_type) 157 158static 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 175static bit_name_fn(mode_flag) 176 177static 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 199static 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 221static 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 245static 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 324static 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 367static 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 400static 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 420static 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 464static 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 517static 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 528 drmSetClientCap(dev->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); 529 530 res->res = drmModeGetResources(dev->fd); 531 if (!res->res) { 532 fprintf(stderr, "drmModeGetResources failed: %s\n", 533 strerror(errno)); 534 goto error; 535 } 536 537 res->crtcs = calloc(res->res->count_crtcs, sizeof(*res->crtcs)); 538 res->encoders = calloc(res->res->count_encoders, sizeof(*res->encoders)); 539 res->connectors = calloc(res->res->count_connectors, sizeof(*res->connectors)); 540 res->fbs = calloc(res->res->count_fbs, sizeof(*res->fbs)); 541 542 if (!res->crtcs || !res->encoders || !res->connectors || !res->fbs) 543 goto error; 544 545#define get_resource(_res, __res, type, Type) \ 546 do { \ 547 for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \ 548 (_res)->type##s[i].type = \ 549 drmModeGet##Type(dev->fd, (_res)->__res->type##s[i]); \ 550 if (!(_res)->type##s[i].type) \ 551 fprintf(stderr, "could not get %s %i: %s\n", \ 552 #type, (_res)->__res->type##s[i], \ 553 strerror(errno)); \ 554 } \ 555 } while (0) 556 557 get_resource(res, res, crtc, Crtc); 558 get_resource(res, res, encoder, Encoder); 559 get_resource(res, res, connector, Connector); 560 get_resource(res, res, fb, FB); 561 562 /* Set the name of all connectors based on the type name and the per-type ID. */ 563 for (i = 0; i < res->res->count_connectors; i++) { 564 struct connector *connector = &res->connectors[i]; 565 drmModeConnector *conn = connector->connector; 566 567 asprintf(&connector->name, "%s-%u", 568 util_lookup_connector_type_name(conn->connector_type), 569 conn->connector_type_id); 570 } 571 572#define get_properties(_res, __res, type, Type) \ 573 do { \ 574 for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \ 575 struct type *obj = &res->type##s[i]; \ 576 unsigned int j; \ 577 obj->props = \ 578 drmModeObjectGetProperties(dev->fd, obj->type->type##_id, \ 579 DRM_MODE_OBJECT_##Type); \ 580 if (!obj->props) { \ 581 fprintf(stderr, \ 582 "could not get %s %i properties: %s\n", \ 583 #type, obj->type->type##_id, \ 584 strerror(errno)); \ 585 continue; \ 586 } \ 587 obj->props_info = calloc(obj->props->count_props, \ 588 sizeof(*obj->props_info)); \ 589 if (!obj->props_info) \ 590 continue; \ 591 for (j = 0; j < obj->props->count_props; ++j) \ 592 obj->props_info[j] = \ 593 drmModeGetProperty(dev->fd, obj->props->props[j]); \ 594 } \ 595 } while (0) 596 597 get_properties(res, res, crtc, CRTC); 598 get_properties(res, res, connector, CONNECTOR); 599 600 for (i = 0; i < res->res->count_crtcs; ++i) 601 res->crtcs[i].mode = &res->crtcs[i].crtc->mode; 602 603 res->plane_res = drmModeGetPlaneResources(dev->fd); 604 if (!res->plane_res) { 605 fprintf(stderr, "drmModeGetPlaneResources failed: %s\n", 606 strerror(errno)); 607 return res; 608 } 609 610 res->planes = calloc(res->plane_res->count_planes, sizeof(*res->planes)); 611 if (!res->planes) 612 goto error; 613 614 get_resource(res, plane_res, plane, Plane); 615 get_properties(res, plane_res, plane, PLANE); 616 617 return res; 618 619error: 620 free_resources(res); 621 return NULL; 622} 623 624static int get_crtc_index(struct device *dev, uint32_t id) 625{ 626 int i; 627 628 for (i = 0; i < dev->resources->res->count_crtcs; ++i) { 629 drmModeCrtc *crtc = dev->resources->crtcs[i].crtc; 630 if (crtc && crtc->crtc_id == id) 631 return i; 632 } 633 634 return -1; 635} 636 637static drmModeConnector *get_connector_by_name(struct device *dev, const char *name) 638{ 639 struct connector *connector; 640 int i; 641 642 for (i = 0; i < dev->resources->res->count_connectors; i++) { 643 connector = &dev->resources->connectors[i]; 644 645 if (strcmp(connector->name, name) == 0) 646 return connector->connector; 647 } 648 649 return NULL; 650} 651 652static drmModeConnector *get_connector_by_id(struct device *dev, uint32_t id) 653{ 654 drmModeConnector *connector; 655 int i; 656 657 for (i = 0; i < dev->resources->res->count_connectors; i++) { 658 connector = dev->resources->connectors[i].connector; 659 if (connector && connector->connector_id == id) 660 return connector; 661 } 662 663 return NULL; 664} 665 666static drmModeEncoder *get_encoder_by_id(struct device *dev, uint32_t id) 667{ 668 drmModeEncoder *encoder; 669 int i; 670 671 for (i = 0; i < dev->resources->res->count_encoders; i++) { 672 encoder = dev->resources->encoders[i].encoder; 673 if (encoder && encoder->encoder_id == id) 674 return encoder; 675 } 676 677 return NULL; 678} 679 680/* ----------------------------------------------------------------------------- 681 * Pipes and planes 682 */ 683 684/* 685 * Mode setting with the kernel interfaces is a bit of a chore. 686 * First you have to find the connector in question and make sure the 687 * requested mode is available. 688 * Then you need to find the encoder attached to that connector so you 689 * can bind it with a free crtc. 690 */ 691struct pipe_arg { 692 const char **cons; 693 uint32_t *con_ids; 694 unsigned int num_cons; 695 uint32_t crtc_id; 696 char mode_str[64]; 697 char format_str[5]; 698 unsigned int vrefresh; 699 unsigned int fourcc; 700 drmModeModeInfo *mode; 701 struct crtc *crtc; 702 unsigned int fb_id[2], current_fb_id; 703 struct timeval start; 704 705 int swap_count; 706}; 707 708struct plane_arg { 709 uint32_t plane_id; /* the id of plane to use */ 710 uint32_t crtc_id; /* the id of CRTC to bind to */ 711 bool has_position; 712 int32_t x, y; 713 uint32_t w, h; 714 double scale; 715 unsigned int fb_id; 716 struct bo *bo; 717 char format_str[5]; /* need to leave room for terminating \0 */ 718 unsigned int fourcc; 719}; 720 721static drmModeModeInfo * 722connector_find_mode(struct device *dev, uint32_t con_id, const char *mode_str, 723 const unsigned int vrefresh) 724{ 725 drmModeConnector *connector; 726 drmModeModeInfo *mode; 727 int i; 728 729 connector = get_connector_by_id(dev, con_id); 730 if (!connector || !connector->count_modes) 731 return NULL; 732 733 for (i = 0; i < connector->count_modes; i++) { 734 mode = &connector->modes[i]; 735 if (!strcmp(mode->name, mode_str)) { 736 /* If the vertical refresh frequency is not specified then return the 737 * first mode that match with the name. Else, return the mode that match 738 * the name and the specified vertical refresh frequency. 739 */ 740 if (vrefresh == 0) 741 return mode; 742 else if (mode->vrefresh == vrefresh) 743 return mode; 744 } 745 } 746 747 return NULL; 748} 749 750static struct crtc *pipe_find_crtc(struct device *dev, struct pipe_arg *pipe) 751{ 752 uint32_t possible_crtcs = ~0; 753 uint32_t active_crtcs = 0; 754 unsigned int crtc_idx; 755 unsigned int i; 756 int j; 757 758 for (i = 0; i < pipe->num_cons; ++i) { 759 uint32_t crtcs_for_connector = 0; 760 drmModeConnector *connector; 761 drmModeEncoder *encoder; 762 int idx; 763 764 connector = get_connector_by_id(dev, pipe->con_ids[i]); 765 if (!connector) 766 return NULL; 767 768 for (j = 0; j < connector->count_encoders; ++j) { 769 encoder = get_encoder_by_id(dev, connector->encoders[j]); 770 if (!encoder) 771 continue; 772 773 crtcs_for_connector |= encoder->possible_crtcs; 774 775 idx = get_crtc_index(dev, encoder->crtc_id); 776 if (idx >= 0) 777 active_crtcs |= 1 << idx; 778 } 779 780 possible_crtcs &= crtcs_for_connector; 781 } 782 783 if (!possible_crtcs) 784 return NULL; 785 786 /* Return the first possible and active CRTC if one exists, or the first 787 * possible CRTC otherwise. 788 */ 789 if (possible_crtcs & active_crtcs) 790 crtc_idx = ffs(possible_crtcs & active_crtcs); 791 else 792 crtc_idx = ffs(possible_crtcs); 793 794 return &dev->resources->crtcs[crtc_idx - 1]; 795} 796 797static int pipe_find_crtc_and_mode(struct device *dev, struct pipe_arg *pipe) 798{ 799 drmModeModeInfo *mode = NULL; 800 int i; 801 802 pipe->mode = NULL; 803 804 for (i = 0; i < (int)pipe->num_cons; i++) { 805 mode = connector_find_mode(dev, pipe->con_ids[i], 806 pipe->mode_str, pipe->vrefresh); 807 if (mode == NULL) { 808 fprintf(stderr, 809 "failed to find mode \"%s\" for connector %s\n", 810 pipe->mode_str, pipe->cons[i]); 811 return -EINVAL; 812 } 813 } 814 815 /* If the CRTC ID was specified, get the corresponding CRTC. Otherwise 816 * locate a CRTC that can be attached to all the connectors. 817 */ 818 if (pipe->crtc_id != (uint32_t)-1) { 819 for (i = 0; i < dev->resources->res->count_crtcs; i++) { 820 struct crtc *crtc = &dev->resources->crtcs[i]; 821 822 if (pipe->crtc_id == crtc->crtc->crtc_id) { 823 pipe->crtc = crtc; 824 break; 825 } 826 } 827 } else { 828 pipe->crtc = pipe_find_crtc(dev, pipe); 829 } 830 831 if (!pipe->crtc) { 832 fprintf(stderr, "failed to find CRTC for pipe\n"); 833 return -EINVAL; 834 } 835 836 pipe->mode = mode; 837 pipe->crtc->mode = mode; 838 839 return 0; 840} 841 842/* ----------------------------------------------------------------------------- 843 * Properties 844 */ 845 846struct property_arg { 847 uint32_t obj_id; 848 uint32_t obj_type; 849 char name[DRM_PROP_NAME_LEN+1]; 850 uint32_t prop_id; 851 uint64_t value; 852}; 853 854static void set_property(struct device *dev, struct property_arg *p) 855{ 856 drmModeObjectProperties *props = NULL; 857 drmModePropertyRes **props_info = NULL; 858 const char *obj_type; 859 int ret; 860 int i; 861 862 p->obj_type = 0; 863 p->prop_id = 0; 864 865#define find_object(_res, __res, type, Type) \ 866 do { \ 867 for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \ 868 struct type *obj = &(_res)->type##s[i]; \ 869 if (obj->type->type##_id != p->obj_id) \ 870 continue; \ 871 p->obj_type = DRM_MODE_OBJECT_##Type; \ 872 obj_type = #Type; \ 873 props = obj->props; \ 874 props_info = obj->props_info; \ 875 } \ 876 } while(0) \ 877 878 find_object(dev->resources, res, crtc, CRTC); 879 if (p->obj_type == 0) 880 find_object(dev->resources, res, connector, CONNECTOR); 881 if (p->obj_type == 0) 882 find_object(dev->resources, plane_res, plane, PLANE); 883 if (p->obj_type == 0) { 884 fprintf(stderr, "Object %i not found, can't set property\n", 885 p->obj_id); 886 return; 887 } 888 889 if (!props) { 890 fprintf(stderr, "%s %i has no properties\n", 891 obj_type, p->obj_id); 892 return; 893 } 894 895 for (i = 0; i < (int)props->count_props; ++i) { 896 if (!props_info[i]) 897 continue; 898 if (strcmp(props_info[i]->name, p->name) == 0) 899 break; 900 } 901 902 if (i == (int)props->count_props) { 903 fprintf(stderr, "%s %i has no %s property\n", 904 obj_type, p->obj_id, p->name); 905 return; 906 } 907 908 p->prop_id = props->props[i]; 909 910 ret = drmModeObjectSetProperty(dev->fd, p->obj_id, p->obj_type, 911 p->prop_id, p->value); 912 if (ret < 0) 913 fprintf(stderr, "failed to set %s %i property %s to %" PRIu64 ": %s\n", 914 obj_type, p->obj_id, p->name, p->value, strerror(errno)); 915} 916 917/* -------------------------------------------------------------------------- */ 918 919static void 920page_flip_handler(int fd, unsigned int frame, 921 unsigned int sec, unsigned int usec, void *data) 922{ 923 struct pipe_arg *pipe; 924 unsigned int new_fb_id; 925 struct timeval end; 926 double t; 927 928 pipe = data; 929 if (pipe->current_fb_id == pipe->fb_id[0]) 930 new_fb_id = pipe->fb_id[1]; 931 else 932 new_fb_id = pipe->fb_id[0]; 933 934 drmModePageFlip(fd, pipe->crtc->crtc->crtc_id, new_fb_id, 935 DRM_MODE_PAGE_FLIP_EVENT, pipe); 936 pipe->current_fb_id = new_fb_id; 937 pipe->swap_count++; 938 if (pipe->swap_count == 60) { 939 gettimeofday(&end, NULL); 940 t = end.tv_sec + end.tv_usec * 1e-6 - 941 (pipe->start.tv_sec + pipe->start.tv_usec * 1e-6); 942 fprintf(stderr, "freq: %.02fHz\n", pipe->swap_count / t); 943 pipe->swap_count = 0; 944 pipe->start = end; 945 } 946} 947 948static bool format_support(const drmModePlanePtr ovr, uint32_t fmt) 949{ 950 unsigned int i; 951 952 for (i = 0; i < ovr->count_formats; ++i) { 953 if (ovr->formats[i] == fmt) 954 return true; 955 } 956 957 return false; 958} 959 960static int set_plane(struct device *dev, struct plane_arg *p) 961{ 962 drmModePlane *ovr; 963 uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0}; 964 uint32_t plane_id; 965 struct bo *plane_bo; 966 uint32_t plane_flags = 0; 967 int crtc_x, crtc_y, crtc_w, crtc_h; 968 struct crtc *crtc = NULL; 969 unsigned int pipe; 970 unsigned int i; 971 972 /* Find an unused plane which can be connected to our CRTC. Find the 973 * CRTC index first, then iterate over available planes. 974 */ 975 for (i = 0; i < (unsigned int)dev->resources->res->count_crtcs; i++) { 976 if (p->crtc_id == dev->resources->res->crtcs[i]) { 977 crtc = &dev->resources->crtcs[i]; 978 pipe = i; 979 break; 980 } 981 } 982 983 if (!crtc) { 984 fprintf(stderr, "CRTC %u not found\n", p->crtc_id); 985 return -1; 986 } 987 988 plane_id = p->plane_id; 989 990 for (i = 0; i < dev->resources->plane_res->count_planes; i++) { 991 ovr = dev->resources->planes[i].plane; 992 if (!ovr) 993 continue; 994 995 if (plane_id && plane_id != ovr->plane_id) 996 continue; 997 998 if (!format_support(ovr, p->fourcc)) 999 continue; 1000 1001 if ((ovr->possible_crtcs & (1 << pipe)) && !ovr->crtc_id) { 1002 plane_id = ovr->plane_id; 1003 break; 1004 } 1005 } 1006 1007 if (i == dev->resources->plane_res->count_planes) { 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, UTIL_PATTERN_TILES); 1018 if (plane_bo == NULL) 1019 return -1; 1020 1021 p->bo = plane_bo; 1022 1023 /* just use single plane format for now.. */ 1024 if (drmModeAddFB2(dev->fd, p->w, p->h, p->fourcc, 1025 handles, pitches, offsets, &p->fb_id, plane_flags)) { 1026 fprintf(stderr, "failed to add fb: %s\n", strerror(errno)); 1027 return -1; 1028 } 1029 1030 crtc_w = p->w * p->scale; 1031 crtc_h = p->h * p->scale; 1032 if (!p->has_position) { 1033 /* Default to the middle of the screen */ 1034 crtc_x = (crtc->mode->hdisplay - crtc_w) / 2; 1035 crtc_y = (crtc->mode->vdisplay - crtc_h) / 2; 1036 } else { 1037 crtc_x = p->x; 1038 crtc_y = p->y; 1039 } 1040 1041 /* note src coords (last 4 args) are in Q16 format */ 1042 if (drmModeSetPlane(dev->fd, plane_id, crtc->crtc->crtc_id, p->fb_id, 1043 plane_flags, crtc_x, crtc_y, crtc_w, crtc_h, 1044 0, 0, p->w << 16, p->h << 16)) { 1045 fprintf(stderr, "failed to enable plane: %s\n", 1046 strerror(errno)); 1047 return -1; 1048 } 1049 1050 ovr->crtc_id = crtc->crtc->crtc_id; 1051 1052 return 0; 1053} 1054 1055static void clear_planes(struct device *dev, struct plane_arg *p, unsigned int count) 1056{ 1057 unsigned int i; 1058 1059 for (i = 0; i < count; i++) { 1060 if (p[i].fb_id) 1061 drmModeRmFB(dev->fd, p[i].fb_id); 1062 if (p[i].bo) 1063 bo_destroy(p[i].bo); 1064 } 1065} 1066 1067 1068static void set_mode(struct device *dev, struct pipe_arg *pipes, unsigned int count) 1069{ 1070 uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0}; 1071 unsigned int fb_id; 1072 struct bo *bo; 1073 unsigned int i; 1074 unsigned int j; 1075 int ret, x; 1076 1077 dev->mode.width = 0; 1078 dev->mode.height = 0; 1079 dev->mode.fb_id = 0; 1080 1081 for (i = 0; i < count; i++) { 1082 struct pipe_arg *pipe = &pipes[i]; 1083 1084 ret = pipe_find_crtc_and_mode(dev, pipe); 1085 if (ret < 0) 1086 continue; 1087 1088 dev->mode.width += pipe->mode->hdisplay; 1089 if (dev->mode.height < pipe->mode->vdisplay) 1090 dev->mode.height = pipe->mode->vdisplay; 1091 } 1092 1093 bo = bo_create(dev->fd, pipes[0].fourcc, dev->mode.width, 1094 dev->mode.height, handles, pitches, offsets, 1095 UTIL_PATTERN_SMPTE); 1096 if (bo == NULL) 1097 return; 1098 1099 dev->mode.bo = bo; 1100 1101 ret = drmModeAddFB2(dev->fd, dev->mode.width, dev->mode.height, 1102 pipes[0].fourcc, handles, pitches, offsets, &fb_id, 0); 1103 if (ret) { 1104 fprintf(stderr, "failed to add fb (%ux%u): %s\n", 1105 dev->mode.width, dev->mode.height, strerror(errno)); 1106 return; 1107 } 1108 1109 dev->mode.fb_id = fb_id; 1110 1111 x = 0; 1112 for (i = 0; i < count; i++) { 1113 struct pipe_arg *pipe = &pipes[i]; 1114 1115 if (pipe->mode == NULL) 1116 continue; 1117 1118 printf("setting mode %s-%dHz@%s on connectors ", 1119 pipe->mode_str, pipe->mode->vrefresh, pipe->format_str); 1120 for (j = 0; j < pipe->num_cons; ++j) 1121 printf("%s, ", pipe->cons[j]); 1122 printf("crtc %d\n", pipe->crtc->crtc->crtc_id); 1123 1124 ret = drmModeSetCrtc(dev->fd, pipe->crtc->crtc->crtc_id, fb_id, 1125 x, 0, pipe->con_ids, pipe->num_cons, 1126 pipe->mode); 1127 1128 /* XXX: Actually check if this is needed */ 1129 drmModeDirtyFB(dev->fd, fb_id, NULL, 0); 1130 1131 x += pipe->mode->hdisplay; 1132 1133 if (ret) { 1134 fprintf(stderr, "failed to set mode: %s\n", strerror(errno)); 1135 return; 1136 } 1137 } 1138} 1139 1140static void clear_mode(struct device *dev) 1141{ 1142 if (dev->mode.fb_id) 1143 drmModeRmFB(dev->fd, dev->mode.fb_id); 1144 if (dev->mode.bo) 1145 bo_destroy(dev->mode.bo); 1146} 1147 1148static void set_planes(struct device *dev, struct plane_arg *p, unsigned int count) 1149{ 1150 unsigned int i; 1151 1152 /* set up planes/overlays */ 1153 for (i = 0; i < count; i++) 1154 if (set_plane(dev, &p[i])) 1155 return; 1156} 1157 1158static void set_cursors(struct device *dev, struct pipe_arg *pipes, unsigned int count) 1159{ 1160 uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0}; 1161 struct bo *bo; 1162 unsigned int i; 1163 int ret; 1164 1165 /* maybe make cursor width/height configurable some day */ 1166 uint32_t cw = 64; 1167 uint32_t ch = 64; 1168 1169 /* create cursor bo.. just using PATTERN_PLAIN as it has 1170 * translucent alpha 1171 */ 1172 bo = bo_create(dev->fd, DRM_FORMAT_ARGB8888, cw, ch, handles, pitches, 1173 offsets, UTIL_PATTERN_PLAIN); 1174 if (bo == NULL) 1175 return; 1176 1177 dev->mode.cursor_bo = bo; 1178 1179 for (i = 0; i < count; i++) { 1180 struct pipe_arg *pipe = &pipes[i]; 1181 ret = cursor_init(dev->fd, handles[0], 1182 pipe->crtc->crtc->crtc_id, 1183 pipe->mode->hdisplay, pipe->mode->vdisplay, 1184 cw, ch); 1185 if (ret) { 1186 fprintf(stderr, "failed to init cursor for CRTC[%u]\n", 1187 pipe->crtc_id); 1188 return; 1189 } 1190 } 1191 1192 cursor_start(); 1193} 1194 1195static void clear_cursors(struct device *dev) 1196{ 1197 cursor_stop(); 1198 1199 if (dev->mode.cursor_bo) 1200 bo_destroy(dev->mode.cursor_bo); 1201} 1202 1203static void test_page_flip(struct device *dev, struct pipe_arg *pipes, unsigned int count) 1204{ 1205 uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0}; 1206 unsigned int other_fb_id; 1207 struct bo *other_bo; 1208 drmEventContext evctx; 1209 unsigned int i; 1210 int ret; 1211 1212 other_bo = bo_create(dev->fd, pipes[0].fourcc, dev->mode.width, 1213 dev->mode.height, handles, pitches, offsets, 1214 UTIL_PATTERN_PLAIN); 1215 if (other_bo == NULL) 1216 return; 1217 1218 ret = drmModeAddFB2(dev->fd, dev->mode.width, dev->mode.height, 1219 pipes[0].fourcc, handles, pitches, offsets, 1220 &other_fb_id, 0); 1221 if (ret) { 1222 fprintf(stderr, "failed to add fb: %s\n", strerror(errno)); 1223 goto err; 1224 } 1225 1226 for (i = 0; i < count; i++) { 1227 struct pipe_arg *pipe = &pipes[i]; 1228 1229 if (pipe->mode == NULL) 1230 continue; 1231 1232 ret = drmModePageFlip(dev->fd, pipe->crtc->crtc->crtc_id, 1233 other_fb_id, DRM_MODE_PAGE_FLIP_EVENT, 1234 pipe); 1235 if (ret) { 1236 fprintf(stderr, "failed to page flip: %s\n", strerror(errno)); 1237 goto err_rmfb; 1238 } 1239 gettimeofday(&pipe->start, NULL); 1240 pipe->swap_count = 0; 1241 pipe->fb_id[0] = dev->mode.fb_id; 1242 pipe->fb_id[1] = other_fb_id; 1243 pipe->current_fb_id = other_fb_id; 1244 } 1245 1246 memset(&evctx, 0, sizeof evctx); 1247 evctx.version = DRM_EVENT_CONTEXT_VERSION; 1248 evctx.vblank_handler = NULL; 1249 evctx.page_flip_handler = page_flip_handler; 1250 1251 while (1) { 1252#if 0 1253 struct pollfd pfd[2]; 1254 1255 pfd[0].fd = 0; 1256 pfd[0].events = POLLIN; 1257 pfd[1].fd = fd; 1258 pfd[1].events = POLLIN; 1259 1260 if (poll(pfd, 2, -1) < 0) { 1261 fprintf(stderr, "poll error\n"); 1262 break; 1263 } 1264 1265 if (pfd[0].revents) 1266 break; 1267#else 1268 struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 }; 1269 fd_set fds; 1270 1271 FD_ZERO(&fds); 1272 FD_SET(0, &fds); 1273 FD_SET(dev->fd, &fds); 1274 ret = select(dev->fd + 1, &fds, NULL, NULL, &timeout); 1275 1276 if (ret <= 0) { 1277 fprintf(stderr, "select timed out or error (ret %d)\n", 1278 ret); 1279 continue; 1280 } else if (FD_ISSET(0, &fds)) { 1281 break; 1282 } 1283#endif 1284 1285 drmHandleEvent(dev->fd, &evctx); 1286 } 1287 1288err_rmfb: 1289 drmModeRmFB(dev->fd, other_fb_id); 1290err: 1291 bo_destroy(other_bo); 1292} 1293 1294#define min(a, b) ((a) < (b) ? (a) : (b)) 1295 1296static int parse_connector(struct pipe_arg *pipe, const char *arg) 1297{ 1298 unsigned int len; 1299 unsigned int i; 1300 const char *p; 1301 char *endp; 1302 1303 pipe->vrefresh = 0; 1304 pipe->crtc_id = (uint32_t)-1; 1305 strcpy(pipe->format_str, "XR24"); 1306 1307 /* Count the number of connectors and allocate them. */ 1308 pipe->num_cons = 1; 1309 for (p = arg; *p && *p != ':' && *p != '@'; ++p) { 1310 if (*p == ',') 1311 pipe->num_cons++; 1312 } 1313 1314 pipe->con_ids = calloc(pipe->num_cons, sizeof(*pipe->con_ids)); 1315 pipe->cons = calloc(pipe->num_cons, sizeof(*pipe->cons)); 1316 if (pipe->con_ids == NULL || pipe->cons == NULL) 1317 return -1; 1318 1319 /* Parse the connectors. */ 1320 for (i = 0, p = arg; i < pipe->num_cons; ++i, p = endp + 1) { 1321 endp = strpbrk(p, ",@:"); 1322 if (!endp) 1323 break; 1324 1325 pipe->cons[i] = strndup(p, endp - p); 1326 1327 if (*endp != ',') 1328 break; 1329 } 1330 1331 if (i != pipe->num_cons - 1) 1332 return -1; 1333 1334 /* Parse the remaining parameters. */ 1335 if (*endp == '@') { 1336 arg = endp + 1; 1337 pipe->crtc_id = strtoul(arg, &endp, 10); 1338 } 1339 if (*endp != ':') 1340 return -1; 1341 1342 arg = endp + 1; 1343 1344 /* Search for the vertical refresh or the format. */ 1345 p = strpbrk(arg, "-@"); 1346 if (p == NULL) 1347 p = arg + strlen(arg); 1348 len = min(sizeof pipe->mode_str - 1, (unsigned int)(p - arg)); 1349 strncpy(pipe->mode_str, arg, len); 1350 pipe->mode_str[len] = '\0'; 1351 1352 if (*p == '-') { 1353 pipe->vrefresh = strtoul(p + 1, &endp, 10); 1354 p = endp; 1355 } 1356 1357 if (*p == '@') { 1358 strncpy(pipe->format_str, p + 1, 4); 1359 pipe->format_str[4] = '\0'; 1360 } 1361 1362 pipe->fourcc = util_format_fourcc(pipe->format_str); 1363 if (pipe->fourcc == 0) { 1364 fprintf(stderr, "unknown format %s\n", pipe->format_str); 1365 return -1; 1366 } 1367 1368 return 0; 1369} 1370 1371static int parse_plane(struct plane_arg *plane, const char *p) 1372{ 1373 char *end; 1374 1375 plane->plane_id = strtoul(p, &end, 10); 1376 if (*end != '@') 1377 return -EINVAL; 1378 1379 p = end + 1; 1380 plane->crtc_id = strtoul(p, &end, 10); 1381 if (*end != ':') 1382 return -EINVAL; 1383 1384 p = end + 1; 1385 plane->w = strtoul(p, &end, 10); 1386 if (*end != 'x') 1387 return -EINVAL; 1388 1389 p = end + 1; 1390 plane->h = strtoul(p, &end, 10); 1391 1392 if (*end == '+' || *end == '-') { 1393 plane->x = strtol(end, &end, 10); 1394 if (*end != '+' && *end != '-') 1395 return -EINVAL; 1396 plane->y = strtol(end, &end, 10); 1397 1398 plane->has_position = true; 1399 } 1400 1401 if (*end == '*') { 1402 p = end + 1; 1403 plane->scale = strtod(p, &end); 1404 if (plane->scale <= 0.0) 1405 return -EINVAL; 1406 } else { 1407 plane->scale = 1.0; 1408 } 1409 1410 if (*end == '@') { 1411 p = end + 1; 1412 if (strlen(p) != 4) 1413 return -EINVAL; 1414 1415 strcpy(plane->format_str, p); 1416 } else { 1417 strcpy(plane->format_str, "XR24"); 1418 } 1419 1420 plane->fourcc = util_format_fourcc(plane->format_str); 1421 if (plane->fourcc == 0) { 1422 fprintf(stderr, "unknown format %s\n", plane->format_str); 1423 return -EINVAL; 1424 } 1425 1426 return 0; 1427} 1428 1429static int parse_property(struct property_arg *p, const char *arg) 1430{ 1431 if (sscanf(arg, "%d:%32[^:]:%" SCNu64, &p->obj_id, p->name, &p->value) != 3) 1432 return -1; 1433 1434 p->obj_type = 0; 1435 p->name[DRM_PROP_NAME_LEN] = '\0'; 1436 1437 return 0; 1438} 1439 1440static void usage(char *name) 1441{ 1442 fprintf(stderr, "usage: %s [-cDdefMPpsCvw]\n", name); 1443 1444 fprintf(stderr, "\n Query options:\n\n"); 1445 fprintf(stderr, "\t-c\tlist connectors\n"); 1446 fprintf(stderr, "\t-e\tlist encoders\n"); 1447 fprintf(stderr, "\t-f\tlist framebuffers\n"); 1448 fprintf(stderr, "\t-p\tlist CRTCs and planes (pipes)\n"); 1449 1450 fprintf(stderr, "\n Test options:\n\n"); 1451 fprintf(stderr, "\t-P <plane_id>@<crtc_id>:<w>x<h>[+<x>+<y>][*<scale>][@<format>]\tset a plane\n"); 1452 fprintf(stderr, "\t-s <connector_id>[,<connector_id>][@<crtc_id>]:<mode>[-<vrefresh>][@<format>]\tset a mode\n"); 1453 fprintf(stderr, "\t-C\ttest hw cursor\n"); 1454 fprintf(stderr, "\t-v\ttest vsynced page flipping\n"); 1455 fprintf(stderr, "\t-w <obj_id>:<prop_name>:<value>\tset property\n"); 1456 1457 fprintf(stderr, "\n Generic options:\n\n"); 1458 fprintf(stderr, "\t-d\tdrop master after mode set\n"); 1459 fprintf(stderr, "\t-M module\tuse the given driver\n"); 1460 fprintf(stderr, "\t-D device\tuse the given device\n"); 1461 1462 fprintf(stderr, "\n\tDefault is to dump all info.\n"); 1463 exit(0); 1464} 1465 1466static int page_flipping_supported(void) 1467{ 1468 /*FIXME: generic ioctl needed? */ 1469 return 1; 1470#if 0 1471 int ret, value; 1472 struct drm_i915_getparam gp; 1473 1474 gp.param = I915_PARAM_HAS_PAGEFLIPPING; 1475 gp.value = &value; 1476 1477 ret = drmCommandWriteRead(fd, DRM_I915_GETPARAM, &gp, sizeof(gp)); 1478 if (ret) { 1479 fprintf(stderr, "drm_i915_getparam: %m\n"); 1480 return 0; 1481 } 1482 1483 return *gp.value; 1484#endif 1485} 1486 1487static int cursor_supported(void) 1488{ 1489 /*FIXME: generic ioctl needed? */ 1490 return 1; 1491} 1492 1493static int pipe_resolve_connectors(struct device *dev, struct pipe_arg *pipe) 1494{ 1495 drmModeConnector *connector; 1496 unsigned int i; 1497 uint32_t id; 1498 char *endp; 1499 1500 for (i = 0; i < pipe->num_cons; i++) { 1501 id = strtoul(pipe->cons[i], &endp, 10); 1502 if (endp == pipe->cons[i]) { 1503 connector = get_connector_by_name(dev, pipe->cons[i]); 1504 if (!connector) { 1505 fprintf(stderr, "no connector named '%s'\n", 1506 pipe->cons[i]); 1507 return -ENODEV; 1508 } 1509 1510 id = connector->connector_id; 1511 } 1512 1513 pipe->con_ids[i] = id; 1514 } 1515 1516 return 0; 1517} 1518 1519static char optstr[] = "cdD:efM:P:ps:Cvw:"; 1520 1521int main(int argc, char **argv) 1522{ 1523 struct device dev; 1524 1525 int c; 1526 int encoders = 0, connectors = 0, crtcs = 0, planes = 0, framebuffers = 0; 1527 int drop_master = 0; 1528 int test_vsync = 0; 1529 int test_cursor = 0; 1530 char *device = NULL; 1531 char *module = NULL; 1532 unsigned int i; 1533 unsigned int count = 0, plane_count = 0; 1534 unsigned int prop_count = 0; 1535 struct pipe_arg *pipe_args = NULL; 1536 struct plane_arg *plane_args = NULL; 1537 struct property_arg *prop_args = NULL; 1538 unsigned int args = 0; 1539 int ret; 1540 1541 memset(&dev, 0, sizeof dev); 1542 1543 opterr = 0; 1544 while ((c = getopt(argc, argv, optstr)) != -1) { 1545 args++; 1546 1547 switch (c) { 1548 case 'c': 1549 connectors = 1; 1550 break; 1551 case 'D': 1552 device = optarg; 1553 args--; 1554 break; 1555 case 'd': 1556 drop_master = 1; 1557 break; 1558 case 'e': 1559 encoders = 1; 1560 break; 1561 case 'f': 1562 framebuffers = 1; 1563 break; 1564 case 'M': 1565 module = optarg; 1566 /* Preserve the default behaviour of dumping all information. */ 1567 args--; 1568 break; 1569 case 'P': 1570 plane_args = realloc(plane_args, 1571 (plane_count + 1) * sizeof *plane_args); 1572 if (plane_args == NULL) { 1573 fprintf(stderr, "memory allocation failed\n"); 1574 return 1; 1575 } 1576 memset(&plane_args[plane_count], 0, sizeof(*plane_args)); 1577 1578 if (parse_plane(&plane_args[plane_count], optarg) < 0) 1579 usage(argv[0]); 1580 1581 plane_count++; 1582 break; 1583 case 'p': 1584 crtcs = 1; 1585 planes = 1; 1586 break; 1587 case 's': 1588 pipe_args = realloc(pipe_args, 1589 (count + 1) * sizeof *pipe_args); 1590 if (pipe_args == NULL) { 1591 fprintf(stderr, "memory allocation failed\n"); 1592 return 1; 1593 } 1594 memset(&pipe_args[count], 0, sizeof(*pipe_args)); 1595 1596 if (parse_connector(&pipe_args[count], optarg) < 0) 1597 usage(argv[0]); 1598 1599 count++; 1600 break; 1601 case 'C': 1602 test_cursor = 1; 1603 break; 1604 case 'v': 1605 test_vsync = 1; 1606 break; 1607 case 'w': 1608 prop_args = realloc(prop_args, 1609 (prop_count + 1) * sizeof *prop_args); 1610 if (prop_args == NULL) { 1611 fprintf(stderr, "memory allocation failed\n"); 1612 return 1; 1613 } 1614 memset(&prop_args[prop_count], 0, sizeof(*prop_args)); 1615 1616 if (parse_property(&prop_args[prop_count], optarg) < 0) 1617 usage(argv[0]); 1618 1619 prop_count++; 1620 break; 1621 default: 1622 usage(argv[0]); 1623 break; 1624 } 1625 } 1626 1627 if (!args) 1628 encoders = connectors = crtcs = planes = framebuffers = 1; 1629 1630 dev.fd = util_open(device, module); 1631 if (dev.fd < 0) 1632 return -1; 1633 1634 if (test_vsync && !page_flipping_supported()) { 1635 fprintf(stderr, "page flipping not supported by drm.\n"); 1636 return -1; 1637 } 1638 1639 if (test_vsync && !count) { 1640 fprintf(stderr, "page flipping requires at least one -s option.\n"); 1641 return -1; 1642 } 1643 1644 if (test_cursor && !cursor_supported()) { 1645 fprintf(stderr, "hw cursor not supported by drm.\n"); 1646 return -1; 1647 } 1648 1649 dev.resources = get_resources(&dev); 1650 if (!dev.resources) { 1651 drmClose(dev.fd); 1652 return 1; 1653 } 1654 1655 for (i = 0; i < count; i++) { 1656 if (pipe_resolve_connectors(&dev, &pipe_args[i]) < 0) { 1657 free_resources(dev.resources); 1658 drmClose(dev.fd); 1659 return 1; 1660 } 1661 } 1662 1663#define dump_resource(dev, res) if (res) dump_##res(dev) 1664 1665 dump_resource(&dev, encoders); 1666 dump_resource(&dev, connectors); 1667 dump_resource(&dev, crtcs); 1668 dump_resource(&dev, planes); 1669 dump_resource(&dev, framebuffers); 1670 1671 for (i = 0; i < prop_count; ++i) 1672 set_property(&dev, &prop_args[i]); 1673 1674 if (count || plane_count) { 1675 uint64_t cap = 0; 1676 1677 ret = drmGetCap(dev.fd, DRM_CAP_DUMB_BUFFER, &cap); 1678 if (ret || cap == 0) { 1679 fprintf(stderr, "driver doesn't support the dumb buffer API\n"); 1680 return 1; 1681 } 1682 1683 if (count) 1684 set_mode(&dev, pipe_args, count); 1685 1686 if (plane_count) 1687 set_planes(&dev, plane_args, plane_count); 1688 1689 if (test_cursor) 1690 set_cursors(&dev, pipe_args, count); 1691 1692 if (test_vsync) 1693 test_page_flip(&dev, pipe_args, count); 1694 1695 if (drop_master) 1696 drmDropMaster(dev.fd); 1697 1698 getchar(); 1699 1700 if (test_cursor) 1701 clear_cursors(&dev); 1702 1703 bo_destroy(dev.mode.bo); 1704 } 1705 1706 free_resources(dev.resources); 1707 1708 return 0; 1709} 1710