modetest.c revision bf6cc7dc
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#ifdef HAVE_SYS_SELECT_H 55#include <sys/select.h> 56#endif 57 58#include "xf86drm.h" 59#include "xf86drmMode.h" 60#include "drm_fourcc.h" 61 62#include "util/common.h" 63#include "util/format.h" 64#include "util/kms.h" 65#include "util/pattern.h" 66 67#include "buffers.h" 68#include "cursor.h" 69 70static enum util_fill_pattern primary_fill = UTIL_PATTERN_SMPTE; 71static enum util_fill_pattern secondary_fill = UTIL_PATTERN_TILES; 72 73struct crtc { 74 drmModeCrtc *crtc; 75 drmModeObjectProperties *props; 76 drmModePropertyRes **props_info; 77 drmModeModeInfo *mode; 78}; 79 80struct encoder { 81 drmModeEncoder *encoder; 82}; 83 84struct connector { 85 drmModeConnector *connector; 86 drmModeObjectProperties *props; 87 drmModePropertyRes **props_info; 88 char *name; 89}; 90 91struct fb { 92 drmModeFB *fb; 93}; 94 95struct plane { 96 drmModePlane *plane; 97 drmModeObjectProperties *props; 98 drmModePropertyRes **props_info; 99}; 100 101struct resources { 102 drmModeRes *res; 103 drmModePlaneRes *plane_res; 104 105 struct crtc *crtcs; 106 struct encoder *encoders; 107 struct connector *connectors; 108 struct fb *fbs; 109 struct plane *planes; 110}; 111 112struct device { 113 int fd; 114 115 struct resources *resources; 116 117 struct { 118 unsigned int width; 119 unsigned int height; 120 121 unsigned int fb_id; 122 struct bo *bo; 123 struct bo *cursor_bo; 124 } mode; 125 126 int use_atomic; 127 drmModeAtomicReq *req; 128}; 129 130static inline int64_t U642I64(uint64_t val) 131{ 132 return (int64_t)*((int64_t *)&val); 133} 134 135#define bit_name_fn(res) \ 136const char * res##_str(int type) { \ 137 unsigned int i; \ 138 const char *sep = ""; \ 139 for (i = 0; i < ARRAY_SIZE(res##_names); i++) { \ 140 if (type & (1 << i)) { \ 141 printf("%s%s", sep, res##_names[i]); \ 142 sep = ", "; \ 143 } \ 144 } \ 145 return NULL; \ 146} 147 148static const char *mode_type_names[] = { 149 "builtin", 150 "clock_c", 151 "crtc_c", 152 "preferred", 153 "default", 154 "userdef", 155 "driver", 156}; 157 158static bit_name_fn(mode_type) 159 160static const char *mode_flag_names[] = { 161 "phsync", 162 "nhsync", 163 "pvsync", 164 "nvsync", 165 "interlace", 166 "dblscan", 167 "csync", 168 "pcsync", 169 "ncsync", 170 "hskew", 171 "bcast", 172 "pixmux", 173 "dblclk", 174 "clkdiv2" 175}; 176 177static bit_name_fn(mode_flag) 178 179static void dump_fourcc(uint32_t fourcc) 180{ 181 printf(" %c%c%c%c", 182 fourcc, 183 fourcc >> 8, 184 fourcc >> 16, 185 fourcc >> 24); 186} 187 188static void dump_encoders(struct device *dev) 189{ 190 drmModeEncoder *encoder; 191 int i; 192 193 printf("Encoders:\n"); 194 printf("id\tcrtc\ttype\tpossible crtcs\tpossible clones\t\n"); 195 for (i = 0; i < dev->resources->res->count_encoders; i++) { 196 encoder = dev->resources->encoders[i].encoder; 197 if (!encoder) 198 continue; 199 200 printf("%d\t%d\t%s\t0x%08x\t0x%08x\n", 201 encoder->encoder_id, 202 encoder->crtc_id, 203 util_lookup_encoder_type_name(encoder->encoder_type), 204 encoder->possible_crtcs, 205 encoder->possible_clones); 206 } 207 printf("\n"); 208} 209 210static void dump_mode(drmModeModeInfo *mode) 211{ 212 printf(" %s %d %d %d %d %d %d %d %d %d %d", 213 mode->name, 214 mode->vrefresh, 215 mode->hdisplay, 216 mode->hsync_start, 217 mode->hsync_end, 218 mode->htotal, 219 mode->vdisplay, 220 mode->vsync_start, 221 mode->vsync_end, 222 mode->vtotal, 223 mode->clock); 224 225 printf(" flags: "); 226 mode_flag_str(mode->flags); 227 printf("; type: "); 228 mode_type_str(mode->type); 229 printf("\n"); 230} 231 232static void dump_blob(struct device *dev, uint32_t blob_id) 233{ 234 uint32_t i; 235 unsigned char *blob_data; 236 drmModePropertyBlobPtr blob; 237 238 blob = drmModeGetPropertyBlob(dev->fd, blob_id); 239 if (!blob) { 240 printf("\n"); 241 return; 242 } 243 244 blob_data = blob->data; 245 246 for (i = 0; i < blob->length; i++) { 247 if (i % 16 == 0) 248 printf("\n\t\t\t"); 249 printf("%.2hhx", blob_data[i]); 250 } 251 printf("\n"); 252 253 drmModeFreePropertyBlob(blob); 254} 255 256static const char *modifier_to_string(uint64_t modifier) 257{ 258 switch (modifier) { 259 case DRM_FORMAT_MOD_INVALID: 260 return "INVALID"; 261 case DRM_FORMAT_MOD_LINEAR: 262 return "LINEAR"; 263 case I915_FORMAT_MOD_X_TILED: 264 return "X_TILED"; 265 case I915_FORMAT_MOD_Y_TILED: 266 return "Y_TILED"; 267 case I915_FORMAT_MOD_Yf_TILED: 268 return "Yf_TILED"; 269 case I915_FORMAT_MOD_Y_TILED_CCS: 270 return "Y_TILED_CCS"; 271 case I915_FORMAT_MOD_Yf_TILED_CCS: 272 return "Yf_TILED_CCS"; 273 case DRM_FORMAT_MOD_SAMSUNG_64_32_TILE: 274 return "SAMSUNG_64_32_TILE"; 275 case DRM_FORMAT_MOD_VIVANTE_TILED: 276 return "VIVANTE_TILED"; 277 case DRM_FORMAT_MOD_VIVANTE_SUPER_TILED: 278 return "VIVANTE_SUPER_TILED"; 279 case DRM_FORMAT_MOD_VIVANTE_SPLIT_TILED: 280 return "VIVANTE_SPLIT_TILED"; 281 case DRM_FORMAT_MOD_VIVANTE_SPLIT_SUPER_TILED: 282 return "VIVANTE_SPLIT_SUPER_TILED"; 283 case DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED: 284 return "NVIDIA_TEGRA_TILED"; 285 case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0): 286 return "NVIDIA_16BX2_BLOCK(0)"; 287 case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1): 288 return "NVIDIA_16BX2_BLOCK(1)"; 289 case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2): 290 return "NVIDIA_16BX2_BLOCK(2)"; 291 case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3): 292 return "NVIDIA_16BX2_BLOCK(3)"; 293 case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4): 294 return "NVIDIA_16BX2_BLOCK(4)"; 295 case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5): 296 return "NVIDIA_16BX2_BLOCK(5)"; 297 case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED: 298 return "MOD_BROADCOM_VC4_T_TILED"; 299 case DRM_FORMAT_MOD_QCOM_COMPRESSED: 300 return "QCOM_COMPRESSED"; 301 default: 302 return "(UNKNOWN MODIFIER)"; 303 } 304} 305 306static void dump_in_formats(struct device *dev, uint32_t blob_id) 307{ 308 uint32_t i, j; 309 drmModePropertyBlobPtr blob; 310 struct drm_format_modifier_blob *header; 311 uint32_t *formats; 312 struct drm_format_modifier *modifiers; 313 314 printf("\t\tin_formats blob decoded:\n"); 315 blob = drmModeGetPropertyBlob(dev->fd, blob_id); 316 if (!blob) { 317 printf("\n"); 318 return; 319 } 320 321 header = blob->data; 322 formats = (uint32_t *) ((char *) header + header->formats_offset); 323 modifiers = (struct drm_format_modifier *) 324 ((char *) header + header->modifiers_offset); 325 326 for (i = 0; i < header->count_formats; i++) { 327 printf("\t\t\t"); 328 dump_fourcc(formats[i]); 329 printf(": "); 330 for (j = 0; j < header->count_modifiers; j++) { 331 uint64_t mask = 1ULL << i; 332 if (modifiers[j].formats & mask) 333 printf(" %s", modifier_to_string(modifiers[j].modifier)); 334 } 335 printf("\n"); 336 } 337 338 drmModeFreePropertyBlob(blob); 339} 340 341static 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=%llu", prop->enums[i].name, 390 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 423static 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->res->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("\tname 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]); 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 466static 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->res->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); 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 499static 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->res->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 519static 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 if (!dev->resources->plane_res) 527 return; 528 529 for (i = 0; i < dev->resources->plane_res->count_planes; i++) { 530 struct plane *plane = &dev->resources->planes[i]; 531 drmModePlane *ovr = plane->plane; 532 if (!ovr) 533 continue; 534 535 printf("%d\t%d\t%d\t%d,%d\t\t%d,%d\t%-8d\t0x%08x\n", 536 ovr->plane_id, ovr->crtc_id, ovr->fb_id, 537 ovr->crtc_x, ovr->crtc_y, ovr->x, ovr->y, 538 ovr->gamma_size, ovr->possible_crtcs); 539 540 if (!ovr->count_formats) 541 continue; 542 543 printf(" formats:"); 544 for (j = 0; j < ovr->count_formats; j++) 545 dump_fourcc(ovr->formats[j]); 546 printf("\n"); 547 548 if (plane->props) { 549 printf(" props:\n"); 550 for (j = 0; j < plane->props->count_props; j++) 551 dump_prop(dev, plane->props_info[j], 552 plane->props->props[j], 553 plane->props->prop_values[j]); 554 } else { 555 printf(" no properties found\n"); 556 } 557 } 558 printf("\n"); 559 560 return; 561} 562 563static void free_resources(struct resources *res) 564{ 565 int i; 566 567 if (!res) 568 return; 569 570#define free_resource(_res, __res, type, Type) \ 571 do { \ 572 if (!(_res)->type##s) \ 573 break; \ 574 for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \ 575 if (!(_res)->type##s[i].type) \ 576 break; \ 577 drmModeFree##Type((_res)->type##s[i].type); \ 578 } \ 579 free((_res)->type##s); \ 580 } while (0) 581 582#define free_properties(_res, __res, type) \ 583 do { \ 584 for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \ 585 drmModeFreeObjectProperties(res->type##s[i].props); \ 586 free(res->type##s[i].props_info); \ 587 } \ 588 } while (0) 589 590 if (res->res) { 591 free_properties(res, res, crtc); 592 593 free_resource(res, res, crtc, Crtc); 594 free_resource(res, res, encoder, Encoder); 595 596 for (i = 0; i < res->res->count_connectors; i++) 597 free(res->connectors[i].name); 598 599 free_resource(res, res, connector, Connector); 600 free_resource(res, res, fb, FB); 601 602 drmModeFreeResources(res->res); 603 } 604 605 if (res->plane_res) { 606 free_properties(res, plane_res, plane); 607 608 free_resource(res, plane_res, plane, Plane); 609 610 drmModeFreePlaneResources(res->plane_res); 611 } 612 613 free(res); 614} 615 616static struct resources *get_resources(struct device *dev) 617{ 618 struct resources *res; 619 int i; 620 621 res = calloc(1, sizeof(*res)); 622 if (res == 0) 623 return NULL; 624 625 drmSetClientCap(dev->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); 626 627 res->res = drmModeGetResources(dev->fd); 628 if (!res->res) { 629 fprintf(stderr, "drmModeGetResources failed: %s\n", 630 strerror(errno)); 631 goto error; 632 } 633 634 res->crtcs = calloc(res->res->count_crtcs, sizeof(*res->crtcs)); 635 res->encoders = calloc(res->res->count_encoders, sizeof(*res->encoders)); 636 res->connectors = calloc(res->res->count_connectors, sizeof(*res->connectors)); 637 res->fbs = calloc(res->res->count_fbs, sizeof(*res->fbs)); 638 639 if (!res->crtcs || !res->encoders || !res->connectors || !res->fbs) 640 goto error; 641 642#define get_resource(_res, __res, type, Type) \ 643 do { \ 644 for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \ 645 (_res)->type##s[i].type = \ 646 drmModeGet##Type(dev->fd, (_res)->__res->type##s[i]); \ 647 if (!(_res)->type##s[i].type) \ 648 fprintf(stderr, "could not get %s %i: %s\n", \ 649 #type, (_res)->__res->type##s[i], \ 650 strerror(errno)); \ 651 } \ 652 } while (0) 653 654 get_resource(res, res, crtc, Crtc); 655 get_resource(res, res, encoder, Encoder); 656 get_resource(res, res, connector, Connector); 657 get_resource(res, res, fb, FB); 658 659 /* Set the name of all connectors based on the type name and the per-type ID. */ 660 for (i = 0; i < res->res->count_connectors; i++) { 661 struct connector *connector = &res->connectors[i]; 662 drmModeConnector *conn = connector->connector; 663 int num; 664 665 num = asprintf(&connector->name, "%s-%u", 666 util_lookup_connector_type_name(conn->connector_type), 667 conn->connector_type_id); 668 if (num < 0) 669 goto error; 670 } 671 672#define get_properties(_res, __res, type, Type) \ 673 do { \ 674 for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \ 675 struct type *obj = &res->type##s[i]; \ 676 unsigned int j; \ 677 obj->props = \ 678 drmModeObjectGetProperties(dev->fd, obj->type->type##_id, \ 679 DRM_MODE_OBJECT_##Type); \ 680 if (!obj->props) { \ 681 fprintf(stderr, \ 682 "could not get %s %i properties: %s\n", \ 683 #type, obj->type->type##_id, \ 684 strerror(errno)); \ 685 continue; \ 686 } \ 687 obj->props_info = calloc(obj->props->count_props, \ 688 sizeof(*obj->props_info)); \ 689 if (!obj->props_info) \ 690 continue; \ 691 for (j = 0; j < obj->props->count_props; ++j) \ 692 obj->props_info[j] = \ 693 drmModeGetProperty(dev->fd, obj->props->props[j]); \ 694 } \ 695 } while (0) 696 697 get_properties(res, res, crtc, CRTC); 698 get_properties(res, res, connector, CONNECTOR); 699 700 for (i = 0; i < res->res->count_crtcs; ++i) 701 res->crtcs[i].mode = &res->crtcs[i].crtc->mode; 702 703 res->plane_res = drmModeGetPlaneResources(dev->fd); 704 if (!res->plane_res) { 705 fprintf(stderr, "drmModeGetPlaneResources failed: %s\n", 706 strerror(errno)); 707 return res; 708 } 709 710 res->planes = calloc(res->plane_res->count_planes, sizeof(*res->planes)); 711 if (!res->planes) 712 goto error; 713 714 get_resource(res, plane_res, plane, Plane); 715 get_properties(res, plane_res, plane, PLANE); 716 717 return res; 718 719error: 720 free_resources(res); 721 return NULL; 722} 723 724static int get_crtc_index(struct device *dev, uint32_t id) 725{ 726 int i; 727 728 for (i = 0; i < dev->resources->res->count_crtcs; ++i) { 729 drmModeCrtc *crtc = dev->resources->crtcs[i].crtc; 730 if (crtc && crtc->crtc_id == id) 731 return i; 732 } 733 734 return -1; 735} 736 737static drmModeConnector *get_connector_by_name(struct device *dev, const char *name) 738{ 739 struct connector *connector; 740 int i; 741 742 for (i = 0; i < dev->resources->res->count_connectors; i++) { 743 connector = &dev->resources->connectors[i]; 744 745 if (strcmp(connector->name, name) == 0) 746 return connector->connector; 747 } 748 749 return NULL; 750} 751 752static drmModeConnector *get_connector_by_id(struct device *dev, uint32_t id) 753{ 754 drmModeConnector *connector; 755 int i; 756 757 for (i = 0; i < dev->resources->res->count_connectors; i++) { 758 connector = dev->resources->connectors[i].connector; 759 if (connector && connector->connector_id == id) 760 return connector; 761 } 762 763 return NULL; 764} 765 766static drmModeEncoder *get_encoder_by_id(struct device *dev, uint32_t id) 767{ 768 drmModeEncoder *encoder; 769 int i; 770 771 for (i = 0; i < dev->resources->res->count_encoders; i++) { 772 encoder = dev->resources->encoders[i].encoder; 773 if (encoder && encoder->encoder_id == id) 774 return encoder; 775 } 776 777 return NULL; 778} 779 780/* ----------------------------------------------------------------------------- 781 * Pipes and planes 782 */ 783 784/* 785 * Mode setting with the kernel interfaces is a bit of a chore. 786 * First you have to find the connector in question and make sure the 787 * requested mode is available. 788 * Then you need to find the encoder attached to that connector so you 789 * can bind it with a free crtc. 790 */ 791struct pipe_arg { 792 const char **cons; 793 uint32_t *con_ids; 794 unsigned int num_cons; 795 uint32_t crtc_id; 796 char mode_str[64]; 797 char format_str[5]; 798 unsigned int vrefresh; 799 unsigned int fourcc; 800 drmModeModeInfo *mode; 801 struct crtc *crtc; 802 unsigned int fb_id[2], current_fb_id; 803 struct timeval start; 804 805 int swap_count; 806}; 807 808struct plane_arg { 809 uint32_t plane_id; /* the id of plane to use */ 810 uint32_t crtc_id; /* the id of CRTC to bind to */ 811 bool has_position; 812 int32_t x, y; 813 uint32_t w, h; 814 double scale; 815 unsigned int fb_id; 816 unsigned int old_fb_id; 817 struct bo *bo; 818 struct bo *old_bo; 819 char format_str[5]; /* need to leave room for terminating \0 */ 820 unsigned int fourcc; 821}; 822 823static drmModeModeInfo * 824connector_find_mode(struct device *dev, uint32_t con_id, const char *mode_str, 825 const unsigned int vrefresh) 826{ 827 drmModeConnector *connector; 828 drmModeModeInfo *mode; 829 int i; 830 831 connector = get_connector_by_id(dev, con_id); 832 if (!connector || !connector->count_modes) 833 return NULL; 834 835 for (i = 0; i < connector->count_modes; i++) { 836 mode = &connector->modes[i]; 837 if (!strcmp(mode->name, mode_str)) { 838 /* If the vertical refresh frequency is not specified then return the 839 * first mode that match with the name. Else, return the mode that match 840 * the name and the specified vertical refresh frequency. 841 */ 842 if (vrefresh == 0) 843 return mode; 844 else if (mode->vrefresh == vrefresh) 845 return mode; 846 } 847 } 848 849 return NULL; 850} 851 852static struct crtc *pipe_find_crtc(struct device *dev, struct pipe_arg *pipe) 853{ 854 uint32_t possible_crtcs = ~0; 855 uint32_t active_crtcs = 0; 856 unsigned int crtc_idx; 857 unsigned int i; 858 int j; 859 860 for (i = 0; i < pipe->num_cons; ++i) { 861 uint32_t crtcs_for_connector = 0; 862 drmModeConnector *connector; 863 drmModeEncoder *encoder; 864 int idx; 865 866 connector = get_connector_by_id(dev, pipe->con_ids[i]); 867 if (!connector) 868 return NULL; 869 870 for (j = 0; j < connector->count_encoders; ++j) { 871 encoder = get_encoder_by_id(dev, connector->encoders[j]); 872 if (!encoder) 873 continue; 874 875 crtcs_for_connector |= encoder->possible_crtcs; 876 877 idx = get_crtc_index(dev, encoder->crtc_id); 878 if (idx >= 0) 879 active_crtcs |= 1 << idx; 880 } 881 882 possible_crtcs &= crtcs_for_connector; 883 } 884 885 if (!possible_crtcs) 886 return NULL; 887 888 /* Return the first possible and active CRTC if one exists, or the first 889 * possible CRTC otherwise. 890 */ 891 if (possible_crtcs & active_crtcs) 892 crtc_idx = ffs(possible_crtcs & active_crtcs); 893 else 894 crtc_idx = ffs(possible_crtcs); 895 896 return &dev->resources->crtcs[crtc_idx - 1]; 897} 898 899static int pipe_find_crtc_and_mode(struct device *dev, struct pipe_arg *pipe) 900{ 901 drmModeModeInfo *mode = NULL; 902 int i; 903 904 pipe->mode = NULL; 905 906 for (i = 0; i < (int)pipe->num_cons; i++) { 907 mode = connector_find_mode(dev, pipe->con_ids[i], 908 pipe->mode_str, pipe->vrefresh); 909 if (mode == NULL) { 910 fprintf(stderr, 911 "failed to find mode \"%s\" for connector %s\n", 912 pipe->mode_str, pipe->cons[i]); 913 return -EINVAL; 914 } 915 } 916 917 /* If the CRTC ID was specified, get the corresponding CRTC. Otherwise 918 * locate a CRTC that can be attached to all the connectors. 919 */ 920 if (pipe->crtc_id != (uint32_t)-1) { 921 for (i = 0; i < dev->resources->res->count_crtcs; i++) { 922 struct crtc *crtc = &dev->resources->crtcs[i]; 923 924 if (pipe->crtc_id == crtc->crtc->crtc_id) { 925 pipe->crtc = crtc; 926 break; 927 } 928 } 929 } else { 930 pipe->crtc = pipe_find_crtc(dev, pipe); 931 } 932 933 if (!pipe->crtc) { 934 fprintf(stderr, "failed to find CRTC for pipe\n"); 935 return -EINVAL; 936 } 937 938 pipe->mode = mode; 939 pipe->crtc->mode = mode; 940 941 return 0; 942} 943 944/* ----------------------------------------------------------------------------- 945 * Properties 946 */ 947 948struct property_arg { 949 uint32_t obj_id; 950 uint32_t obj_type; 951 char name[DRM_PROP_NAME_LEN+1]; 952 uint32_t prop_id; 953 uint64_t value; 954 bool optional; 955}; 956 957static bool set_property(struct device *dev, struct property_arg *p) 958{ 959 drmModeObjectProperties *props = NULL; 960 drmModePropertyRes **props_info = NULL; 961 const char *obj_type; 962 int ret; 963 int i; 964 965 p->obj_type = 0; 966 p->prop_id = 0; 967 968#define find_object(_res, __res, type, Type) \ 969 do { \ 970 for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \ 971 struct type *obj = &(_res)->type##s[i]; \ 972 if (obj->type->type##_id != p->obj_id) \ 973 continue; \ 974 p->obj_type = DRM_MODE_OBJECT_##Type; \ 975 obj_type = #Type; \ 976 props = obj->props; \ 977 props_info = obj->props_info; \ 978 } \ 979 } while(0) \ 980 981 find_object(dev->resources, res, crtc, CRTC); 982 if (p->obj_type == 0) 983 find_object(dev->resources, res, connector, CONNECTOR); 984 if (p->obj_type == 0) 985 find_object(dev->resources, plane_res, plane, PLANE); 986 if (p->obj_type == 0) { 987 fprintf(stderr, "Object %i not found, can't set property\n", 988 p->obj_id); 989 return false; 990 } 991 992 if (!props) { 993 fprintf(stderr, "%s %i has no properties\n", 994 obj_type, p->obj_id); 995 return false; 996 } 997 998 for (i = 0; i < (int)props->count_props; ++i) { 999 if (!props_info[i]) 1000 continue; 1001 if (strcmp(props_info[i]->name, p->name) == 0) 1002 break; 1003 } 1004 1005 if (i == (int)props->count_props) { 1006 if (!p->optional) 1007 fprintf(stderr, "%s %i has no %s property\n", 1008 obj_type, p->obj_id, p->name); 1009 return false; 1010 } 1011 1012 p->prop_id = props->props[i]; 1013 1014 if (!dev->use_atomic) 1015 ret = drmModeObjectSetProperty(dev->fd, p->obj_id, p->obj_type, 1016 p->prop_id, p->value); 1017 else 1018 ret = drmModeAtomicAddProperty(dev->req, p->obj_id, p->prop_id, p->value); 1019 1020 if (ret < 0) 1021 fprintf(stderr, "failed to set %s %i property %s to %" PRIu64 ": %s\n", 1022 obj_type, p->obj_id, p->name, p->value, strerror(errno)); 1023 1024 return true; 1025} 1026 1027/* -------------------------------------------------------------------------- */ 1028 1029static void 1030page_flip_handler(int fd, unsigned int frame, 1031 unsigned int sec, unsigned int usec, void *data) 1032{ 1033 struct pipe_arg *pipe; 1034 unsigned int new_fb_id; 1035 struct timeval end; 1036 double t; 1037 1038 pipe = data; 1039 if (pipe->current_fb_id == pipe->fb_id[0]) 1040 new_fb_id = pipe->fb_id[1]; 1041 else 1042 new_fb_id = pipe->fb_id[0]; 1043 1044 drmModePageFlip(fd, pipe->crtc->crtc->crtc_id, new_fb_id, 1045 DRM_MODE_PAGE_FLIP_EVENT, pipe); 1046 pipe->current_fb_id = new_fb_id; 1047 pipe->swap_count++; 1048 if (pipe->swap_count == 60) { 1049 gettimeofday(&end, NULL); 1050 t = end.tv_sec + end.tv_usec * 1e-6 - 1051 (pipe->start.tv_sec + pipe->start.tv_usec * 1e-6); 1052 fprintf(stderr, "freq: %.02fHz\n", pipe->swap_count / t); 1053 pipe->swap_count = 0; 1054 pipe->start = end; 1055 } 1056} 1057 1058static bool format_support(const drmModePlanePtr ovr, uint32_t fmt) 1059{ 1060 unsigned int i; 1061 1062 for (i = 0; i < ovr->count_formats; ++i) { 1063 if (ovr->formats[i] == fmt) 1064 return true; 1065 } 1066 1067 return false; 1068} 1069 1070static void add_property(struct device *dev, uint32_t obj_id, 1071 const char *name, uint64_t value) 1072{ 1073 struct property_arg p; 1074 1075 p.obj_id = obj_id; 1076 strcpy(p.name, name); 1077 p.value = value; 1078 1079 set_property(dev, &p); 1080} 1081 1082static bool add_property_optional(struct device *dev, uint32_t obj_id, 1083 const char *name, uint64_t value) 1084{ 1085 struct property_arg p; 1086 1087 p.obj_id = obj_id; 1088 strcpy(p.name, name); 1089 p.value = value; 1090 p.optional = true; 1091 1092 return set_property(dev, &p); 1093} 1094 1095static void set_gamma(struct device *dev, unsigned crtc_id, unsigned fourcc) 1096{ 1097 unsigned blob_id = 0; 1098 /* TODO: support 1024-sized LUTs, when the use-case arises */ 1099 struct drm_color_lut gamma_lut[256]; 1100 int i, ret; 1101 1102 if (fourcc == DRM_FORMAT_C8) { 1103 /* TODO: Add C8 support for more patterns */ 1104 util_smpte_c8_gamma(256, gamma_lut); 1105 drmModeCreatePropertyBlob(dev->fd, gamma_lut, sizeof(gamma_lut), &blob_id); 1106 } else { 1107 for (i = 0; i < 256; i++) { 1108 gamma_lut[i].red = 1109 gamma_lut[i].green = 1110 gamma_lut[i].blue = i << 8; 1111 } 1112 } 1113 1114 add_property_optional(dev, crtc_id, "DEGAMMA_LUT", 0); 1115 add_property_optional(dev, crtc_id, "CTM", 0); 1116 if (!add_property_optional(dev, crtc_id, "GAMMA_LUT", blob_id)) { 1117 uint16_t r[256], g[256], b[256]; 1118 1119 for (i = 0; i < 256; i++) { 1120 r[i] = gamma_lut[i].red; 1121 g[i] = gamma_lut[i].green; 1122 b[i] = gamma_lut[i].blue; 1123 } 1124 1125 ret = drmModeCrtcSetGamma(dev->fd, crtc_id, 256, r, g, b); 1126 if (ret) 1127 fprintf(stderr, "failed to set gamma: %s\n", strerror(errno)); 1128 } 1129} 1130 1131static int atomic_set_plane(struct device *dev, struct plane_arg *p, 1132 int pattern, bool update) 1133{ 1134 uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0}; 1135 struct bo *plane_bo; 1136 int crtc_x, crtc_y, crtc_w, crtc_h; 1137 struct crtc *crtc = NULL; 1138 unsigned int i; 1139 unsigned int old_fb_id; 1140 1141 /* Find an unused plane which can be connected to our CRTC. Find the 1142 * CRTC index first, then iterate over available planes. 1143 */ 1144 for (i = 0; i < (unsigned int)dev->resources->res->count_crtcs; i++) { 1145 if (p->crtc_id == dev->resources->res->crtcs[i]) { 1146 crtc = &dev->resources->crtcs[i]; 1147 break; 1148 } 1149 } 1150 1151 if (!crtc) { 1152 fprintf(stderr, "CRTC %u not found\n", p->crtc_id); 1153 return -1; 1154 } 1155 1156 if (!update) 1157 fprintf(stderr, "testing %dx%d@%s on plane %u, crtc %u\n", 1158 p->w, p->h, p->format_str, p->plane_id, p->crtc_id); 1159 1160 plane_bo = p->old_bo; 1161 p->old_bo = p->bo; 1162 1163 if (!plane_bo) { 1164 plane_bo = bo_create(dev->fd, p->fourcc, p->w, p->h, 1165 handles, pitches, offsets, pattern); 1166 1167 if (plane_bo == NULL) 1168 return -1; 1169 1170 if (drmModeAddFB2(dev->fd, p->w, p->h, p->fourcc, 1171 handles, pitches, offsets, &p->fb_id, 0)) { 1172 fprintf(stderr, "failed to add fb: %s\n", strerror(errno)); 1173 return -1; 1174 } 1175 } 1176 1177 p->bo = plane_bo; 1178 1179 old_fb_id = p->fb_id; 1180 p->old_fb_id = old_fb_id; 1181 1182 crtc_w = p->w * p->scale; 1183 crtc_h = p->h * p->scale; 1184 if (!p->has_position) { 1185 /* Default to the middle of the screen */ 1186 crtc_x = (crtc->mode->hdisplay - crtc_w) / 2; 1187 crtc_y = (crtc->mode->vdisplay - crtc_h) / 2; 1188 } else { 1189 crtc_x = p->x; 1190 crtc_y = p->y; 1191 } 1192 1193 add_property(dev, p->plane_id, "FB_ID", p->fb_id); 1194 add_property(dev, p->plane_id, "CRTC_ID", p->crtc_id); 1195 add_property(dev, p->plane_id, "SRC_X", 0); 1196 add_property(dev, p->plane_id, "SRC_Y", 0); 1197 add_property(dev, p->plane_id, "SRC_W", p->w << 16); 1198 add_property(dev, p->plane_id, "SRC_H", p->h << 16); 1199 add_property(dev, p->plane_id, "CRTC_X", crtc_x); 1200 add_property(dev, p->plane_id, "CRTC_Y", crtc_y); 1201 add_property(dev, p->plane_id, "CRTC_W", crtc_w); 1202 add_property(dev, p->plane_id, "CRTC_H", crtc_h); 1203 1204 return 0; 1205} 1206 1207static int set_plane(struct device *dev, struct plane_arg *p) 1208{ 1209 drmModePlane *ovr; 1210 uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0}; 1211 uint32_t plane_id; 1212 struct bo *plane_bo; 1213 uint32_t plane_flags = 0; 1214 int crtc_x, crtc_y, crtc_w, crtc_h; 1215 struct crtc *crtc = NULL; 1216 unsigned int pipe; 1217 unsigned int i; 1218 1219 /* Find an unused plane which can be connected to our CRTC. Find the 1220 * CRTC index first, then iterate over available planes. 1221 */ 1222 for (i = 0; i < (unsigned int)dev->resources->res->count_crtcs; i++) { 1223 if (p->crtc_id == dev->resources->res->crtcs[i]) { 1224 crtc = &dev->resources->crtcs[i]; 1225 pipe = i; 1226 break; 1227 } 1228 } 1229 1230 if (!crtc) { 1231 fprintf(stderr, "CRTC %u not found\n", p->crtc_id); 1232 return -1; 1233 } 1234 1235 plane_id = p->plane_id; 1236 1237 for (i = 0; i < dev->resources->plane_res->count_planes; i++) { 1238 ovr = dev->resources->planes[i].plane; 1239 if (!ovr) 1240 continue; 1241 1242 if (plane_id && plane_id != ovr->plane_id) 1243 continue; 1244 1245 if (!format_support(ovr, p->fourcc)) 1246 continue; 1247 1248 if ((ovr->possible_crtcs & (1 << pipe)) && 1249 (ovr->crtc_id == 0 || ovr->crtc_id == p->crtc_id)) { 1250 plane_id = ovr->plane_id; 1251 break; 1252 } 1253 } 1254 1255 if (i == dev->resources->plane_res->count_planes) { 1256 fprintf(stderr, "no unused plane available for CRTC %u\n", 1257 crtc->crtc->crtc_id); 1258 return -1; 1259 } 1260 1261 fprintf(stderr, "testing %dx%d@%s overlay plane %u\n", 1262 p->w, p->h, p->format_str, plane_id); 1263 1264 plane_bo = bo_create(dev->fd, p->fourcc, p->w, p->h, handles, 1265 pitches, offsets, secondary_fill); 1266 if (plane_bo == NULL) 1267 return -1; 1268 1269 p->bo = plane_bo; 1270 1271 /* just use single plane format for now.. */ 1272 if (drmModeAddFB2(dev->fd, p->w, p->h, p->fourcc, 1273 handles, pitches, offsets, &p->fb_id, plane_flags)) { 1274 fprintf(stderr, "failed to add fb: %s\n", strerror(errno)); 1275 return -1; 1276 } 1277 1278 crtc_w = p->w * p->scale; 1279 crtc_h = p->h * p->scale; 1280 if (!p->has_position) { 1281 /* Default to the middle of the screen */ 1282 crtc_x = (crtc->mode->hdisplay - crtc_w) / 2; 1283 crtc_y = (crtc->mode->vdisplay - crtc_h) / 2; 1284 } else { 1285 crtc_x = p->x; 1286 crtc_y = p->y; 1287 } 1288 1289 /* note src coords (last 4 args) are in Q16 format */ 1290 if (drmModeSetPlane(dev->fd, plane_id, crtc->crtc->crtc_id, p->fb_id, 1291 plane_flags, crtc_x, crtc_y, crtc_w, crtc_h, 1292 0, 0, p->w << 16, p->h << 16)) { 1293 fprintf(stderr, "failed to enable plane: %s\n", 1294 strerror(errno)); 1295 return -1; 1296 } 1297 1298 ovr->crtc_id = crtc->crtc->crtc_id; 1299 1300 return 0; 1301} 1302 1303static void atomic_set_planes(struct device *dev, struct plane_arg *p, 1304 unsigned int count, bool update) 1305{ 1306 unsigned int i, pattern = primary_fill; 1307 1308 /* set up planes */ 1309 for (i = 0; i < count; i++) { 1310 if (i > 0) 1311 pattern = secondary_fill; 1312 else 1313 set_gamma(dev, p[i].crtc_id, p[i].fourcc); 1314 1315 if (atomic_set_plane(dev, &p[i], pattern, update)) 1316 return; 1317 } 1318} 1319 1320static void atomic_clear_planes(struct device *dev, struct plane_arg *p, unsigned int count) 1321{ 1322 unsigned int i; 1323 1324 for (i = 0; i < count; i++) { 1325 add_property(dev, p[i].plane_id, "FB_ID", 0); 1326 add_property(dev, p[i].plane_id, "CRTC_ID", 0); 1327 add_property(dev, p[i].plane_id, "SRC_X", 0); 1328 add_property(dev, p[i].plane_id, "SRC_Y", 0); 1329 add_property(dev, p[i].plane_id, "SRC_W", 0); 1330 add_property(dev, p[i].plane_id, "SRC_H", 0); 1331 add_property(dev, p[i].plane_id, "CRTC_X", 0); 1332 add_property(dev, p[i].plane_id, "CRTC_Y", 0); 1333 add_property(dev, p[i].plane_id, "CRTC_W", 0); 1334 add_property(dev, p[i].plane_id, "CRTC_H", 0); 1335 } 1336} 1337 1338static void atomic_clear_FB(struct device *dev, struct plane_arg *p, unsigned int count) 1339{ 1340 unsigned int i; 1341 1342 for (i = 0; i < count; i++) { 1343 if (p[i].fb_id) { 1344 drmModeRmFB(dev->fd, p[i].fb_id); 1345 p[i].fb_id = 0; 1346 } 1347 if (p[i].old_fb_id) { 1348 drmModeRmFB(dev->fd, p[i].old_fb_id); 1349 p[i].old_fb_id = 0; 1350 } 1351 if (p[i].bo) { 1352 bo_destroy(p[i].bo); 1353 p[i].bo = NULL; 1354 } 1355 if (p[i].old_bo) { 1356 bo_destroy(p[i].old_bo); 1357 p[i].old_bo = NULL; 1358 } 1359 1360 } 1361} 1362 1363static void clear_planes(struct device *dev, struct plane_arg *p, unsigned int count) 1364{ 1365 unsigned int i; 1366 1367 for (i = 0; i < count; i++) { 1368 if (p[i].fb_id) 1369 drmModeRmFB(dev->fd, p[i].fb_id); 1370 if (p[i].bo) 1371 bo_destroy(p[i].bo); 1372 } 1373} 1374 1375static void atomic_set_mode(struct device *dev, struct pipe_arg *pipes, unsigned int count) 1376{ 1377 unsigned int i; 1378 unsigned int j; 1379 int ret; 1380 1381 for (i = 0; i < count; i++) { 1382 struct pipe_arg *pipe = &pipes[i]; 1383 1384 ret = pipe_find_crtc_and_mode(dev, pipe); 1385 if (ret < 0) 1386 continue; 1387 } 1388 1389 for (i = 0; i < count; i++) { 1390 struct pipe_arg *pipe = &pipes[i]; 1391 uint32_t blob_id; 1392 1393 if (pipe->mode == NULL) 1394 continue; 1395 1396 printf("setting mode %s-%dHz on connectors ", 1397 pipe->mode_str, pipe->mode->vrefresh); 1398 for (j = 0; j < pipe->num_cons; ++j) { 1399 printf("%s, ", pipe->cons[j]); 1400 add_property(dev, pipe->con_ids[j], "CRTC_ID", pipe->crtc->crtc->crtc_id); 1401 } 1402 printf("crtc %d\n", pipe->crtc->crtc->crtc_id); 1403 1404 drmModeCreatePropertyBlob(dev->fd, pipe->mode, sizeof(*pipe->mode), &blob_id); 1405 add_property(dev, pipe->crtc->crtc->crtc_id, "MODE_ID", blob_id); 1406 add_property(dev, pipe->crtc->crtc->crtc_id, "ACTIVE", 1); 1407 } 1408} 1409 1410static void atomic_clear_mode(struct device *dev, struct pipe_arg *pipes, unsigned int count) 1411{ 1412 unsigned int i; 1413 unsigned int j; 1414 1415 for (i = 0; i < count; i++) { 1416 struct pipe_arg *pipe = &pipes[i]; 1417 1418 if (pipe->mode == NULL) 1419 continue; 1420 1421 for (j = 0; j < pipe->num_cons; ++j) 1422 add_property(dev, pipe->con_ids[j], "CRTC_ID",0); 1423 1424 add_property(dev, pipe->crtc->crtc->crtc_id, "MODE_ID", 0); 1425 add_property(dev, pipe->crtc->crtc->crtc_id, "ACTIVE", 0); 1426 } 1427} 1428 1429static void set_mode(struct device *dev, struct pipe_arg *pipes, unsigned int count) 1430{ 1431 uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0}; 1432 unsigned int fb_id; 1433 struct bo *bo; 1434 unsigned int i; 1435 unsigned int j; 1436 int ret, x; 1437 1438 dev->mode.width = 0; 1439 dev->mode.height = 0; 1440 dev->mode.fb_id = 0; 1441 1442 for (i = 0; i < count; i++) { 1443 struct pipe_arg *pipe = &pipes[i]; 1444 1445 ret = pipe_find_crtc_and_mode(dev, pipe); 1446 if (ret < 0) 1447 continue; 1448 1449 dev->mode.width += pipe->mode->hdisplay; 1450 if (dev->mode.height < pipe->mode->vdisplay) 1451 dev->mode.height = pipe->mode->vdisplay; 1452 } 1453 1454 bo = bo_create(dev->fd, pipes[0].fourcc, dev->mode.width, 1455 dev->mode.height, handles, pitches, offsets, 1456 primary_fill); 1457 if (bo == NULL) 1458 return; 1459 1460 dev->mode.bo = bo; 1461 1462 ret = drmModeAddFB2(dev->fd, dev->mode.width, dev->mode.height, 1463 pipes[0].fourcc, handles, pitches, offsets, &fb_id, 0); 1464 if (ret) { 1465 fprintf(stderr, "failed to add fb (%ux%u): %s\n", 1466 dev->mode.width, dev->mode.height, strerror(errno)); 1467 return; 1468 } 1469 1470 dev->mode.fb_id = fb_id; 1471 1472 x = 0; 1473 for (i = 0; i < count; i++) { 1474 struct pipe_arg *pipe = &pipes[i]; 1475 1476 if (pipe->mode == NULL) 1477 continue; 1478 1479 printf("setting mode %s-%dHz@%s on connectors ", 1480 pipe->mode_str, pipe->mode->vrefresh, pipe->format_str); 1481 for (j = 0; j < pipe->num_cons; ++j) 1482 printf("%s, ", pipe->cons[j]); 1483 printf("crtc %d\n", pipe->crtc->crtc->crtc_id); 1484 1485 ret = drmModeSetCrtc(dev->fd, pipe->crtc->crtc->crtc_id, fb_id, 1486 x, 0, pipe->con_ids, pipe->num_cons, 1487 pipe->mode); 1488 1489 /* XXX: Actually check if this is needed */ 1490 drmModeDirtyFB(dev->fd, fb_id, NULL, 0); 1491 1492 x += pipe->mode->hdisplay; 1493 1494 if (ret) { 1495 fprintf(stderr, "failed to set mode: %s\n", strerror(errno)); 1496 return; 1497 } 1498 1499 set_gamma(dev, pipe->crtc->crtc->crtc_id, pipe->fourcc); 1500 } 1501} 1502 1503static void clear_mode(struct device *dev) 1504{ 1505 if (dev->mode.fb_id) 1506 drmModeRmFB(dev->fd, dev->mode.fb_id); 1507 if (dev->mode.bo) 1508 bo_destroy(dev->mode.bo); 1509} 1510 1511static void set_planes(struct device *dev, struct plane_arg *p, unsigned int count) 1512{ 1513 unsigned int i; 1514 1515 /* set up planes/overlays */ 1516 for (i = 0; i < count; i++) 1517 if (set_plane(dev, &p[i])) 1518 return; 1519} 1520 1521static void set_cursors(struct device *dev, struct pipe_arg *pipes, unsigned int count) 1522{ 1523 uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0}; 1524 struct bo *bo; 1525 unsigned int i; 1526 int ret; 1527 1528 /* maybe make cursor width/height configurable some day */ 1529 uint32_t cw = 64; 1530 uint32_t ch = 64; 1531 1532 /* create cursor bo.. just using PATTERN_PLAIN as it has 1533 * translucent alpha 1534 */ 1535 bo = bo_create(dev->fd, DRM_FORMAT_ARGB8888, cw, ch, handles, pitches, 1536 offsets, UTIL_PATTERN_PLAIN); 1537 if (bo == NULL) 1538 return; 1539 1540 dev->mode.cursor_bo = bo; 1541 1542 for (i = 0; i < count; i++) { 1543 struct pipe_arg *pipe = &pipes[i]; 1544 ret = cursor_init(dev->fd, handles[0], 1545 pipe->crtc->crtc->crtc_id, 1546 pipe->mode->hdisplay, pipe->mode->vdisplay, 1547 cw, ch); 1548 if (ret) { 1549 fprintf(stderr, "failed to init cursor for CRTC[%u]\n", 1550 pipe->crtc_id); 1551 return; 1552 } 1553 } 1554 1555 cursor_start(); 1556} 1557 1558static void clear_cursors(struct device *dev) 1559{ 1560 cursor_stop(); 1561 1562 if (dev->mode.cursor_bo) 1563 bo_destroy(dev->mode.cursor_bo); 1564} 1565 1566static void test_page_flip(struct device *dev, struct pipe_arg *pipes, unsigned int count) 1567{ 1568 uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0}; 1569 unsigned int other_fb_id; 1570 struct bo *other_bo; 1571 drmEventContext evctx; 1572 unsigned int i; 1573 int ret; 1574 1575 other_bo = bo_create(dev->fd, pipes[0].fourcc, dev->mode.width, 1576 dev->mode.height, handles, pitches, offsets, 1577 UTIL_PATTERN_PLAIN); 1578 if (other_bo == NULL) 1579 return; 1580 1581 ret = drmModeAddFB2(dev->fd, dev->mode.width, dev->mode.height, 1582 pipes[0].fourcc, handles, pitches, offsets, 1583 &other_fb_id, 0); 1584 if (ret) { 1585 fprintf(stderr, "failed to add fb: %s\n", strerror(errno)); 1586 goto err; 1587 } 1588 1589 for (i = 0; i < count; i++) { 1590 struct pipe_arg *pipe = &pipes[i]; 1591 1592 if (pipe->mode == NULL) 1593 continue; 1594 1595 ret = drmModePageFlip(dev->fd, pipe->crtc->crtc->crtc_id, 1596 other_fb_id, DRM_MODE_PAGE_FLIP_EVENT, 1597 pipe); 1598 if (ret) { 1599 fprintf(stderr, "failed to page flip: %s\n", strerror(errno)); 1600 goto err_rmfb; 1601 } 1602 gettimeofday(&pipe->start, NULL); 1603 pipe->swap_count = 0; 1604 pipe->fb_id[0] = dev->mode.fb_id; 1605 pipe->fb_id[1] = other_fb_id; 1606 pipe->current_fb_id = other_fb_id; 1607 } 1608 1609 memset(&evctx, 0, sizeof evctx); 1610 evctx.version = DRM_EVENT_CONTEXT_VERSION; 1611 evctx.vblank_handler = NULL; 1612 evctx.page_flip_handler = page_flip_handler; 1613 1614 while (1) { 1615#if 0 1616 struct pollfd pfd[2]; 1617 1618 pfd[0].fd = 0; 1619 pfd[0].events = POLLIN; 1620 pfd[1].fd = fd; 1621 pfd[1].events = POLLIN; 1622 1623 if (poll(pfd, 2, -1) < 0) { 1624 fprintf(stderr, "poll error\n"); 1625 break; 1626 } 1627 1628 if (pfd[0].revents) 1629 break; 1630#else 1631 struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 }; 1632 fd_set fds; 1633 1634 FD_ZERO(&fds); 1635 FD_SET(0, &fds); 1636 FD_SET(dev->fd, &fds); 1637 ret = select(dev->fd + 1, &fds, NULL, NULL, &timeout); 1638 1639 if (ret <= 0) { 1640 fprintf(stderr, "select timed out or error (ret %d)\n", 1641 ret); 1642 continue; 1643 } else if (FD_ISSET(0, &fds)) { 1644 break; 1645 } 1646#endif 1647 1648 drmHandleEvent(dev->fd, &evctx); 1649 } 1650 1651err_rmfb: 1652 drmModeRmFB(dev->fd, other_fb_id); 1653err: 1654 bo_destroy(other_bo); 1655} 1656 1657#define min(a, b) ((a) < (b) ? (a) : (b)) 1658 1659static int parse_connector(struct pipe_arg *pipe, const char *arg) 1660{ 1661 unsigned int len; 1662 unsigned int i; 1663 const char *p; 1664 char *endp; 1665 1666 pipe->vrefresh = 0; 1667 pipe->crtc_id = (uint32_t)-1; 1668 strcpy(pipe->format_str, "XR24"); 1669 1670 /* Count the number of connectors and allocate them. */ 1671 pipe->num_cons = 1; 1672 for (p = arg; *p && *p != ':' && *p != '@'; ++p) { 1673 if (*p == ',') 1674 pipe->num_cons++; 1675 } 1676 1677 pipe->con_ids = calloc(pipe->num_cons, sizeof(*pipe->con_ids)); 1678 pipe->cons = calloc(pipe->num_cons, sizeof(*pipe->cons)); 1679 if (pipe->con_ids == NULL || pipe->cons == NULL) 1680 return -1; 1681 1682 /* Parse the connectors. */ 1683 for (i = 0, p = arg; i < pipe->num_cons; ++i, p = endp + 1) { 1684 endp = strpbrk(p, ",@:"); 1685 if (!endp) 1686 break; 1687 1688 pipe->cons[i] = strndup(p, endp - p); 1689 1690 if (*endp != ',') 1691 break; 1692 } 1693 1694 if (i != pipe->num_cons - 1) 1695 return -1; 1696 1697 /* Parse the remaining parameters. */ 1698 if (*endp == '@') { 1699 arg = endp + 1; 1700 pipe->crtc_id = strtoul(arg, &endp, 10); 1701 } 1702 if (*endp != ':') 1703 return -1; 1704 1705 arg = endp + 1; 1706 1707 /* Search for the vertical refresh or the format. */ 1708 p = strpbrk(arg, "-@"); 1709 if (p == NULL) 1710 p = arg + strlen(arg); 1711 len = min(sizeof pipe->mode_str - 1, (unsigned int)(p - arg)); 1712 strncpy(pipe->mode_str, arg, len); 1713 pipe->mode_str[len] = '\0'; 1714 1715 if (*p == '-') { 1716 pipe->vrefresh = strtoul(p + 1, &endp, 10); 1717 p = endp; 1718 } 1719 1720 if (*p == '@') { 1721 strncpy(pipe->format_str, p + 1, 4); 1722 pipe->format_str[4] = '\0'; 1723 } 1724 1725 pipe->fourcc = util_format_fourcc(pipe->format_str); 1726 if (pipe->fourcc == 0) { 1727 fprintf(stderr, "unknown format %s\n", pipe->format_str); 1728 return -1; 1729 } 1730 1731 return 0; 1732} 1733 1734static int parse_plane(struct plane_arg *plane, const char *p) 1735{ 1736 char *end; 1737 1738 plane->plane_id = strtoul(p, &end, 10); 1739 if (*end != '@') 1740 return -EINVAL; 1741 1742 p = end + 1; 1743 plane->crtc_id = strtoul(p, &end, 10); 1744 if (*end != ':') 1745 return -EINVAL; 1746 1747 p = end + 1; 1748 plane->w = strtoul(p, &end, 10); 1749 if (*end != 'x') 1750 return -EINVAL; 1751 1752 p = end + 1; 1753 plane->h = strtoul(p, &end, 10); 1754 1755 if (*end == '+' || *end == '-') { 1756 plane->x = strtol(end, &end, 10); 1757 if (*end != '+' && *end != '-') 1758 return -EINVAL; 1759 plane->y = strtol(end, &end, 10); 1760 1761 plane->has_position = true; 1762 } 1763 1764 if (*end == '*') { 1765 p = end + 1; 1766 plane->scale = strtod(p, &end); 1767 if (plane->scale <= 0.0) 1768 return -EINVAL; 1769 } else { 1770 plane->scale = 1.0; 1771 } 1772 1773 if (*end == '@') { 1774 strncpy(plane->format_str, end + 1, 4); 1775 plane->format_str[4] = '\0'; 1776 } else { 1777 strcpy(plane->format_str, "XR24"); 1778 } 1779 1780 plane->fourcc = util_format_fourcc(plane->format_str); 1781 if (plane->fourcc == 0) { 1782 fprintf(stderr, "unknown format %s\n", plane->format_str); 1783 return -EINVAL; 1784 } 1785 1786 return 0; 1787} 1788 1789static int parse_property(struct property_arg *p, const char *arg) 1790{ 1791 if (sscanf(arg, "%d:%32[^:]:%" SCNu64, &p->obj_id, p->name, &p->value) != 3) 1792 return -1; 1793 1794 p->obj_type = 0; 1795 p->name[DRM_PROP_NAME_LEN] = '\0'; 1796 1797 return 0; 1798} 1799 1800static void parse_fill_patterns(char *arg) 1801{ 1802 char *fill = strtok(arg, ","); 1803 if (!fill) 1804 return; 1805 primary_fill = util_pattern_enum(fill); 1806 fill = strtok(NULL, ","); 1807 if (!fill) 1808 return; 1809 secondary_fill = util_pattern_enum(fill); 1810} 1811 1812static void usage(char *name) 1813{ 1814 fprintf(stderr, "usage: %s [-acDdefMPpsCvw]\n", name); 1815 1816 fprintf(stderr, "\n Query options:\n\n"); 1817 fprintf(stderr, "\t-c\tlist connectors\n"); 1818 fprintf(stderr, "\t-e\tlist encoders\n"); 1819 fprintf(stderr, "\t-f\tlist framebuffers\n"); 1820 fprintf(stderr, "\t-p\tlist CRTCs and planes (pipes)\n"); 1821 1822 fprintf(stderr, "\n Test options:\n\n"); 1823 fprintf(stderr, "\t-P <plane_id>@<crtc_id>:<w>x<h>[+<x>+<y>][*<scale>][@<format>]\tset a plane\n"); 1824 fprintf(stderr, "\t-s <connector_id>[,<connector_id>][@<crtc_id>]:<mode>[-<vrefresh>][@<format>]\tset a mode\n"); 1825 fprintf(stderr, "\t-C\ttest hw cursor\n"); 1826 fprintf(stderr, "\t-v\ttest vsynced page flipping\n"); 1827 fprintf(stderr, "\t-w <obj_id>:<prop_name>:<value>\tset property\n"); 1828 fprintf(stderr, "\t-a \tuse atomic API\n"); 1829 fprintf(stderr, "\t-F pattern1,pattern2\tspecify fill patterns\n"); 1830 1831 fprintf(stderr, "\n Generic options:\n\n"); 1832 fprintf(stderr, "\t-d\tdrop master after mode set\n"); 1833 fprintf(stderr, "\t-M module\tuse the given driver\n"); 1834 fprintf(stderr, "\t-D device\tuse the given device\n"); 1835 1836 fprintf(stderr, "\n\tDefault is to dump all info.\n"); 1837 exit(0); 1838} 1839 1840static int page_flipping_supported(void) 1841{ 1842 /*FIXME: generic ioctl needed? */ 1843 return 1; 1844#if 0 1845 int ret, value; 1846 struct drm_i915_getparam gp; 1847 1848 gp.param = I915_PARAM_HAS_PAGEFLIPPING; 1849 gp.value = &value; 1850 1851 ret = drmCommandWriteRead(fd, DRM_I915_GETPARAM, &gp, sizeof(gp)); 1852 if (ret) { 1853 fprintf(stderr, "drm_i915_getparam: %m\n"); 1854 return 0; 1855 } 1856 1857 return *gp.value; 1858#endif 1859} 1860 1861static int cursor_supported(void) 1862{ 1863 /*FIXME: generic ioctl needed? */ 1864 return 1; 1865} 1866 1867static int pipe_resolve_connectors(struct device *dev, struct pipe_arg *pipe) 1868{ 1869 drmModeConnector *connector; 1870 unsigned int i; 1871 uint32_t id; 1872 char *endp; 1873 1874 for (i = 0; i < pipe->num_cons; i++) { 1875 id = strtoul(pipe->cons[i], &endp, 10); 1876 if (endp == pipe->cons[i]) { 1877 connector = get_connector_by_name(dev, pipe->cons[i]); 1878 if (!connector) { 1879 fprintf(stderr, "no connector named '%s'\n", 1880 pipe->cons[i]); 1881 return -ENODEV; 1882 } 1883 1884 id = connector->connector_id; 1885 } 1886 1887 pipe->con_ids[i] = id; 1888 } 1889 1890 return 0; 1891} 1892 1893static char optstr[] = "acdD:efF:M:P:ps:Cvw:"; 1894 1895int main(int argc, char **argv) 1896{ 1897 struct device dev; 1898 1899 int c; 1900 int encoders = 0, connectors = 0, crtcs = 0, planes = 0, framebuffers = 0; 1901 int drop_master = 0; 1902 int test_vsync = 0; 1903 int test_cursor = 0; 1904 int use_atomic = 0; 1905 char *device = NULL; 1906 char *module = NULL; 1907 unsigned int i; 1908 unsigned int count = 0, plane_count = 0; 1909 unsigned int prop_count = 0; 1910 struct pipe_arg *pipe_args = NULL; 1911 struct plane_arg *plane_args = NULL; 1912 struct property_arg *prop_args = NULL; 1913 unsigned int args = 0; 1914 int ret; 1915 1916 memset(&dev, 0, sizeof dev); 1917 1918 opterr = 0; 1919 while ((c = getopt(argc, argv, optstr)) != -1) { 1920 args++; 1921 1922 switch (c) { 1923 case 'a': 1924 use_atomic = 1; 1925 break; 1926 case 'c': 1927 connectors = 1; 1928 break; 1929 case 'D': 1930 device = optarg; 1931 args--; 1932 break; 1933 case 'd': 1934 drop_master = 1; 1935 break; 1936 case 'e': 1937 encoders = 1; 1938 break; 1939 case 'f': 1940 framebuffers = 1; 1941 break; 1942 case 'F': 1943 parse_fill_patterns(optarg); 1944 break; 1945 case 'M': 1946 module = optarg; 1947 /* Preserve the default behaviour of dumping all information. */ 1948 args--; 1949 break; 1950 case 'P': 1951 plane_args = realloc(plane_args, 1952 (plane_count + 1) * sizeof *plane_args); 1953 if (plane_args == NULL) { 1954 fprintf(stderr, "memory allocation failed\n"); 1955 return 1; 1956 } 1957 memset(&plane_args[plane_count], 0, sizeof(*plane_args)); 1958 1959 if (parse_plane(&plane_args[plane_count], optarg) < 0) 1960 usage(argv[0]); 1961 1962 plane_count++; 1963 break; 1964 case 'p': 1965 crtcs = 1; 1966 planes = 1; 1967 break; 1968 case 's': 1969 pipe_args = realloc(pipe_args, 1970 (count + 1) * sizeof *pipe_args); 1971 if (pipe_args == NULL) { 1972 fprintf(stderr, "memory allocation failed\n"); 1973 return 1; 1974 } 1975 memset(&pipe_args[count], 0, sizeof(*pipe_args)); 1976 1977 if (parse_connector(&pipe_args[count], optarg) < 0) 1978 usage(argv[0]); 1979 1980 count++; 1981 break; 1982 case 'C': 1983 test_cursor = 1; 1984 break; 1985 case 'v': 1986 test_vsync = 1; 1987 break; 1988 case 'w': 1989 prop_args = realloc(prop_args, 1990 (prop_count + 1) * sizeof *prop_args); 1991 if (prop_args == NULL) { 1992 fprintf(stderr, "memory allocation failed\n"); 1993 return 1; 1994 } 1995 memset(&prop_args[prop_count], 0, sizeof(*prop_args)); 1996 1997 if (parse_property(&prop_args[prop_count], optarg) < 0) 1998 usage(argv[0]); 1999 2000 prop_count++; 2001 break; 2002 default: 2003 usage(argv[0]); 2004 break; 2005 } 2006 } 2007 2008 if (!args || (args == 1 && use_atomic)) 2009 encoders = connectors = crtcs = planes = framebuffers = 1; 2010 2011 dev.fd = util_open(device, module); 2012 if (dev.fd < 0) 2013 return -1; 2014 2015 ret = drmSetClientCap(dev.fd, DRM_CLIENT_CAP_ATOMIC, 1); 2016 if (ret && use_atomic) { 2017 fprintf(stderr, "no atomic modesetting support: %s\n", strerror(errno)); 2018 drmClose(dev.fd); 2019 return -1; 2020 } 2021 2022 dev.use_atomic = use_atomic; 2023 2024 if (test_vsync && !page_flipping_supported()) { 2025 fprintf(stderr, "page flipping not supported by drm.\n"); 2026 return -1; 2027 } 2028 2029 if (test_vsync && !count) { 2030 fprintf(stderr, "page flipping requires at least one -s option.\n"); 2031 return -1; 2032 } 2033 2034 if (test_cursor && !cursor_supported()) { 2035 fprintf(stderr, "hw cursor not supported by drm.\n"); 2036 return -1; 2037 } 2038 2039 dev.resources = get_resources(&dev); 2040 if (!dev.resources) { 2041 drmClose(dev.fd); 2042 return 1; 2043 } 2044 2045 for (i = 0; i < count; i++) { 2046 if (pipe_resolve_connectors(&dev, &pipe_args[i]) < 0) { 2047 free_resources(dev.resources); 2048 drmClose(dev.fd); 2049 return 1; 2050 } 2051 } 2052 2053#define dump_resource(dev, res) if (res) dump_##res(dev) 2054 2055 dump_resource(&dev, encoders); 2056 dump_resource(&dev, connectors); 2057 dump_resource(&dev, crtcs); 2058 dump_resource(&dev, planes); 2059 dump_resource(&dev, framebuffers); 2060 2061 for (i = 0; i < prop_count; ++i) 2062 set_property(&dev, &prop_args[i]); 2063 2064 if (dev.use_atomic) { 2065 dev.req = drmModeAtomicAlloc(); 2066 2067 if (count && plane_count) { 2068 uint64_t cap = 0; 2069 2070 ret = drmGetCap(dev.fd, DRM_CAP_DUMB_BUFFER, &cap); 2071 if (ret || cap == 0) { 2072 fprintf(stderr, "driver doesn't support the dumb buffer API\n"); 2073 return 1; 2074 } 2075 2076 atomic_set_mode(&dev, pipe_args, count); 2077 atomic_set_planes(&dev, plane_args, plane_count, false); 2078 2079 ret = drmModeAtomicCommit(dev.fd, dev.req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); 2080 if (ret) { 2081 fprintf(stderr, "Atomic Commit failed [1]\n"); 2082 return 1; 2083 } 2084 2085 gettimeofday(&pipe_args->start, NULL); 2086 pipe_args->swap_count = 0; 2087 2088 while (test_vsync) { 2089 drmModeAtomicFree(dev.req); 2090 dev.req = drmModeAtomicAlloc(); 2091 atomic_set_planes(&dev, plane_args, plane_count, true); 2092 2093 ret = drmModeAtomicCommit(dev.fd, dev.req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); 2094 if (ret) { 2095 fprintf(stderr, "Atomic Commit failed [2]\n"); 2096 return 1; 2097 } 2098 2099 pipe_args->swap_count++; 2100 if (pipe_args->swap_count == 60) { 2101 struct timeval end; 2102 double t; 2103 2104 gettimeofday(&end, NULL); 2105 t = end.tv_sec + end.tv_usec * 1e-6 - 2106 (pipe_args->start.tv_sec + pipe_args->start.tv_usec * 1e-6); 2107 fprintf(stderr, "freq: %.02fHz\n", pipe_args->swap_count / t); 2108 pipe_args->swap_count = 0; 2109 pipe_args->start = end; 2110 } 2111 } 2112 2113 if (drop_master) 2114 drmDropMaster(dev.fd); 2115 2116 getchar(); 2117 2118 drmModeAtomicFree(dev.req); 2119 dev.req = drmModeAtomicAlloc(); 2120 2121 atomic_clear_mode(&dev, pipe_args, count); 2122 atomic_clear_planes(&dev, plane_args, plane_count); 2123 ret = drmModeAtomicCommit(dev.fd, dev.req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); 2124 if (ret) { 2125 fprintf(stderr, "Atomic Commit failed\n"); 2126 return 1; 2127 } 2128 2129 atomic_clear_FB(&dev, plane_args, plane_count); 2130 } 2131 2132 drmModeAtomicFree(dev.req); 2133 } else { 2134 if (count || plane_count) { 2135 uint64_t cap = 0; 2136 2137 ret = drmGetCap(dev.fd, DRM_CAP_DUMB_BUFFER, &cap); 2138 if (ret || cap == 0) { 2139 fprintf(stderr, "driver doesn't support the dumb buffer API\n"); 2140 return 1; 2141 } 2142 2143 if (count) 2144 set_mode(&dev, pipe_args, count); 2145 2146 if (plane_count) 2147 set_planes(&dev, plane_args, plane_count); 2148 2149 if (test_cursor) 2150 set_cursors(&dev, pipe_args, count); 2151 2152 if (test_vsync) 2153 test_page_flip(&dev, pipe_args, count); 2154 2155 if (drop_master) 2156 drmDropMaster(dev.fd); 2157 2158 getchar(); 2159 2160 if (test_cursor) 2161 clear_cursors(&dev); 2162 2163 if (plane_count) 2164 clear_planes(&dev, plane_args, plane_count); 2165 2166 if (count) 2167 clear_mode(&dev); 2168 } 2169 } 2170 2171 free_resources(dev.resources); 2172 2173 return 0; 2174} 2175