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