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 #include <assert.h> 42 #include <ctype.h> 43 #include <stdbool.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <stdint.h> 47 #include <inttypes.h> 48 #include <unistd.h> 49 #include <string.h> 50 #include <strings.h> 51 #include <errno.h> 52 #include <poll.h> 53 #include <sys/time.h> 54 #if HAVE_SYS_SELECT_H 55 #include <sys/select.h> 56 #endif 57 #include <math.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 71 static enum util_fill_pattern primary_fill = UTIL_PATTERN_SMPTE; 72 static enum util_fill_pattern secondary_fill = UTIL_PATTERN_TILES; 73 static drmModeModeInfo user_mode; 74 75 struct crtc { 76 drmModeCrtc *crtc; 77 drmModeObjectProperties *props; 78 drmModePropertyRes **props_info; 79 drmModeModeInfo *mode; 80 }; 81 82 struct encoder { 83 drmModeEncoder *encoder; 84 }; 85 86 struct connector { 87 drmModeConnector *connector; 88 drmModeObjectProperties *props; 89 drmModePropertyRes **props_info; 90 char *name; 91 }; 92 93 struct fb { 94 drmModeFB *fb; 95 }; 96 97 struct plane { 98 drmModePlane *plane; 99 drmModeObjectProperties *props; 100 drmModePropertyRes **props_info; 101 }; 102 103 struct resources { 104 struct crtc *crtcs; 105 int count_crtcs; 106 struct encoder *encoders; 107 int count_encoders; 108 struct connector *connectors; 109 int count_connectors; 110 struct fb *fbs; 111 int count_fbs; 112 struct plane *planes; 113 uint32_t count_planes; 114 }; 115 116 struct device { 117 int fd; 118 119 struct resources *resources; 120 121 struct { 122 unsigned int width; 123 unsigned int height; 124 125 unsigned int fb_id; 126 struct bo *bo; 127 struct bo *cursor_bo; 128 } mode; 129 130 int use_atomic; 131 drmModeAtomicReq *req; 132 int32_t writeback_fence_fd; 133 }; 134 135 static inline int64_t U642I64(uint64_t val) 136 { 137 return (int64_t)*((int64_t *)&val); 138 } 139 140 static float mode_vrefresh(drmModeModeInfo *mode) 141 { 142 unsigned int num, den; 143 144 num = mode->clock; 145 den = mode->htotal * mode->vtotal; 146 147 if (mode->flags & DRM_MODE_FLAG_INTERLACE) 148 num *= 2; 149 if (mode->flags & DRM_MODE_FLAG_DBLSCAN) 150 den *= 2; 151 if (mode->vscan > 1) 152 den *= mode->vscan; 153 154 return num * 1000.00 / den; 155 } 156 157 #define bit_name_fn(res) \ 158 const char * res##_str(int type) { \ 159 unsigned int i; \ 160 const char *sep = ""; \ 161 for (i = 0; i < ARRAY_SIZE(res##_names); i++) { \ 162 if (type & (1 << i)) { \ 163 printf("%s%s", sep, res##_names[i]); \ 164 sep = ", "; \ 165 } \ 166 } \ 167 return NULL; \ 168 } 169 170 static const char *mode_type_names[] = { 171 "builtin", 172 "clock_c", 173 "crtc_c", 174 "preferred", 175 "default", 176 "userdef", 177 "driver", 178 }; 179 180 static bit_name_fn(mode_type) 181 182 static const char *mode_flag_names[] = { 183 "phsync", 184 "nhsync", 185 "pvsync", 186 "nvsync", 187 "interlace", 188 "dblscan", 189 "csync", 190 "pcsync", 191 "ncsync", 192 "hskew", 193 "bcast", 194 "pixmux", 195 "dblclk", 196 "clkdiv2" 197 }; 198 199 static bit_name_fn(mode_flag) 200 201 static void dump_fourcc(uint32_t fourcc) 202 { 203 char *name = drmGetFormatName(fourcc); 204 printf(" %s", name); 205 free(name); 206 } 207 208 static void dump_encoders(struct device *dev) 209 { 210 drmModeEncoder *encoder; 211 int i; 212 213 printf("Encoders:\n"); 214 printf("id\tcrtc\ttype\tpossible crtcs\tpossible clones\t\n"); 215 for (i = 0; i < dev->resources->count_encoders; i++) { 216 encoder = dev->resources->encoders[i].encoder; 217 if (!encoder) 218 continue; 219 220 printf("%d\t%d\t%s\t0x%08x\t0x%08x\n", 221 encoder->encoder_id, 222 encoder->crtc_id, 223 util_lookup_encoder_type_name(encoder->encoder_type), 224 encoder->possible_crtcs, 225 encoder->possible_clones); 226 } 227 printf("\n"); 228 } 229 230 static void dump_mode(drmModeModeInfo *mode, int index) 231 { 232 printf(" #%i %s %.2f %d %d %d %d %d %d %d %d %d", 233 index, 234 mode->name, 235 mode_vrefresh(mode), 236 mode->hdisplay, 237 mode->hsync_start, 238 mode->hsync_end, 239 mode->htotal, 240 mode->vdisplay, 241 mode->vsync_start, 242 mode->vsync_end, 243 mode->vtotal, 244 mode->clock); 245 246 printf(" flags: "); 247 mode_flag_str(mode->flags); 248 printf("; type: "); 249 mode_type_str(mode->type); 250 printf("\n"); 251 } 252 253 static void dump_blob(struct device *dev, uint32_t blob_id) 254 { 255 uint32_t i; 256 unsigned char *blob_data; 257 drmModePropertyBlobPtr blob; 258 259 blob = drmModeGetPropertyBlob(dev->fd, blob_id); 260 if (!blob) { 261 printf("\n"); 262 return; 263 } 264 265 blob_data = blob->data; 266 267 for (i = 0; i < blob->length; i++) { 268 if (i % 16 == 0) 269 printf("\n\t\t\t"); 270 printf("%.2hhx", blob_data[i]); 271 } 272 printf("\n"); 273 274 drmModeFreePropertyBlob(blob); 275 } 276 277 static const char *modifier_to_string(uint64_t modifier) 278 { 279 static char mod_string[4096]; 280 281 char *modifier_name = drmGetFormatModifierName(modifier); 282 char *vendor_name = drmGetFormatModifierVendor(modifier); 283 memset(mod_string, 0x00, sizeof(mod_string)); 284 285 if (!modifier_name) { 286 if (vendor_name) 287 snprintf(mod_string, sizeof(mod_string), "%s_%s", 288 vendor_name, "UNKNOWN_MODIFIER"); 289 else 290 snprintf(mod_string, sizeof(mod_string), "%s_%s", 291 "UNKNOWN_VENDOR", "UNKNOWN_MODIFIER"); 292 /* safe, as free is no-op for NULL */ 293 free(vendor_name); 294 return mod_string; 295 } 296 297 if (modifier == DRM_FORMAT_MOD_LINEAR) { 298 snprintf(mod_string, sizeof(mod_string), "%s", modifier_name); 299 free(modifier_name); 300 free(vendor_name); 301 return mod_string; 302 } 303 304 snprintf(mod_string, sizeof(mod_string), "%s_%s", 305 vendor_name, modifier_name); 306 307 free(modifier_name); 308 free(vendor_name); 309 return mod_string; 310 } 311 312 static void dump_in_formats(struct device *dev, uint32_t blob_id) 313 { 314 drmModeFormatModifierIterator iter = {0}; 315 drmModePropertyBlobPtr blob; 316 uint32_t fmt = 0; 317 318 printf("\t\tin_formats blob decoded:\n"); 319 blob = drmModeGetPropertyBlob(dev->fd, blob_id); 320 if (!blob) { 321 printf("\n"); 322 return; 323 } 324 325 while (drmModeFormatModifierBlobIterNext(blob, &iter)) { 326 if (!fmt || fmt != iter.fmt) { 327 printf("%s\t\t\t", !fmt ? "" : "\n"); 328 fmt = iter.fmt; 329 dump_fourcc(fmt); 330 printf(": "); 331 } 332 333 printf(" %s(0x%"PRIx64")", modifier_to_string(iter.mod), iter.mod); 334 } 335 336 printf("\n"); 337 338 drmModeFreePropertyBlob(blob); 339 } 340 341 static void dump_prop(struct device *dev, drmModePropertyPtr prop, 342 uint32_t prop_id, uint64_t value) 343 { 344 int i; 345 printf("\t%d", prop_id); 346 if (!prop) { 347 printf("\n"); 348 return; 349 } 350 351 printf(" %s:\n", prop->name); 352 353 printf("\t\tflags:"); 354 if (prop->flags & DRM_MODE_PROP_PENDING) 355 printf(" pending"); 356 if (prop->flags & DRM_MODE_PROP_IMMUTABLE) 357 printf(" immutable"); 358 if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE)) 359 printf(" signed range"); 360 if (drm_property_type_is(prop, DRM_MODE_PROP_RANGE)) 361 printf(" range"); 362 if (drm_property_type_is(prop, DRM_MODE_PROP_ENUM)) 363 printf(" enum"); 364 if (drm_property_type_is(prop, DRM_MODE_PROP_BITMASK)) 365 printf(" bitmask"); 366 if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB)) 367 printf(" blob"); 368 if (drm_property_type_is(prop, DRM_MODE_PROP_OBJECT)) 369 printf(" object"); 370 printf("\n"); 371 372 if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE)) { 373 printf("\t\tvalues:"); 374 for (i = 0; i < prop->count_values; i++) 375 printf(" %"PRId64, U642I64(prop->values[i])); 376 printf("\n"); 377 } 378 379 if (drm_property_type_is(prop, DRM_MODE_PROP_RANGE)) { 380 printf("\t\tvalues:"); 381 for (i = 0; i < prop->count_values; i++) 382 printf(" %"PRIu64, prop->values[i]); 383 printf("\n"); 384 } 385 386 if (drm_property_type_is(prop, DRM_MODE_PROP_ENUM)) { 387 printf("\t\tenums:"); 388 for (i = 0; i < prop->count_enums; i++) 389 printf(" %s=%"PRIu64, prop->enums[i].name, 390 (uint64_t)prop->enums[i].value); 391 printf("\n"); 392 } else if (drm_property_type_is(prop, DRM_MODE_PROP_BITMASK)) { 393 printf("\t\tvalues:"); 394 for (i = 0; i < prop->count_enums; i++) 395 printf(" %s=0x%llx", prop->enums[i].name, 396 (1LL << prop->enums[i].value)); 397 printf("\n"); 398 } else { 399 assert(prop->count_enums == 0); 400 } 401 402 if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB)) { 403 printf("\t\tblobs:\n"); 404 for (i = 0; i < prop->count_blobs; i++) 405 dump_blob(dev, prop->blob_ids[i]); 406 printf("\n"); 407 } else { 408 assert(prop->count_blobs == 0); 409 } 410 411 printf("\t\tvalue:"); 412 if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB)) 413 dump_blob(dev, value); 414 else if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE)) 415 printf(" %"PRId64"\n", value); 416 else 417 printf(" %"PRIu64"\n", value); 418 419 if (strcmp(prop->name, "IN_FORMATS") == 0) 420 dump_in_formats(dev, value); 421 } 422 423 static void dump_connectors(struct device *dev) 424 { 425 int i, j; 426 427 printf("Connectors:\n"); 428 printf("id\tencoder\tstatus\t\tname\t\tsize (mm)\tmodes\tencoders\n"); 429 for (i = 0; i < dev->resources->count_connectors; i++) { 430 struct connector *_connector = &dev->resources->connectors[i]; 431 drmModeConnector *connector = _connector->connector; 432 if (!connector) 433 continue; 434 435 printf("%d\t%d\t%s\t%-15s\t%dx%d\t\t%d\t", 436 connector->connector_id, 437 connector->encoder_id, 438 util_lookup_connector_status_name(connector->connection), 439 _connector->name, 440 connector->mmWidth, connector->mmHeight, 441 connector->count_modes); 442 443 for (j = 0; j < connector->count_encoders; j++) 444 printf("%s%d", j > 0 ? ", " : "", connector->encoders[j]); 445 printf("\n"); 446 447 if (connector->count_modes) { 448 printf(" modes:\n"); 449 printf("\tindex name refresh (Hz) hdisp hss hse htot vdisp " 450 "vss vse vtot\n"); 451 for (j = 0; j < connector->count_modes; j++) 452 dump_mode(&connector->modes[j], j); 453 } 454 455 if (_connector->props) { 456 printf(" props:\n"); 457 for (j = 0; j < (int)_connector->props->count_props; j++) 458 dump_prop(dev, _connector->props_info[j], 459 _connector->props->props[j], 460 _connector->props->prop_values[j]); 461 } 462 } 463 printf("\n"); 464 } 465 466 static void dump_crtcs(struct device *dev) 467 { 468 int i; 469 uint32_t j; 470 471 printf("CRTCs:\n"); 472 printf("id\tfb\tpos\tsize\n"); 473 for (i = 0; i < dev->resources->count_crtcs; i++) { 474 struct crtc *_crtc = &dev->resources->crtcs[i]; 475 drmModeCrtc *crtc = _crtc->crtc; 476 if (!crtc) 477 continue; 478 479 printf("%d\t%d\t(%d,%d)\t(%dx%d)\n", 480 crtc->crtc_id, 481 crtc->buffer_id, 482 crtc->x, crtc->y, 483 crtc->width, crtc->height); 484 dump_mode(&crtc->mode, 0); 485 486 if (_crtc->props) { 487 printf(" props:\n"); 488 for (j = 0; j < _crtc->props->count_props; j++) 489 dump_prop(dev, _crtc->props_info[j], 490 _crtc->props->props[j], 491 _crtc->props->prop_values[j]); 492 } else { 493 printf(" no properties found\n"); 494 } 495 } 496 printf("\n"); 497 } 498 499 static void dump_framebuffers(struct device *dev) 500 { 501 drmModeFB *fb; 502 int i; 503 504 printf("Frame buffers:\n"); 505 printf("id\tsize\tpitch\n"); 506 for (i = 0; i < dev->resources->count_fbs; i++) { 507 fb = dev->resources->fbs[i].fb; 508 if (!fb) 509 continue; 510 511 printf("%u\t(%ux%u)\t%u\n", 512 fb->fb_id, 513 fb->width, fb->height, 514 fb->pitch); 515 } 516 printf("\n"); 517 } 518 519 static void dump_planes(struct device *dev) 520 { 521 unsigned int i, j; 522 523 printf("Planes:\n"); 524 printf("id\tcrtc\tfb\tCRTC x,y\tx,y\tgamma size\tpossible crtcs\n"); 525 526 for (i = 0; i < dev->resources->count_planes; i++) { 527 struct plane *plane = &dev->resources->planes[i]; 528 drmModePlane *ovr = plane->plane; 529 if (!ovr) 530 continue; 531 532 printf("%d\t%d\t%d\t%d,%d\t\t%d,%d\t%-8d\t0x%08x\n", 533 ovr->plane_id, ovr->crtc_id, ovr->fb_id, 534 ovr->crtc_x, ovr->crtc_y, ovr->x, ovr->y, 535 ovr->gamma_size, ovr->possible_crtcs); 536 537 if (!ovr->count_formats) 538 continue; 539 540 printf(" formats:"); 541 for (j = 0; j < ovr->count_formats; j++) 542 dump_fourcc(ovr->formats[j]); 543 printf("\n"); 544 545 if (plane->props) { 546 printf(" props:\n"); 547 for (j = 0; j < plane->props->count_props; j++) 548 dump_prop(dev, plane->props_info[j], 549 plane->props->props[j], 550 plane->props->prop_values[j]); 551 } else { 552 printf(" no properties found\n"); 553 } 554 } 555 printf("\n"); 556 557 return; 558 } 559 560 static void free_resources(struct resources *res) 561 { 562 int i; 563 564 if (!res) 565 return; 566 567 #define free_resource(_res, type, Type) \ 568 do { \ 569 if (!(_res)->type##s) \ 570 break; \ 571 for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \ 572 if (!(_res)->type##s[i].type) \ 573 break; \ 574 drmModeFree##Type((_res)->type##s[i].type); \ 575 } \ 576 free((_res)->type##s); \ 577 } while (0) 578 579 #define free_properties(_res, type) \ 580 do { \ 581 for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \ 582 unsigned int j; \ 583 for (j = 0; j < res->type##s[i].props->count_props; ++j)\ 584 drmModeFreeProperty(res->type##s[i].props_info[j]);\ 585 free(res->type##s[i].props_info); \ 586 drmModeFreeObjectProperties(res->type##s[i].props); \ 587 } \ 588 } while (0) 589 590 free_properties(res, plane); 591 free_resource(res, plane, Plane); 592 593 free_properties(res, connector); 594 free_properties(res, crtc); 595 596 for (i = 0; i < res->count_connectors; i++) 597 free(res->connectors[i].name); 598 599 free_resource(res, fb, FB); 600 free_resource(res, connector, Connector); 601 free_resource(res, encoder, Encoder); 602 free_resource(res, crtc, Crtc); 603 604 free(res); 605 } 606 607 static struct resources *get_resources(struct device *dev) 608 { 609 drmModeRes *_res; 610 drmModePlaneRes *plane_res; 611 struct resources *res; 612 int i; 613 614 res = calloc(1, sizeof(*res)); 615 if (res == 0) 616 return NULL; 617 618 drmSetClientCap(dev->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); 619 620 _res = drmModeGetResources(dev->fd); 621 if (!_res) { 622 fprintf(stderr, "drmModeGetResources failed: %s\n", 623 strerror(errno)); 624 free(res); 625 return NULL; 626 } 627 628 res->count_crtcs = _res->count_crtcs; 629 res->count_encoders = _res->count_encoders; 630 res->count_connectors = _res->count_connectors; 631 res->count_fbs = _res->count_fbs; 632 633 res->crtcs = calloc(res->count_crtcs, sizeof(*res->crtcs)); 634 res->encoders = calloc(res->count_encoders, sizeof(*res->encoders)); 635 res->connectors = calloc(res->count_connectors, sizeof(*res->connectors)); 636 res->fbs = calloc(res->count_fbs, sizeof(*res->fbs)); 637 638 if (!res->crtcs || !res->encoders || !res->connectors || !res->fbs) { 639 drmModeFreeResources(_res); 640 goto error; 641 } 642 643 #define get_resource(_res, __res, type, Type) \ 644 do { \ 645 for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \ 646 uint32_t type##id = (__res)->type##s[i]; \ 647 (_res)->type##s[i].type = \ 648 drmModeGet##Type(dev->fd, type##id); \ 649 if (!(_res)->type##s[i].type) \ 650 fprintf(stderr, "could not get %s %i: %s\n", \ 651 #type, type##id, \ 652 strerror(errno)); \ 653 } \ 654 } while (0) 655 656 get_resource(res, _res, crtc, Crtc); 657 get_resource(res, _res, encoder, Encoder); 658 get_resource(res, _res, connector, Connector); 659 get_resource(res, _res, fb, FB); 660 661 drmModeFreeResources(_res); 662 663 /* Set the name of all connectors based on the type name and the per-type ID. */ 664 for (i = 0; i < res->count_connectors; i++) { 665 struct connector *connector = &res->connectors[i]; 666 drmModeConnector *conn = connector->connector; 667 int num; 668 669 num = asprintf(&connector->name, "%s-%u", 670 drmModeGetConnectorTypeName(conn->connector_type), 671 conn->connector_type_id); 672 if (num < 0) 673 goto error; 674 } 675 676 #define get_properties(_res, type, Type) \ 677 do { \ 678 for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \ 679 struct type *obj = &res->type##s[i]; \ 680 unsigned int j; \ 681 obj->props = \ 682 drmModeObjectGetProperties(dev->fd, obj->type->type##_id, \ 683 DRM_MODE_OBJECT_##Type); \ 684 if (!obj->props) { \ 685 fprintf(stderr, \ 686 "could not get %s %i properties: %s\n", \ 687 #type, obj->type->type##_id, \ 688 strerror(errno)); \ 689 continue; \ 690 } \ 691 obj->props_info = calloc(obj->props->count_props, \ 692 sizeof(*obj->props_info)); \ 693 if (!obj->props_info) \ 694 continue; \ 695 for (j = 0; j < obj->props->count_props; ++j) \ 696 obj->props_info[j] = \ 697 drmModeGetProperty(dev->fd, obj->props->props[j]); \ 698 } \ 699 } while (0) 700 701 get_properties(res, crtc, CRTC); 702 get_properties(res, connector, CONNECTOR); 703 704 for (i = 0; i < res->count_crtcs; ++i) 705 res->crtcs[i].mode = &res->crtcs[i].crtc->mode; 706 707 plane_res = drmModeGetPlaneResources(dev->fd); 708 if (!plane_res) { 709 fprintf(stderr, "drmModeGetPlaneResources failed: %s\n", 710 strerror(errno)); 711 return res; 712 } 713 714 res->count_planes = plane_res->count_planes; 715 716 res->planes = calloc(res->count_planes, sizeof(*res->planes)); 717 if (!res->planes) { 718 drmModeFreePlaneResources(plane_res); 719 goto error; 720 } 721 722 get_resource(res, plane_res, plane, Plane); 723 drmModeFreePlaneResources(plane_res); 724 get_properties(res, plane, PLANE); 725 726 return res; 727 728 error: 729 free_resources(res); 730 return NULL; 731 } 732 733 static struct crtc *get_crtc_by_id(struct device *dev, uint32_t id) 734 { 735 int i; 736 737 for (i = 0; i < dev->resources->count_crtcs; ++i) { 738 drmModeCrtc *crtc = dev->resources->crtcs[i].crtc; 739 if (crtc && crtc->crtc_id == id) 740 return &dev->resources->crtcs[i]; 741 } 742 743 return NULL; 744 } 745 746 static uint32_t get_crtc_mask(struct device *dev, struct crtc *crtc) 747 { 748 unsigned int i; 749 750 for (i = 0; i < (unsigned int)dev->resources->count_crtcs; i++) { 751 if (crtc->crtc->crtc_id == dev->resources->crtcs[i].crtc->crtc_id) 752 return 1 << i; 753 } 754 /* Unreachable: crtc->crtc is one of resources->crtcs[] */ 755 /* Don't return zero or static analysers will complain */ 756 abort(); 757 return 0; 758 } 759 760 static drmModeConnector *get_connector_by_name(struct device *dev, const char *name) 761 { 762 struct connector *connector; 763 int i; 764 765 for (i = 0; i < dev->resources->count_connectors; i++) { 766 connector = &dev->resources->connectors[i]; 767 768 if (strcmp(connector->name, name) == 0) 769 return connector->connector; 770 } 771 772 return NULL; 773 } 774 775 static drmModeConnector *get_connector_by_id(struct device *dev, uint32_t id) 776 { 777 drmModeConnector *connector; 778 int i; 779 780 for (i = 0; i < dev->resources->count_connectors; i++) { 781 connector = dev->resources->connectors[i].connector; 782 if (connector && connector->connector_id == id) 783 return connector; 784 } 785 786 return NULL; 787 } 788 789 static drmModeEncoder *get_encoder_by_id(struct device *dev, uint32_t id) 790 { 791 drmModeEncoder *encoder; 792 int i; 793 794 for (i = 0; i < dev->resources->count_encoders; i++) { 795 encoder = dev->resources->encoders[i].encoder; 796 if (encoder && encoder->encoder_id == id) 797 return encoder; 798 } 799 800 return NULL; 801 } 802 803 /* ----------------------------------------------------------------------------- 804 * Pipes and planes 805 */ 806 807 /* 808 * Mode setting with the kernel interfaces is a bit of a chore. 809 * First you have to find the connector in question and make sure the 810 * requested mode is available. 811 * Then you need to find the encoder attached to that connector so you 812 * can bind it with a free crtc. 813 */ 814 struct pipe_arg { 815 const char **cons; 816 uint32_t *con_ids; 817 unsigned int num_cons; 818 uint32_t crtc_id; 819 char mode_str[64]; 820 char format_str[8]; /* need to leave room for "_BE" and terminating \0 */ 821 float vrefresh; 822 unsigned int fourcc; 823 drmModeModeInfo *mode; 824 struct crtc *crtc; 825 unsigned int fb_id[2], current_fb_id; 826 struct timeval start; 827 unsigned int out_fb_id; 828 struct bo *out_bo; 829 830 int swap_count; 831 }; 832 833 struct plane_arg { 834 uint32_t plane_id; /* the id of plane to use */ 835 uint32_t crtc_id; /* the id of CRTC to bind to */ 836 bool has_position; 837 int32_t x, y; 838 uint32_t w, h; 839 double scale; 840 unsigned int fb_id; 841 unsigned int old_fb_id; 842 struct bo *bo; 843 struct bo *old_bo; 844 char format_str[8]; /* need to leave room for "_BE" and terminating \0 */ 845 unsigned int fourcc; 846 }; 847 848 static drmModeModeInfo * 849 connector_find_mode(struct device *dev, uint32_t con_id, const char *mode_str, 850 const float vrefresh) 851 { 852 drmModeConnector *connector; 853 drmModeModeInfo *mode; 854 int i; 855 856 connector = get_connector_by_id(dev, con_id); 857 if (!connector) 858 return NULL; 859 860 if (strchr(mode_str, ',')) { 861 i = sscanf(mode_str, "%hu,%hu,%hu,%hu,%hu,%hu,%hu,%hu", 862 &user_mode.hdisplay, &user_mode.hsync_start, 863 &user_mode.hsync_end, &user_mode.htotal, 864 &user_mode.vdisplay, &user_mode.vsync_start, 865 &user_mode.vsync_end, &user_mode.vtotal); 866 if (i == 8) { 867 user_mode.clock = roundf(user_mode.htotal * user_mode.vtotal * vrefresh / 1000); 868 user_mode.vrefresh = roundf(vrefresh); 869 snprintf(user_mode.name, sizeof(user_mode.name), "custom%dx%d", user_mode.hdisplay, user_mode.vdisplay); 870 871 return &user_mode; 872 } 873 } 874 875 if (!connector->count_modes) 876 return NULL; 877 878 /* Pick by Index */ 879 if (mode_str[0] == '#') { 880 int index = atoi(mode_str + 1); 881 882 if (index >= connector->count_modes || index < 0) 883 return NULL; 884 return &connector->modes[index]; 885 } 886 887 /* Pick by Name */ 888 for (i = 0; i < connector->count_modes; i++) { 889 mode = &connector->modes[i]; 890 if (!strcmp(mode->name, mode_str)) { 891 /* If the vertical refresh frequency is not specified 892 * then return the first mode that match with the name. 893 * Else, return the mode that match the name and 894 * the specified vertical refresh frequency. 895 */ 896 if (vrefresh == 0) 897 return mode; 898 else if (fabs(mode_vrefresh(mode) - vrefresh) < 0.005) 899 return mode; 900 } 901 } 902 903 return NULL; 904 } 905 906 static struct crtc *pipe_find_crtc(struct device *dev, struct pipe_arg *pipe) 907 { 908 uint32_t possible_crtcs = ~0; 909 uint32_t active_crtcs = 0; 910 unsigned int crtc_idx; 911 unsigned int i; 912 int j; 913 914 for (i = 0; i < pipe->num_cons; ++i) { 915 uint32_t crtcs_for_connector = 0; 916 drmModeConnector *connector; 917 drmModeEncoder *encoder; 918 struct crtc *crtc; 919 920 connector = get_connector_by_id(dev, pipe->con_ids[i]); 921 if (!connector) 922 return NULL; 923 924 for (j = 0; j < connector->count_encoders; ++j) { 925 encoder = get_encoder_by_id(dev, connector->encoders[j]); 926 if (!encoder) 927 continue; 928 929 crtcs_for_connector |= encoder->possible_crtcs; 930 crtc = get_crtc_by_id(dev, encoder->crtc_id); 931 if (!crtc) 932 continue; 933 active_crtcs |= get_crtc_mask(dev, crtc); 934 } 935 936 possible_crtcs &= crtcs_for_connector; 937 } 938 939 if (!possible_crtcs) 940 return NULL; 941 942 /* Return the first possible and active CRTC if one exists, or the first 943 * possible CRTC otherwise. 944 */ 945 if (possible_crtcs & active_crtcs) 946 crtc_idx = ffs(possible_crtcs & active_crtcs); 947 else 948 crtc_idx = ffs(possible_crtcs); 949 950 return &dev->resources->crtcs[crtc_idx - 1]; 951 } 952 953 static int pipe_find_crtc_and_mode(struct device *dev, struct pipe_arg *pipe) 954 { 955 drmModeModeInfo *mode = NULL; 956 int i; 957 958 pipe->mode = NULL; 959 960 for (i = 0; i < (int)pipe->num_cons; i++) { 961 mode = connector_find_mode(dev, pipe->con_ids[i], 962 pipe->mode_str, pipe->vrefresh); 963 if (mode == NULL) { 964 if (pipe->vrefresh) 965 fprintf(stderr, 966 "failed to find mode " 967 "\"%s-%.2fHz\" for connector %s\n", 968 pipe->mode_str, pipe->vrefresh, pipe->cons[i]); 969 else 970 fprintf(stderr, 971 "failed to find mode \"%s\" for connector %s\n", 972 pipe->mode_str, pipe->cons[i]); 973 return -EINVAL; 974 } 975 } 976 977 /* If the CRTC ID was specified, get the corresponding CRTC. Otherwise 978 * locate a CRTC that can be attached to all the connectors. 979 */ 980 if (pipe->crtc_id != (uint32_t)-1) { 981 pipe->crtc = get_crtc_by_id(dev, pipe->crtc_id); 982 } else { 983 pipe->crtc = pipe_find_crtc(dev, pipe); 984 pipe->crtc_id = pipe->crtc->crtc->crtc_id; 985 } 986 987 if (!pipe->crtc) { 988 fprintf(stderr, "failed to find CRTC for pipe\n"); 989 return -EINVAL; 990 } 991 992 pipe->mode = mode; 993 pipe->crtc->mode = mode; 994 995 return 0; 996 } 997 998 /* ----------------------------------------------------------------------------- 999 * Properties 1000 */ 1001 1002 struct property_arg { 1003 uint32_t obj_id; 1004 uint32_t obj_type; 1005 char name[DRM_PROP_NAME_LEN+1]; 1006 uint32_t prop_id; 1007 uint64_t value; 1008 bool optional; 1009 }; 1010 1011 static bool set_property(struct device *dev, struct property_arg *p) 1012 { 1013 drmModeObjectProperties *props = NULL; 1014 drmModePropertyRes **props_info = NULL; 1015 const char *obj_type; 1016 int ret; 1017 int i; 1018 1019 p->obj_type = 0; 1020 p->prop_id = 0; 1021 1022 #define find_object(_res, type, Type) \ 1023 do { \ 1024 for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \ 1025 struct type *obj = &(_res)->type##s[i]; \ 1026 if (obj->type->type##_id != p->obj_id) \ 1027 continue; \ 1028 p->obj_type = DRM_MODE_OBJECT_##Type; \ 1029 obj_type = #Type; \ 1030 props = obj->props; \ 1031 props_info = obj->props_info; \ 1032 } \ 1033 } while(0) \ 1034 1035 find_object(dev->resources, crtc, CRTC); 1036 if (p->obj_type == 0) 1037 find_object(dev->resources, connector, CONNECTOR); 1038 if (p->obj_type == 0) 1039 find_object(dev->resources, plane, PLANE); 1040 if (p->obj_type == 0) { 1041 fprintf(stderr, "Object %i not found, can't set property\n", 1042 p->obj_id); 1043 return false; 1044 } 1045 1046 if (!props) { 1047 fprintf(stderr, "%s %i has no properties\n", 1048 obj_type, p->obj_id); 1049 return false; 1050 } 1051 1052 for (i = 0; i < (int)props->count_props; ++i) { 1053 if (!props_info[i]) 1054 continue; 1055 if (strcmp(props_info[i]->name, p->name) == 0) 1056 break; 1057 } 1058 1059 if (i == (int)props->count_props) { 1060 if (!p->optional) 1061 fprintf(stderr, "%s %i has no %s property\n", 1062 obj_type, p->obj_id, p->name); 1063 return false; 1064 } 1065 1066 p->prop_id = props->props[i]; 1067 1068 if (!dev->use_atomic) 1069 ret = drmModeObjectSetProperty(dev->fd, p->obj_id, p->obj_type, 1070 p->prop_id, p->value); 1071 else 1072 ret = drmModeAtomicAddProperty(dev->req, p->obj_id, p->prop_id, p->value); 1073 1074 if (ret < 0) 1075 fprintf(stderr, "failed to set %s %i property %s to %" PRIu64 ": %s\n", 1076 obj_type, p->obj_id, p->name, p->value, strerror(-ret)); 1077 1078 return true; 1079 } 1080 1081 /* -------------------------------------------------------------------------- */ 1082 1083 static void 1084 page_flip_handler(int fd, unsigned int frame, 1085 unsigned int sec, unsigned int usec, void *data) 1086 { 1087 struct pipe_arg *pipe; 1088 unsigned int new_fb_id; 1089 struct timeval end; 1090 double t; 1091 1092 pipe = data; 1093 if (pipe->current_fb_id == pipe->fb_id[0]) 1094 new_fb_id = pipe->fb_id[1]; 1095 else 1096 new_fb_id = pipe->fb_id[0]; 1097 1098 drmModePageFlip(fd, pipe->crtc_id, new_fb_id, 1099 DRM_MODE_PAGE_FLIP_EVENT, pipe); 1100 pipe->current_fb_id = new_fb_id; 1101 pipe->swap_count++; 1102 if (pipe->swap_count == 60) { 1103 gettimeofday(&end, NULL); 1104 t = end.tv_sec + end.tv_usec * 1e-6 - 1105 (pipe->start.tv_sec + pipe->start.tv_usec * 1e-6); 1106 fprintf(stderr, "freq: %.02fHz\n", pipe->swap_count / t); 1107 pipe->swap_count = 0; 1108 pipe->start = end; 1109 } 1110 } 1111 1112 static bool format_support(const drmModePlanePtr ovr, uint32_t fmt) 1113 { 1114 unsigned int i; 1115 1116 for (i = 0; i < ovr->count_formats; ++i) { 1117 if (ovr->formats[i] == fmt) 1118 return true; 1119 } 1120 1121 return false; 1122 } 1123 1124 static void add_property(struct device *dev, uint32_t obj_id, 1125 const char *name, uint64_t value) 1126 { 1127 struct property_arg p; 1128 1129 p.obj_id = obj_id; 1130 strcpy(p.name, name); 1131 p.value = value; 1132 1133 set_property(dev, &p); 1134 } 1135 1136 static bool add_property_optional(struct device *dev, uint32_t obj_id, 1137 const char *name, uint64_t value) 1138 { 1139 struct property_arg p; 1140 1141 p.obj_id = obj_id; 1142 strcpy(p.name, name); 1143 p.value = value; 1144 p.optional = true; 1145 1146 return set_property(dev, &p); 1147 } 1148 1149 static void set_gamma(struct device *dev, unsigned crtc_id, unsigned fourcc) 1150 { 1151 unsigned blob_id = 0; 1152 const struct util_format_info *info; 1153 /* TODO: support 1024-sized LUTs, when the use-case arises */ 1154 struct drm_color_lut gamma_lut[256]; 1155 int i, ret; 1156 1157 info = util_format_info_find(fourcc); 1158 if (info->ncolors) { 1159 memset(gamma_lut, 0, sizeof(gamma_lut)); 1160 /* TODO: Add index support for more patterns */ 1161 util_smpte_fill_lut(info->ncolors, gamma_lut); 1162 drmModeCreatePropertyBlob(dev->fd, gamma_lut, sizeof(gamma_lut), &blob_id); 1163 } else { 1164 /* 1165 * Initialize gamma_lut to a linear table for the legacy API below. 1166 * The modern property API resets to a linear/pass-thru table if blob_id 1167 * is 0, hence no PropertyBlob is created here. 1168 */ 1169 for (i = 0; i < 256; i++) { 1170 gamma_lut[i].red = 1171 gamma_lut[i].green = 1172 gamma_lut[i].blue = i << 8; 1173 } 1174 } 1175 1176 add_property_optional(dev, crtc_id, "DEGAMMA_LUT", 0); 1177 add_property_optional(dev, crtc_id, "CTM", 0); 1178 if (!add_property_optional(dev, crtc_id, "GAMMA_LUT", blob_id)) { 1179 /* If we can't add the GAMMA_LUT property, try the legacy API. */ 1180 uint16_t r[256], g[256], b[256]; 1181 1182 for (i = 0; i < 256; i++) { 1183 r[i] = gamma_lut[i].red; 1184 g[i] = gamma_lut[i].green; 1185 b[i] = gamma_lut[i].blue; 1186 } 1187 1188 ret = drmModeCrtcSetGamma(dev->fd, crtc_id, 256, r, g, b); 1189 if (ret && errno != ENOSYS) 1190 fprintf(stderr, "failed to set gamma: %s\n", strerror(errno)); 1191 } 1192 } 1193 1194 static int 1195 bo_fb_create(int fd, unsigned int fourcc, const uint32_t w, const uint32_t h, 1196 enum util_fill_pattern pat, struct bo **out_bo, unsigned int *out_fb_id) 1197 { 1198 uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0}; 1199 struct bo *bo; 1200 unsigned int fb_id; 1201 1202 bo = bo_create(fd, fourcc, w, h, handles, pitches, offsets, pat); 1203 1204 if (bo == NULL) 1205 return -1; 1206 1207 if (drmModeAddFB2(fd, w, h, fourcc, handles, pitches, offsets, &fb_id, 0)) { 1208 fprintf(stderr, "failed to add fb (%ux%u): %s\n", w, h, strerror(errno)); 1209 bo_destroy(bo); 1210 return -1; 1211 } 1212 *out_bo = bo; 1213 *out_fb_id = fb_id; 1214 return 0; 1215 } 1216 1217 static int atomic_set_plane(struct device *dev, struct plane_arg *p, 1218 int pattern, bool update) 1219 { 1220 struct bo *plane_bo; 1221 int crtc_x, crtc_y, crtc_w, crtc_h; 1222 struct crtc *crtc = NULL; 1223 unsigned int old_fb_id; 1224 1225 /* Find an unused plane which can be connected to our CRTC. Find the 1226 * CRTC index first, then iterate over available planes. 1227 */ 1228 crtc = get_crtc_by_id(dev, p->crtc_id); 1229 if (!crtc) { 1230 fprintf(stderr, "CRTC %u not found\n", p->crtc_id); 1231 return -1; 1232 } 1233 1234 if (!update) 1235 fprintf(stderr, "testing %dx%d@%s on plane %u, crtc %u\n", 1236 p->w, p->h, p->format_str, p->plane_id, p->crtc_id); 1237 1238 plane_bo = p->old_bo; 1239 p->old_bo = p->bo; 1240 1241 if (!plane_bo) { 1242 if (bo_fb_create(dev->fd, p->fourcc, p->w, p->h, 1243 pattern, &plane_bo, &p->fb_id)) 1244 return -1; 1245 } 1246 1247 p->bo = plane_bo; 1248 1249 old_fb_id = p->fb_id; 1250 p->old_fb_id = old_fb_id; 1251 1252 crtc_w = p->w * p->scale; 1253 crtc_h = p->h * p->scale; 1254 if (!p->has_position) { 1255 /* Default to the middle of the screen */ 1256 crtc_x = (crtc->mode->hdisplay - crtc_w) / 2; 1257 crtc_y = (crtc->mode->vdisplay - crtc_h) / 2; 1258 } else { 1259 crtc_x = p->x; 1260 crtc_y = p->y; 1261 } 1262 1263 add_property(dev, p->plane_id, "FB_ID", p->fb_id); 1264 add_property(dev, p->plane_id, "CRTC_ID", p->crtc_id); 1265 add_property(dev, p->plane_id, "SRC_X", 0); 1266 add_property(dev, p->plane_id, "SRC_Y", 0); 1267 add_property(dev, p->plane_id, "SRC_W", p->w << 16); 1268 add_property(dev, p->plane_id, "SRC_H", p->h << 16); 1269 add_property(dev, p->plane_id, "CRTC_X", crtc_x); 1270 add_property(dev, p->plane_id, "CRTC_Y", crtc_y); 1271 add_property(dev, p->plane_id, "CRTC_W", crtc_w); 1272 add_property(dev, p->plane_id, "CRTC_H", crtc_h); 1273 1274 return 0; 1275 } 1276 1277 static int set_plane(struct device *dev, struct plane_arg *p) 1278 { 1279 drmModePlane *ovr; 1280 uint32_t plane_id; 1281 int crtc_x, crtc_y, crtc_w, crtc_h; 1282 struct crtc *crtc = NULL; 1283 unsigned int i, crtc_mask; 1284 1285 /* Find an unused plane which can be connected to our CRTC. Find the 1286 * CRTC index first, then iterate over available planes. 1287 */ 1288 crtc = get_crtc_by_id(dev, p->crtc_id); 1289 if (!crtc) { 1290 fprintf(stderr, "CRTC %u not found\n", p->crtc_id); 1291 return -1; 1292 } 1293 crtc_mask = get_crtc_mask(dev, crtc); 1294 plane_id = p->plane_id; 1295 1296 for (i = 0; i < dev->resources->count_planes; i++) { 1297 ovr = dev->resources->planes[i].plane; 1298 if (!ovr) 1299 continue; 1300 1301 if (plane_id && plane_id != ovr->plane_id) 1302 continue; 1303 1304 if (!format_support(ovr, p->fourcc)) 1305 continue; 1306 1307 if ((ovr->possible_crtcs & crtc_mask) && 1308 (ovr->crtc_id == 0 || ovr->crtc_id == p->crtc_id)) { 1309 plane_id = ovr->plane_id; 1310 break; 1311 } 1312 } 1313 1314 if (i == dev->resources->count_planes) { 1315 fprintf(stderr, "no unused plane available for CRTC %u\n", 1316 p->crtc_id); 1317 return -1; 1318 } 1319 1320 fprintf(stderr, "testing %dx%d@%s overlay plane %u\n", 1321 p->w, p->h, p->format_str, plane_id); 1322 1323 /* just use single plane format for now.. */ 1324 if (bo_fb_create(dev->fd, p->fourcc, p->w, p->h, 1325 secondary_fill, &p->bo, &p->fb_id)) 1326 return -1; 1327 1328 crtc_w = p->w * p->scale; 1329 crtc_h = p->h * p->scale; 1330 if (!p->has_position) { 1331 /* Default to the middle of the screen */ 1332 crtc_x = (crtc->mode->hdisplay - crtc_w) / 2; 1333 crtc_y = (crtc->mode->vdisplay - crtc_h) / 2; 1334 } else { 1335 crtc_x = p->x; 1336 crtc_y = p->y; 1337 } 1338 1339 /* note src coords (last 4 args) are in Q16 format */ 1340 if (drmModeSetPlane(dev->fd, plane_id, p->crtc_id, p->fb_id, 1341 0, crtc_x, crtc_y, crtc_w, crtc_h, 1342 0, 0, p->w << 16, p->h << 16)) { 1343 fprintf(stderr, "failed to enable plane: %s\n", 1344 strerror(errno)); 1345 return -1; 1346 } 1347 1348 ovr->crtc_id = p->crtc_id; 1349 1350 return 0; 1351 } 1352 1353 static void atomic_set_planes(struct device *dev, struct plane_arg *p, 1354 unsigned int count, bool update) 1355 { 1356 unsigned int i, pattern = primary_fill; 1357 1358 /* set up planes */ 1359 for (i = 0; i < count; i++) { 1360 if (i > 0) 1361 pattern = secondary_fill; 1362 else 1363 set_gamma(dev, p[i].crtc_id, p[i].fourcc); 1364 1365 if (atomic_set_plane(dev, &p[i], pattern, update)) 1366 return; 1367 } 1368 } 1369 1370 static void 1371 atomic_test_page_flip(struct device *dev, struct pipe_arg *pipe_args, 1372 struct plane_arg *plane_args, unsigned int plane_count) 1373 { 1374 int ret; 1375 1376 gettimeofday(&pipe_args->start, NULL); 1377 pipe_args->swap_count = 0; 1378 1379 while (true) { 1380 drmModeAtomicFree(dev->req); 1381 dev->req = drmModeAtomicAlloc(); 1382 atomic_set_planes(dev, plane_args, plane_count, true); 1383 1384 ret = drmModeAtomicCommit(dev->fd, dev->req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); 1385 if (ret) { 1386 fprintf(stderr, "Atomic Commit failed [2]\n"); 1387 return; 1388 } 1389 1390 pipe_args->swap_count++; 1391 if (pipe_args->swap_count == 60) { 1392 struct timeval end; 1393 double t; 1394 1395 gettimeofday(&end, NULL); 1396 t = end.tv_sec + end.tv_usec * 1e-6 - 1397 (pipe_args->start.tv_sec + pipe_args->start.tv_usec * 1e-6); 1398 fprintf(stderr, "freq: %.02fHz\n", pipe_args->swap_count / t); 1399 pipe_args->swap_count = 0; 1400 pipe_args->start = end; 1401 } 1402 } 1403 } 1404 1405 static void atomic_clear_planes(struct device *dev, struct plane_arg *p, unsigned int count) 1406 { 1407 unsigned int i; 1408 1409 for (i = 0; i < count; i++) { 1410 add_property(dev, p[i].plane_id, "FB_ID", 0); 1411 add_property(dev, p[i].plane_id, "CRTC_ID", 0); 1412 add_property(dev, p[i].plane_id, "SRC_X", 0); 1413 add_property(dev, p[i].plane_id, "SRC_Y", 0); 1414 add_property(dev, p[i].plane_id, "SRC_W", 0); 1415 add_property(dev, p[i].plane_id, "SRC_H", 0); 1416 add_property(dev, p[i].plane_id, "CRTC_X", 0); 1417 add_property(dev, p[i].plane_id, "CRTC_Y", 0); 1418 add_property(dev, p[i].plane_id, "CRTC_W", 0); 1419 add_property(dev, p[i].plane_id, "CRTC_H", 0); 1420 } 1421 } 1422 1423 static void atomic_clear_FB(struct device *dev, struct plane_arg *p, unsigned int count) 1424 { 1425 unsigned int i; 1426 1427 for (i = 0; i < count; i++) { 1428 if (p[i].fb_id) { 1429 drmModeRmFB(dev->fd, p[i].fb_id); 1430 p[i].fb_id = 0; 1431 } 1432 if (p[i].old_fb_id) { 1433 drmModeRmFB(dev->fd, p[i].old_fb_id); 1434 p[i].old_fb_id = 0; 1435 } 1436 if (p[i].bo) { 1437 bo_destroy(p[i].bo); 1438 p[i].bo = NULL; 1439 } 1440 if (p[i].old_bo) { 1441 bo_destroy(p[i].old_bo); 1442 p[i].old_bo = NULL; 1443 } 1444 1445 } 1446 } 1447 1448 static void clear_planes(struct device *dev, struct plane_arg *p, unsigned int count) 1449 { 1450 unsigned int i; 1451 1452 for (i = 0; i < count; i++) { 1453 if (p[i].fb_id) 1454 drmModeRmFB(dev->fd, p[i].fb_id); 1455 if (p[i].bo) 1456 bo_destroy(p[i].bo); 1457 } 1458 } 1459 1460 static int pipe_resolve_connectors(struct device *dev, struct pipe_arg *pipe) 1461 { 1462 drmModeConnector *connector; 1463 unsigned int i; 1464 uint32_t id; 1465 char *endp; 1466 1467 for (i = 0; i < pipe->num_cons; i++) { 1468 id = strtoul(pipe->cons[i], &endp, 10); 1469 if (endp == pipe->cons[i]) { 1470 connector = get_connector_by_name(dev, pipe->cons[i]); 1471 if (!connector) { 1472 fprintf(stderr, "no connector named '%s'\n", 1473 pipe->cons[i]); 1474 return -ENODEV; 1475 } 1476 1477 id = connector->connector_id; 1478 } 1479 1480 pipe->con_ids[i] = id; 1481 } 1482 1483 return 0; 1484 } 1485 1486 static bool pipe_has_writeback_connector(struct device *dev, struct pipe_arg *pipes, 1487 unsigned int count) 1488 { 1489 drmModeConnector *connector; 1490 unsigned int i, j; 1491 1492 for (j = 0; j < count; j++) { 1493 struct pipe_arg *pipe = &pipes[j]; 1494 1495 for (i = 0; i < pipe->num_cons; i++) { 1496 connector = get_connector_by_id(dev, pipe->con_ids[i]); 1497 if (connector && connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) 1498 return true; 1499 } 1500 } 1501 return false; 1502 } 1503 1504 static int pipe_attempt_connector(struct device *dev, drmModeConnector *con, 1505 struct pipe_arg *pipe) 1506 { 1507 char *con_str; 1508 int i; 1509 1510 con_str = calloc(8, sizeof(char)); 1511 if (!con_str) 1512 return -1; 1513 1514 sprintf(con_str, "%d", con->connector_id); 1515 strcpy(pipe->format_str, "XR24"); 1516 pipe->fourcc = util_format_fourcc(pipe->format_str); 1517 pipe->num_cons = 1; 1518 pipe->con_ids = calloc(1, sizeof(*pipe->con_ids)); 1519 pipe->cons = calloc(1, sizeof(*pipe->cons)); 1520 1521 if (!pipe->con_ids || !pipe->cons) 1522 goto free_con_str; 1523 1524 pipe->con_ids[0] = con->connector_id; 1525 pipe->cons[0] = (const char*)con_str; 1526 1527 pipe->crtc = pipe_find_crtc(dev, pipe); 1528 if (!pipe->crtc) 1529 goto free_all; 1530 1531 pipe->crtc_id = pipe->crtc->crtc->crtc_id; 1532 1533 /* Return the first mode if no preferred. */ 1534 pipe->mode = &con->modes[0]; 1535 1536 for (i = 0; i < con->count_modes; i++) { 1537 drmModeModeInfo *current_mode = &con->modes[i]; 1538 1539 if (current_mode->type & DRM_MODE_TYPE_PREFERRED) { 1540 pipe->mode = current_mode; 1541 break; 1542 } 1543 } 1544 1545 sprintf(pipe->mode_str, "%dx%d", pipe->mode->hdisplay, pipe->mode->vdisplay); 1546 1547 return 0; 1548 1549 free_all: 1550 free(pipe->cons); 1551 free(pipe->con_ids); 1552 free_con_str: 1553 free(con_str); 1554 return -1; 1555 } 1556 1557 static int pipe_find_preferred(struct device *dev, struct pipe_arg **out_pipes) 1558 { 1559 struct pipe_arg *pipes; 1560 struct resources *res = dev->resources; 1561 drmModeConnector *con = NULL; 1562 int i, connected = 0, attempted = 0; 1563 1564 for (i = 0; i < res->count_connectors; i++) { 1565 con = res->connectors[i].connector; 1566 if (!con || con->connection != DRM_MODE_CONNECTED || 1567 con->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) 1568 continue; 1569 connected++; 1570 } 1571 if (!connected) { 1572 printf("no connected connector!\n"); 1573 return 0; 1574 } 1575 1576 pipes = calloc(connected, sizeof(struct pipe_arg)); 1577 if (!pipes) 1578 return 0; 1579 1580 for (i = 0; i < res->count_connectors && attempted < connected; i++) { 1581 con = res->connectors[i].connector; 1582 if (!con || con->connection != DRM_MODE_CONNECTED) 1583 continue; 1584 1585 if (pipe_attempt_connector(dev, con, &pipes[attempted]) < 0) { 1586 printf("failed fetching preferred mode for connector\n"); 1587 continue; 1588 } 1589 attempted++; 1590 } 1591 1592 *out_pipes = pipes; 1593 return attempted; 1594 } 1595 1596 static struct plane *get_primary_plane_by_crtc(struct device *dev, struct crtc *crtc) 1597 { 1598 unsigned int i; 1599 1600 for (i = 0; i < dev->resources->count_planes; i++) { 1601 struct plane *plane = &dev->resources->planes[i]; 1602 drmModePlane *ovr = plane->plane; 1603 if (!ovr) 1604 continue; 1605 1606 // XXX: add is_primary_plane and (?) format checks 1607 1608 if (ovr->possible_crtcs & get_crtc_mask(dev, crtc)) 1609 return plane; 1610 } 1611 return NULL; 1612 } 1613 1614 static unsigned int set_mode(struct device *dev, struct pipe_arg **pipe_args, unsigned int count) 1615 { 1616 unsigned int i, j; 1617 int ret, x = 0; 1618 int preferred = count == 0; 1619 struct pipe_arg *pipes; 1620 1621 if (preferred) { 1622 count = pipe_find_preferred(dev, pipe_args); 1623 if (!count) { 1624 fprintf(stderr, "can't find any preferred connector/mode.\n"); 1625 return 0; 1626 } 1627 1628 pipes = *pipe_args; 1629 } else { 1630 pipes = *pipe_args; 1631 1632 for (i = 0; i < count; i++) { 1633 struct pipe_arg *pipe = &pipes[i]; 1634 1635 ret = pipe_resolve_connectors(dev, pipe); 1636 if (ret < 0) 1637 return 0; 1638 1639 ret = pipe_find_crtc_and_mode(dev, pipe); 1640 if (ret < 0) 1641 continue; 1642 } 1643 } 1644 1645 if (!dev->use_atomic) { 1646 for (i = 0; i < count; i++) { 1647 struct pipe_arg *pipe = &pipes[i]; 1648 1649 if (pipe->mode == NULL) 1650 continue; 1651 1652 if (!preferred) { 1653 dev->mode.width += pipe->mode->hdisplay; 1654 if (dev->mode.height < pipe->mode->vdisplay) 1655 dev->mode.height = pipe->mode->vdisplay; 1656 } else { 1657 /* XXX: Use a clone mode, more like atomic. We could do per 1658 * connector bo/fb, so we don't have the stretched image. 1659 */ 1660 if (dev->mode.width < pipe->mode->hdisplay) 1661 dev->mode.width = pipe->mode->hdisplay; 1662 if (dev->mode.height < pipe->mode->vdisplay) 1663 dev->mode.height = pipe->mode->vdisplay; 1664 } 1665 } 1666 1667 if (bo_fb_create(dev->fd, pipes[0].fourcc, dev->mode.width, dev->mode.height, 1668 primary_fill, &dev->mode.bo, &dev->mode.fb_id)) 1669 return 0; 1670 } 1671 1672 for (i = 0; i < count; i++) { 1673 struct pipe_arg *pipe = &pipes[i]; 1674 uint32_t blob_id; 1675 1676 if (pipe->mode == NULL) 1677 continue; 1678 1679 printf("setting mode %s-%.2fHz on connectors ", 1680 pipe->mode->name, mode_vrefresh(pipe->mode)); 1681 for (j = 0; j < pipe->num_cons; ++j) { 1682 printf("%s, ", pipe->cons[j]); 1683 if (dev->use_atomic) 1684 add_property(dev, pipe->con_ids[j], "CRTC_ID", pipe->crtc_id); 1685 } 1686 printf("crtc %d\n", pipe->crtc_id); 1687 1688 if (!dev->use_atomic) { 1689 ret = drmModeSetCrtc(dev->fd, pipe->crtc_id, dev->mode.fb_id, 1690 x, 0, pipe->con_ids, pipe->num_cons, 1691 pipe->mode); 1692 1693 /* XXX: Actually check if this is needed */ 1694 drmModeDirtyFB(dev->fd, dev->mode.fb_id, NULL, 0); 1695 1696 if (!preferred) 1697 x += pipe->mode->hdisplay; 1698 1699 if (ret) { 1700 fprintf(stderr, "failed to set mode: %s\n", strerror(errno)); 1701 return 0; 1702 } 1703 1704 set_gamma(dev, pipe->crtc_id, pipe->fourcc); 1705 } else { 1706 drmModeCreatePropertyBlob(dev->fd, pipe->mode, sizeof(*pipe->mode), &blob_id); 1707 add_property(dev, pipe->crtc_id, "MODE_ID", blob_id); 1708 add_property(dev, pipe->crtc_id, "ACTIVE", 1); 1709 1710 /* By default atomic modeset does not set a primary plane, shrug */ 1711 if (preferred) { 1712 struct plane *plane = get_primary_plane_by_crtc(dev, pipe->crtc); 1713 struct plane_arg plane_args = { 1714 .plane_id = plane->plane->plane_id, 1715 .crtc_id = pipe->crtc_id, 1716 .w = pipe->mode->hdisplay, 1717 .h = pipe->mode->vdisplay, 1718 .scale = 1.0, 1719 .format_str = "XR24", 1720 .fourcc = util_format_fourcc(pipe->format_str), 1721 }; 1722 1723 atomic_set_planes(dev, &plane_args, 1, false); 1724 } 1725 } 1726 } 1727 1728 return count; 1729 } 1730 1731 static void writeback_config(struct device *dev, struct pipe_arg *pipes, unsigned int count) 1732 { 1733 drmModeConnector *connector; 1734 unsigned int i, j; 1735 1736 for (j = 0; j < count; j++) { 1737 struct pipe_arg *pipe = &pipes[j]; 1738 1739 for (i = 0; i < pipe->num_cons; i++) { 1740 connector = get_connector_by_id(dev, pipe->con_ids[i]); 1741 if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) { 1742 if (!pipe->mode) { 1743 fprintf(stderr, "no mode for writeback\n"); 1744 return; 1745 } 1746 bo_fb_create(dev->fd, pipes[j].fourcc, 1747 pipe->mode->hdisplay, pipe->mode->vdisplay, 1748 UTIL_PATTERN_PLAIN, 1749 &pipe->out_bo, &pipe->out_fb_id); 1750 add_property(dev, pipe->con_ids[i], "WRITEBACK_FB_ID", 1751 pipe->out_fb_id); 1752 add_property(dev, pipe->con_ids[i], "WRITEBACK_OUT_FENCE_PTR", 1753 (uintptr_t)(&dev->writeback_fence_fd)); 1754 } 1755 } 1756 } 1757 } 1758 1759 static int poll_writeback_fence(int fd, int timeout) 1760 { 1761 struct pollfd fds = { fd, POLLIN }; 1762 int ret; 1763 1764 do { 1765 ret = poll(&fds, 1, timeout); 1766 if (ret > 0) { 1767 if (fds.revents & (POLLERR | POLLNVAL)) 1768 return -EINVAL; 1769 1770 return 0; 1771 } else if (ret == 0) { 1772 return -ETIMEDOUT; 1773 } else { 1774 ret = -errno; 1775 if (ret == -EINTR || ret == -EAGAIN) 1776 continue; 1777 return ret; 1778 } 1779 } while (1); 1780 1781 } 1782 1783 static void dump_output_fb(struct device *dev, struct pipe_arg *pipes, char *dump_path, 1784 unsigned int count) 1785 { 1786 drmModeConnector *connector; 1787 unsigned int i, j; 1788 1789 for (j = 0; j < count; j++) { 1790 struct pipe_arg *pipe = &pipes[j]; 1791 1792 for (i = 0; i < pipe->num_cons; i++) { 1793 connector = get_connector_by_id(dev, pipe->con_ids[i]); 1794 if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) 1795 bo_dump(pipe->out_bo, dump_path); 1796 } 1797 } 1798 } 1799 1800 static void atomic_clear_mode(struct device *dev, struct pipe_arg *pipes, unsigned int count) 1801 { 1802 unsigned int i; 1803 unsigned int j; 1804 1805 for (i = 0; i < count; i++) { 1806 struct pipe_arg *pipe = &pipes[i]; 1807 1808 if (pipe->mode == NULL) 1809 continue; 1810 1811 for (j = 0; j < pipe->num_cons; ++j) 1812 add_property(dev, pipe->con_ids[j], "CRTC_ID",0); 1813 1814 add_property(dev, pipe->crtc_id, "MODE_ID", 0); 1815 add_property(dev, pipe->crtc_id, "ACTIVE", 0); 1816 } 1817 } 1818 1819 static void clear_mode(struct device *dev) 1820 { 1821 if (dev->mode.fb_id) 1822 drmModeRmFB(dev->fd, dev->mode.fb_id); 1823 if (dev->mode.bo) 1824 bo_destroy(dev->mode.bo); 1825 } 1826 1827 static void set_planes(struct device *dev, struct plane_arg *p, unsigned int count) 1828 { 1829 unsigned int i; 1830 1831 /* set up planes/overlays */ 1832 for (i = 0; i < count; i++) 1833 if (set_plane(dev, &p[i])) 1834 return; 1835 } 1836 1837 static void set_cursors(struct device *dev, struct pipe_arg *pipes, unsigned int count) 1838 { 1839 uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0}; 1840 uint32_t cw = 64; 1841 uint32_t ch = 64; 1842 struct bo *bo; 1843 uint64_t value; 1844 unsigned int i; 1845 int ret; 1846 1847 ret = drmGetCap(dev->fd, DRM_CAP_CURSOR_WIDTH, &value); 1848 if (!ret) 1849 cw = value; 1850 1851 ret = drmGetCap(dev->fd, DRM_CAP_CURSOR_HEIGHT, &value); 1852 if (!ret) 1853 ch = value; 1854 1855 1856 /* create cursor bo.. just using PATTERN_PLAIN as it has 1857 * translucent alpha 1858 */ 1859 bo = bo_create(dev->fd, DRM_FORMAT_ARGB8888, cw, ch, handles, pitches, 1860 offsets, UTIL_PATTERN_PLAIN); 1861 if (bo == NULL) 1862 return; 1863 1864 dev->mode.cursor_bo = bo; 1865 1866 for (i = 0; i < count; i++) { 1867 struct pipe_arg *pipe = &pipes[i]; 1868 ret = cursor_init(dev->fd, handles[0], 1869 pipe->crtc_id, 1870 pipe->mode->hdisplay, pipe->mode->vdisplay, 1871 cw, ch); 1872 if (ret) { 1873 fprintf(stderr, "failed to init cursor for CRTC[%u]\n", 1874 pipe->crtc_id); 1875 return; 1876 } 1877 } 1878 1879 cursor_start(); 1880 } 1881 1882 static void clear_cursors(struct device *dev) 1883 { 1884 cursor_stop(); 1885 1886 if (dev->mode.cursor_bo) 1887 bo_destroy(dev->mode.cursor_bo); 1888 } 1889 1890 static void test_page_flip(struct device *dev, struct pipe_arg *pipes, unsigned int count) 1891 { 1892 unsigned int other_fb_id; 1893 struct bo *other_bo; 1894 drmEventContext evctx; 1895 unsigned int i; 1896 int ret; 1897 1898 if (bo_fb_create(dev->fd, pipes[0].fourcc, dev->mode.width, dev->mode.height, 1899 UTIL_PATTERN_PLAIN, &other_bo, &other_fb_id)) 1900 return; 1901 1902 for (i = 0; i < count; i++) { 1903 struct pipe_arg *pipe = &pipes[i]; 1904 1905 if (pipe->mode == NULL) 1906 continue; 1907 1908 ret = drmModePageFlip(dev->fd, pipe->crtc_id, 1909 other_fb_id, DRM_MODE_PAGE_FLIP_EVENT, 1910 pipe); 1911 if (ret) { 1912 fprintf(stderr, "failed to page flip: %s\n", strerror(errno)); 1913 goto err_rmfb; 1914 } 1915 gettimeofday(&pipe->start, NULL); 1916 pipe->swap_count = 0; 1917 pipe->fb_id[0] = dev->mode.fb_id; 1918 pipe->fb_id[1] = other_fb_id; 1919 pipe->current_fb_id = other_fb_id; 1920 } 1921 1922 memset(&evctx, 0, sizeof evctx); 1923 evctx.version = DRM_EVENT_CONTEXT_VERSION; 1924 evctx.vblank_handler = NULL; 1925 evctx.page_flip_handler = page_flip_handler; 1926 1927 while (1) { 1928 #if 0 1929 struct pollfd pfd[2]; 1930 1931 pfd[0].fd = 0; 1932 pfd[0].events = POLLIN; 1933 pfd[1].fd = fd; 1934 pfd[1].events = POLLIN; 1935 1936 if (poll(pfd, 2, -1) < 0) { 1937 fprintf(stderr, "poll error\n"); 1938 break; 1939 } 1940 1941 if (pfd[0].revents) 1942 break; 1943 #else 1944 struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 }; 1945 fd_set fds; 1946 1947 FD_ZERO(&fds); 1948 FD_SET(0, &fds); 1949 FD_SET(dev->fd, &fds); 1950 ret = select(dev->fd + 1, &fds, NULL, NULL, &timeout); 1951 1952 if (ret <= 0) { 1953 fprintf(stderr, "select timed out or error (ret %d)\n", 1954 ret); 1955 continue; 1956 } else if (FD_ISSET(0, &fds)) { 1957 break; 1958 } 1959 #endif 1960 1961 drmHandleEvent(dev->fd, &evctx); 1962 } 1963 1964 err_rmfb: 1965 drmModeRmFB(dev->fd, other_fb_id); 1966 bo_destroy(other_bo); 1967 } 1968 1969 #define min(a, b) ((a) < (b) ? (a) : (b)) 1970 1971 static int parse_connector(struct pipe_arg *pipe, const char *arg) 1972 { 1973 unsigned int len; 1974 unsigned int i; 1975 const char *p; 1976 char *endp; 1977 1978 pipe->vrefresh = 0; 1979 pipe->crtc_id = (uint32_t)-1; 1980 strcpy(pipe->format_str, "XR24"); 1981 1982 /* Count the number of connectors and allocate them. */ 1983 pipe->num_cons = 1; 1984 for (p = arg; *p && *p != ':' && *p != '@'; ++p) { 1985 if (*p == ',') 1986 pipe->num_cons++; 1987 } 1988 1989 pipe->con_ids = calloc(pipe->num_cons, sizeof(*pipe->con_ids)); 1990 pipe->cons = calloc(pipe->num_cons, sizeof(*pipe->cons)); 1991 if (pipe->con_ids == NULL || pipe->cons == NULL) 1992 return -1; 1993 1994 /* Parse the connectors. */ 1995 for (i = 0, p = arg; i < pipe->num_cons; ++i, p = endp + 1) { 1996 endp = strpbrk(p, ",@:"); 1997 if (!endp) 1998 break; 1999 2000 pipe->cons[i] = strndup(p, endp - p); 2001 2002 if (*endp != ',') 2003 break; 2004 } 2005 2006 if (i != pipe->num_cons - 1) 2007 return -1; 2008 2009 /* Parse the remaining parameters. */ 2010 if (!endp) 2011 return -1; 2012 if (*endp == '@') { 2013 arg = endp + 1; 2014 pipe->crtc_id = strtoul(arg, &endp, 10); 2015 } 2016 if (*endp != ':') 2017 return -1; 2018 2019 arg = endp + 1; 2020 2021 /* Search for the vertical refresh or the format. */ 2022 p = strpbrk(arg, "-@"); 2023 if (p == NULL) 2024 p = arg + strlen(arg); 2025 len = min(sizeof pipe->mode_str - 1, (unsigned int)(p - arg)); 2026 strncpy(pipe->mode_str, arg, len); 2027 pipe->mode_str[len] = '\0'; 2028 2029 if (*p == '-') { 2030 pipe->vrefresh = strtof(p + 1, &endp); 2031 p = endp; 2032 } 2033 2034 if (*p == '@') { 2035 len = sizeof(pipe->format_str) - 1; 2036 strncpy(pipe->format_str, p + 1, len); 2037 pipe->format_str[len] = '\0'; 2038 } 2039 2040 pipe->fourcc = util_format_fourcc(pipe->format_str); 2041 if (pipe->fourcc == 0) { 2042 fprintf(stderr, "unknown format %s\n", pipe->format_str); 2043 return -1; 2044 } 2045 2046 return 0; 2047 } 2048 2049 static int parse_plane(struct plane_arg *plane, const char *p) 2050 { 2051 unsigned int len; 2052 char *end; 2053 2054 plane->plane_id = strtoul(p, &end, 10); 2055 if (*end != '@') 2056 return -EINVAL; 2057 2058 p = end + 1; 2059 plane->crtc_id = strtoul(p, &end, 10); 2060 if (*end != ':') 2061 return -EINVAL; 2062 2063 p = end + 1; 2064 plane->w = strtoul(p, &end, 10); 2065 if (*end != 'x') 2066 return -EINVAL; 2067 2068 p = end + 1; 2069 plane->h = strtoul(p, &end, 10); 2070 2071 if (*end == '+' || *end == '-') { 2072 plane->x = strtol(end, &end, 10); 2073 if (*end != '+' && *end != '-') 2074 return -EINVAL; 2075 plane->y = strtol(end, &end, 10); 2076 2077 plane->has_position = true; 2078 } 2079 2080 if (*end == '*') { 2081 p = end + 1; 2082 plane->scale = strtod(p, &end); 2083 if (plane->scale <= 0.0) 2084 return -EINVAL; 2085 } else { 2086 plane->scale = 1.0; 2087 } 2088 2089 if (*end == '@') { 2090 len = sizeof(plane->format_str) - 1; 2091 strncpy(plane->format_str, end + 1, len); 2092 plane->format_str[len] = '\0'; 2093 } else { 2094 strcpy(plane->format_str, "XR24"); 2095 } 2096 2097 plane->fourcc = util_format_fourcc(plane->format_str); 2098 if (plane->fourcc == 0) { 2099 fprintf(stderr, "unknown format %s\n", plane->format_str); 2100 return -EINVAL; 2101 } 2102 2103 return 0; 2104 } 2105 2106 static int parse_property(struct property_arg *p, const char *arg) 2107 { 2108 if (sscanf(arg, "%d:%32[^:]:%" SCNu64, &p->obj_id, p->name, &p->value) != 3) 2109 return -1; 2110 2111 p->obj_type = 0; 2112 p->name[DRM_PROP_NAME_LEN] = '\0'; 2113 2114 return 0; 2115 } 2116 2117 static void parse_fill_patterns(char *arg) 2118 { 2119 char *fill = strtok(arg, ","); 2120 if (!fill) 2121 return; 2122 primary_fill = util_pattern_enum(fill); 2123 fill = strtok(NULL, ","); 2124 if (!fill) 2125 return; 2126 secondary_fill = util_pattern_enum(fill); 2127 } 2128 2129 static void usage(char *name) 2130 { 2131 fprintf(stderr, "usage: %s [-acDdefMoPpsCvrw]\n", name); 2132 2133 fprintf(stderr, "\n Query options:\n\n"); 2134 fprintf(stderr, "\t-c\tlist connectors\n"); 2135 fprintf(stderr, "\t-e\tlist encoders\n"); 2136 fprintf(stderr, "\t-f\tlist framebuffers\n"); 2137 fprintf(stderr, "\t-p\tlist CRTCs and planes (pipes)\n"); 2138 2139 fprintf(stderr, "\n Test options:\n\n"); 2140 fprintf(stderr, "\t-P <plane_id>@<crtc_id>:<w>x<h>[+<x>+<y>][*<scale>][@<format>]\tset a plane, see 'plane-topology'\n"); 2141 fprintf(stderr, "\t-s <connector_id>[,<connector_id>][@<crtc_id>]:mode[@<format>]\tset a mode, see 'mode-topology'\n"); 2142 fprintf(stderr, "\t\twhere mode can be specified as:\n"); 2143 fprintf(stderr, "\t\t<hdisp>x<vdisp>[-<vrefresh>]\n"); 2144 fprintf(stderr, "\t\t<hdisp>,<hss>,<hse>,<htot>,<vdisp>,<vss>,<vse>,<vtot>-<vrefresh>\n"); 2145 fprintf(stderr, "\t\t#<mode index>\n"); 2146 fprintf(stderr, "\t-C\ttest hw cursor\n"); 2147 fprintf(stderr, "\t-v\ttest vsynced page flipping\n"); 2148 fprintf(stderr, "\t-r\tset the preferred mode for all connectors\n"); 2149 fprintf(stderr, "\t-w <obj_id>:<prop_name>:<value>\tset property, see 'property'\n"); 2150 fprintf(stderr, "\t-a \tuse atomic API\n"); 2151 fprintf(stderr, "\t-F pattern1,pattern2\tspecify fill patterns\n"); 2152 fprintf(stderr, "\t-o <desired file path> \t Dump writeback output buffer to file\n"); 2153 2154 fprintf(stderr, "\n Generic options:\n\n"); 2155 fprintf(stderr, "\t-d\tdrop master after mode set\n"); 2156 fprintf(stderr, "\t-M module\tuse the given driver\n"); 2157 fprintf(stderr, "\t-D device\tuse the given device\n"); 2158 2159 fprintf(stderr, "\n\tDefault is to dump all info.\n"); 2160 2161 fprintf(stderr, "\n"); 2162 fprintf(stderr, "Plane Topology is defined as:\n"); 2163 fprintf(stderr, "\tplane-topology\t::= plane-id '@' crtc-id ':' width 'x' height ( <plane-offsets> )? ;\n"); 2164 fprintf(stderr, "\tplane-offsets\t::= '+' x-offset '+' y-offset ( <plane-scale> )? ;\n"); 2165 fprintf(stderr, "\tplane-scale\t::= '*' scale ( <plane-format> )? ;\n"); 2166 fprintf(stderr, "\tplane-format\t::= '@' format ;\n"); 2167 2168 fprintf(stderr, "\n"); 2169 fprintf(stderr, "Mode Topology is defined as:\n"); 2170 fprintf(stderr, "\tmode-topology\t::= connector-id ( ',' connector-id )* ( '@' crtc-id )? ':' <mode-selection> ( '@' format )? ;\n"); 2171 fprintf(stderr, "\tmode-selection\t::= <indexed-mode> | <named-mode> | <custom-mode> ;\n"); 2172 fprintf(stderr, "\tindexed-mode\t::= '#' mode-index ;\n"); 2173 fprintf(stderr, "\tnamed-mode\t::= width 'x' height ( '-' vrefresh )? ;\n"); 2174 fprintf(stderr, "\tcustom-mode\t::= hdisplay ',' hsyncstart ',' hsyncend ',' htotal ',' vdisplay ',' vsyncstart ',' vsyncend ',' vtotal '-' vrefresh ;\n"); 2175 2176 fprintf(stderr, "\n"); 2177 fprintf(stderr, "Property is defined as:\n"); 2178 fprintf(stderr, "\tproperty\t::= object-id ':' property-name ':' value ;\n"); 2179 exit(0); 2180 } 2181 2182 static char optstr[] = "acdD:efF:M:P:ps:Cvrw:o:"; 2183 2184 int main(int argc, char **argv) 2185 { 2186 struct device dev; 2187 2188 int c; 2189 int encoders = 0, connectors = 0, crtcs = 0, planes = 0, framebuffers = 0; 2190 int drop_master = 0; 2191 int test_vsync = 0; 2192 int test_cursor = 0; 2193 int set_preferred = 0; 2194 int use_atomic = 0; 2195 char *device = NULL; 2196 char *module = NULL; 2197 unsigned int i; 2198 unsigned int count = 0, plane_count = 0; 2199 unsigned int prop_count = 0; 2200 struct pipe_arg *pipe_args = NULL; 2201 struct plane_arg *plane_args = NULL; 2202 struct property_arg *prop_args = NULL; 2203 unsigned int args = 0; 2204 int ret; 2205 char *dump_path = NULL; 2206 2207 memset(&dev, 0, sizeof dev); 2208 2209 opterr = 0; 2210 while ((c = getopt(argc, argv, optstr)) != -1) { 2211 args++; 2212 2213 switch (c) { 2214 case 'a': 2215 use_atomic = 1; 2216 /* Preserve the default behaviour of dumping all information. */ 2217 args--; 2218 break; 2219 case 'c': 2220 connectors = 1; 2221 break; 2222 case 'D': 2223 device = optarg; 2224 /* Preserve the default behaviour of dumping all information. */ 2225 args--; 2226 break; 2227 case 'd': 2228 drop_master = 1; 2229 break; 2230 case 'e': 2231 encoders = 1; 2232 break; 2233 case 'f': 2234 framebuffers = 1; 2235 break; 2236 case 'F': 2237 parse_fill_patterns(optarg); 2238 break; 2239 case 'M': 2240 module = optarg; 2241 /* Preserve the default behaviour of dumping all information. */ 2242 args--; 2243 break; 2244 case 'o': 2245 dump_path = optarg; 2246 break; 2247 case 'P': 2248 plane_args = realloc(plane_args, 2249 (plane_count + 1) * sizeof *plane_args); 2250 if (plane_args == NULL) { 2251 fprintf(stderr, "memory allocation failed\n"); 2252 return 1; 2253 } 2254 memset(&plane_args[plane_count], 0, sizeof(*plane_args)); 2255 2256 if (parse_plane(&plane_args[plane_count], optarg) < 0) 2257 usage(argv[0]); 2258 2259 plane_count++; 2260 break; 2261 case 'p': 2262 crtcs = 1; 2263 planes = 1; 2264 break; 2265 case 's': 2266 pipe_args = realloc(pipe_args, 2267 (count + 1) * sizeof *pipe_args); 2268 if (pipe_args == NULL) { 2269 fprintf(stderr, "memory allocation failed\n"); 2270 return 1; 2271 } 2272 memset(&pipe_args[count], 0, sizeof(*pipe_args)); 2273 2274 if (parse_connector(&pipe_args[count], optarg) < 0) 2275 usage(argv[0]); 2276 2277 count++; 2278 break; 2279 case 'C': 2280 test_cursor = 1; 2281 break; 2282 case 'v': 2283 test_vsync = 1; 2284 break; 2285 case 'r': 2286 set_preferred = 1; 2287 break; 2288 case 'w': 2289 prop_args = realloc(prop_args, 2290 (prop_count + 1) * sizeof *prop_args); 2291 if (prop_args == NULL) { 2292 fprintf(stderr, "memory allocation failed\n"); 2293 return 1; 2294 } 2295 memset(&prop_args[prop_count], 0, sizeof(*prop_args)); 2296 2297 if (parse_property(&prop_args[prop_count], optarg) < 0) 2298 usage(argv[0]); 2299 2300 prop_count++; 2301 break; 2302 default: 2303 usage(argv[0]); 2304 break; 2305 } 2306 } 2307 2308 /* Dump all the details when no* arguments are provided. */ 2309 if (!args) 2310 encoders = connectors = crtcs = planes = framebuffers = 1; 2311 2312 if (test_vsync && !count && !set_preferred) { 2313 fprintf(stderr, "page flipping requires at least one -s or -r option.\n"); 2314 return -1; 2315 } 2316 if (set_preferred && count) { 2317 fprintf(stderr, "cannot use -r (preferred) when -s (mode) is set\n"); 2318 return -1; 2319 } 2320 2321 dev.fd = util_open(device, module); 2322 if (dev.fd < 0) 2323 return -1; 2324 2325 if (use_atomic) { 2326 ret = drmSetClientCap(dev.fd, DRM_CLIENT_CAP_ATOMIC, 1); 2327 drmSetClientCap(dev.fd, DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1); 2328 if (ret) { 2329 fprintf(stderr, "no atomic modesetting support: %s\n", strerror(errno)); 2330 drmClose(dev.fd); 2331 return -1; 2332 } 2333 } 2334 2335 dev.use_atomic = use_atomic; 2336 2337 dev.resources = get_resources(&dev); 2338 if (!dev.resources) { 2339 drmClose(dev.fd); 2340 return 1; 2341 } 2342 2343 #define dump_resource(dev, res) if (res) dump_##res(dev) 2344 2345 dump_resource(&dev, encoders); 2346 dump_resource(&dev, connectors); 2347 dump_resource(&dev, crtcs); 2348 dump_resource(&dev, planes); 2349 dump_resource(&dev, framebuffers); 2350 2351 if (dev.use_atomic) 2352 dev.req = drmModeAtomicAlloc(); 2353 2354 for (i = 0; i < prop_count; ++i) 2355 set_property(&dev, &prop_args[i]); 2356 2357 if (dev.use_atomic) { 2358 if (set_preferred || (count && plane_count)) { 2359 uint64_t cap = 0; 2360 2361 ret = drmGetCap(dev.fd, DRM_CAP_DUMB_BUFFER, &cap); 2362 if (ret || cap == 0) { 2363 fprintf(stderr, "driver doesn't support the dumb buffer API\n"); 2364 return 1; 2365 } 2366 2367 if (set_preferred || count) 2368 count = set_mode(&dev, &pipe_args, count); 2369 2370 if (dump_path) { 2371 if (!pipe_has_writeback_connector(&dev, pipe_args, count)) { 2372 fprintf(stderr, "No writeback connector found, can not dump.\n"); 2373 return 1; 2374 } 2375 2376 writeback_config(&dev, pipe_args, count); 2377 } 2378 2379 if (plane_count) 2380 atomic_set_planes(&dev, plane_args, plane_count, false); 2381 2382 ret = drmModeAtomicCommit(dev.fd, dev.req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); 2383 if (ret) { 2384 fprintf(stderr, "Atomic Commit failed [1]\n"); 2385 return 1; 2386 } 2387 2388 /* 2389 * Since only writeback connectors have an output fb, this should only be 2390 * called for writeback. 2391 */ 2392 if (dump_path) { 2393 ret = poll_writeback_fence(dev.writeback_fence_fd, 1000); 2394 if (ret) 2395 fprintf(stderr, "Poll for writeback error: %d. Skipping Dump.\n", 2396 ret); 2397 dump_output_fb(&dev, pipe_args, dump_path, count); 2398 } 2399 2400 if (test_vsync) 2401 atomic_test_page_flip(&dev, pipe_args, plane_args, plane_count); 2402 2403 if (drop_master) 2404 drmDropMaster(dev.fd); 2405 2406 getchar(); 2407 2408 drmModeAtomicFree(dev.req); 2409 dev.req = drmModeAtomicAlloc(); 2410 2411 /* XXX: properly teardown the preferred mode/plane state */ 2412 if (plane_count) 2413 atomic_clear_planes(&dev, plane_args, plane_count); 2414 2415 if (count) 2416 atomic_clear_mode(&dev, pipe_args, count); 2417 } 2418 2419 ret = drmModeAtomicCommit(dev.fd, dev.req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); 2420 if (ret) 2421 fprintf(stderr, "Atomic Commit failed\n"); 2422 2423 if (count && plane_count) 2424 atomic_clear_FB(&dev, plane_args, plane_count); 2425 2426 drmModeAtomicFree(dev.req); 2427 } else { 2428 if (dump_path) { 2429 fprintf(stderr, "writeback / dump is only supported in atomic mode\n"); 2430 return 1; 2431 } 2432 2433 if (set_preferred || count || plane_count) { 2434 uint64_t cap = 0; 2435 2436 ret = drmGetCap(dev.fd, DRM_CAP_DUMB_BUFFER, &cap); 2437 if (ret || cap == 0) { 2438 fprintf(stderr, "driver doesn't support the dumb buffer API\n"); 2439 return 1; 2440 } 2441 2442 if (set_preferred || count) 2443 count = set_mode(&dev, &pipe_args, count); 2444 2445 if (plane_count) 2446 set_planes(&dev, plane_args, plane_count); 2447 2448 if (test_cursor) 2449 set_cursors(&dev, pipe_args, count); 2450 2451 if (test_vsync) 2452 test_page_flip(&dev, pipe_args, count); 2453 2454 if (drop_master) 2455 drmDropMaster(dev.fd); 2456 2457 getchar(); 2458 2459 if (test_cursor) 2460 clear_cursors(&dev); 2461 2462 if (plane_count) 2463 clear_planes(&dev, plane_args, plane_count); 2464 2465 if (set_preferred || count) 2466 clear_mode(&dev); 2467 } 2468 } 2469 2470 free_resources(dev.resources); 2471 drmClose(dev.fd); 2472 2473 return 0; 2474 } 2475