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