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