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