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