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