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