modetest.c revision 605efd7e05e94b8d9d742d3a8af1040776e2742d
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#include "config.h" 41 42#include <assert.h> 43#include <stdbool.h> 44#include <stdio.h> 45#include <stdlib.h> 46#include <stdint.h> 47#include <inttypes.h> 48#include <unistd.h> 49#include <string.h> 50#include <errno.h> 51#include <sys/poll.h> 52#include <sys/time.h> 53 54#include "xf86drm.h" 55#include "xf86drmMode.h" 56#include "drm_fourcc.h" 57#include "libkms.h" 58 59#include "buffers.h" 60 61struct crtc { 62 drmModeCrtc *crtc; 63 drmModeObjectProperties *props; 64 drmModePropertyRes **props_info; 65}; 66 67struct encoder { 68 drmModeEncoder *encoder; 69}; 70 71struct connector { 72 drmModeConnector *connector; 73 drmModeObjectProperties *props; 74 drmModePropertyRes **props_info; 75}; 76 77struct fb { 78 drmModeFB *fb; 79}; 80 81struct plane { 82 drmModePlane *plane; 83 drmModeObjectProperties *props; 84 drmModePropertyRes **props_info; 85}; 86 87struct resources { 88 drmModeRes *res; 89 drmModePlaneRes *plane_res; 90 91 struct crtc *crtcs; 92 struct encoder *encoders; 93 struct connector *connectors; 94 struct fb *fbs; 95 struct plane *planes; 96}; 97 98struct device { 99 int fd; 100 101 struct resources *resources; 102 struct kms_driver *kms; 103}; 104 105#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) 106 107struct type_name { 108 int type; 109 const char *name; 110}; 111 112#define type_name_fn(res) \ 113const char * res##_str(int type) { \ 114 unsigned int i; \ 115 for (i = 0; i < ARRAY_SIZE(res##_names); i++) { \ 116 if (res##_names[i].type == type) \ 117 return res##_names[i].name; \ 118 } \ 119 return "(invalid)"; \ 120} 121 122struct type_name encoder_type_names[] = { 123 { DRM_MODE_ENCODER_NONE, "none" }, 124 { DRM_MODE_ENCODER_DAC, "DAC" }, 125 { DRM_MODE_ENCODER_TMDS, "TMDS" }, 126 { DRM_MODE_ENCODER_LVDS, "LVDS" }, 127 { DRM_MODE_ENCODER_TVDAC, "TVDAC" }, 128}; 129 130static type_name_fn(encoder_type) 131 132struct type_name connector_status_names[] = { 133 { DRM_MODE_CONNECTED, "connected" }, 134 { DRM_MODE_DISCONNECTED, "disconnected" }, 135 { DRM_MODE_UNKNOWNCONNECTION, "unknown" }, 136}; 137 138static type_name_fn(connector_status) 139 140struct type_name connector_type_names[] = { 141 { DRM_MODE_CONNECTOR_Unknown, "unknown" }, 142 { DRM_MODE_CONNECTOR_VGA, "VGA" }, 143 { DRM_MODE_CONNECTOR_DVII, "DVI-I" }, 144 { DRM_MODE_CONNECTOR_DVID, "DVI-D" }, 145 { DRM_MODE_CONNECTOR_DVIA, "DVI-A" }, 146 { DRM_MODE_CONNECTOR_Composite, "composite" }, 147 { DRM_MODE_CONNECTOR_SVIDEO, "s-video" }, 148 { DRM_MODE_CONNECTOR_LVDS, "LVDS" }, 149 { DRM_MODE_CONNECTOR_Component, "component" }, 150 { DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN" }, 151 { DRM_MODE_CONNECTOR_DisplayPort, "DP" }, 152 { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" }, 153 { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" }, 154 { DRM_MODE_CONNECTOR_TV, "TV" }, 155 { DRM_MODE_CONNECTOR_eDP, "eDP" }, 156}; 157 158static type_name_fn(connector_type) 159 160#define bit_name_fn(res) \ 161const char * res##_str(int type) { \ 162 unsigned int i; \ 163 const char *sep = ""; \ 164 for (i = 0; i < ARRAY_SIZE(res##_names); i++) { \ 165 if (type & (1 << i)) { \ 166 printf("%s%s", sep, res##_names[i]); \ 167 sep = ", "; \ 168 } \ 169 } \ 170 return NULL; \ 171} 172 173static const char *mode_type_names[] = { 174 "builtin", 175 "clock_c", 176 "crtc_c", 177 "preferred", 178 "default", 179 "userdef", 180 "driver", 181}; 182 183static bit_name_fn(mode_type) 184 185static const char *mode_flag_names[] = { 186 "phsync", 187 "nhsync", 188 "pvsync", 189 "nvsync", 190 "interlace", 191 "dblscan", 192 "csync", 193 "pcsync", 194 "ncsync", 195 "hskew", 196 "bcast", 197 "pixmux", 198 "dblclk", 199 "clkdiv2" 200}; 201 202static bit_name_fn(mode_flag) 203 204static void dump_encoders(struct device *dev) 205{ 206 drmModeEncoder *encoder; 207 int i; 208 209 printf("Encoders:\n"); 210 printf("id\tcrtc\ttype\tpossible crtcs\tpossible clones\t\n"); 211 for (i = 0; i < dev->resources->res->count_encoders; i++) { 212 encoder = dev->resources->encoders[i].encoder; 213 if (!encoder) 214 continue; 215 216 printf("%d\t%d\t%s\t0x%08x\t0x%08x\n", 217 encoder->encoder_id, 218 encoder->crtc_id, 219 encoder_type_str(encoder->encoder_type), 220 encoder->possible_crtcs, 221 encoder->possible_clones); 222 } 223 printf("\n"); 224} 225 226static void dump_mode(drmModeModeInfo *mode) 227{ 228 printf(" %s %d %d %d %d %d %d %d %d %d", 229 mode->name, 230 mode->vrefresh, 231 mode->hdisplay, 232 mode->hsync_start, 233 mode->hsync_end, 234 mode->htotal, 235 mode->vdisplay, 236 mode->vsync_start, 237 mode->vsync_end, 238 mode->vtotal); 239 240 printf(" flags: "); 241 mode_flag_str(mode->flags); 242 printf("; type: "); 243 mode_type_str(mode->type); 244 printf("\n"); 245} 246 247static void dump_blob(struct device *dev, uint32_t blob_id) 248{ 249 uint32_t i; 250 unsigned char *blob_data; 251 drmModePropertyBlobPtr blob; 252 253 blob = drmModeGetPropertyBlob(dev->fd, blob_id); 254 if (!blob) 255 return; 256 257 blob_data = blob->data; 258 259 for (i = 0; i < blob->length; i++) { 260 if (i % 16 == 0) 261 printf("\n\t\t\t"); 262 printf("%.2hhx", blob_data[i]); 263 } 264 printf("\n"); 265 266 drmModeFreePropertyBlob(blob); 267} 268 269static void dump_prop(struct device *dev, drmModePropertyPtr prop, 270 uint32_t prop_id, uint64_t value) 271{ 272 int i; 273 printf("\t%d", prop_id); 274 if (!prop) { 275 printf("\n"); 276 return; 277 } 278 279 printf(" %s:\n", prop->name); 280 281 printf("\t\tflags:"); 282 if (prop->flags & DRM_MODE_PROP_PENDING) 283 printf(" pending"); 284 if (prop->flags & DRM_MODE_PROP_RANGE) 285 printf(" range"); 286 if (prop->flags & DRM_MODE_PROP_IMMUTABLE) 287 printf(" immutable"); 288 if (prop->flags & DRM_MODE_PROP_ENUM) 289 printf(" enum"); 290 if (prop->flags & DRM_MODE_PROP_BITMASK) 291 printf(" bitmask"); 292 if (prop->flags & DRM_MODE_PROP_BLOB) 293 printf(" blob"); 294 printf("\n"); 295 296 if (prop->flags & DRM_MODE_PROP_RANGE) { 297 printf("\t\tvalues:"); 298 for (i = 0; i < prop->count_values; i++) 299 printf(" %"PRIu64, prop->values[i]); 300 printf("\n"); 301 } 302 303 if (prop->flags & DRM_MODE_PROP_ENUM) { 304 printf("\t\tenums:"); 305 for (i = 0; i < prop->count_enums; i++) 306 printf(" %s=%llu", prop->enums[i].name, 307 prop->enums[i].value); 308 printf("\n"); 309 } else if (prop->flags & DRM_MODE_PROP_BITMASK) { 310 printf("\t\tvalues:"); 311 for (i = 0; i < prop->count_enums; i++) 312 printf(" %s=0x%llx", prop->enums[i].name, 313 (1LL << prop->enums[i].value)); 314 printf("\n"); 315 } else { 316 assert(prop->count_enums == 0); 317 } 318 319 if (prop->flags & DRM_MODE_PROP_BLOB) { 320 printf("\t\tblobs:\n"); 321 for (i = 0; i < prop->count_blobs; i++) 322 dump_blob(dev, prop->blob_ids[i]); 323 printf("\n"); 324 } else { 325 assert(prop->count_blobs == 0); 326 } 327 328 printf("\t\tvalue:"); 329 if (prop->flags & DRM_MODE_PROP_BLOB) 330 dump_blob(dev, value); 331 else 332 printf(" %"PRIu64"\n", value); 333} 334 335static void dump_connectors(struct device *dev) 336{ 337 int i, j; 338 339 printf("Connectors:\n"); 340 printf("id\tencoder\tstatus\t\ttype\tsize (mm)\tmodes\tencoders\n"); 341 for (i = 0; i < dev->resources->res->count_connectors; i++) { 342 struct connector *_connector = &dev->resources->connectors[i]; 343 drmModeConnector *connector = _connector->connector; 344 if (!connector) 345 continue; 346 347 printf("%d\t%d\t%s\t%s\t%dx%d\t\t%d\t", 348 connector->connector_id, 349 connector->encoder_id, 350 connector_status_str(connector->connection), 351 connector_type_str(connector->connector_type), 352 connector->mmWidth, connector->mmHeight, 353 connector->count_modes); 354 355 for (j = 0; j < connector->count_encoders; j++) 356 printf("%s%d", j > 0 ? ", " : "", connector->encoders[j]); 357 printf("\n"); 358 359 if (connector->count_modes) { 360 printf(" modes:\n"); 361 printf("\tname refresh (Hz) hdisp hss hse htot vdisp " 362 "vss vse vtot)\n"); 363 for (j = 0; j < connector->count_modes; j++) 364 dump_mode(&connector->modes[j]); 365 } 366 367 if (_connector->props) { 368 printf(" props:\n"); 369 for (j = 0; j < (int)_connector->props->count_props; j++) 370 dump_prop(dev, _connector->props_info[j], 371 _connector->props->props[j], 372 _connector->props->prop_values[j]); 373 } 374 } 375 printf("\n"); 376} 377 378static void dump_crtcs(struct device *dev) 379{ 380 int i; 381 uint32_t j; 382 383 printf("CRTCs:\n"); 384 printf("id\tfb\tpos\tsize\n"); 385 for (i = 0; i < dev->resources->res->count_crtcs; i++) { 386 struct crtc *_crtc = &dev->resources->crtcs[i]; 387 drmModeCrtc *crtc = _crtc->crtc; 388 if (!crtc) 389 continue; 390 391 printf("%d\t%d\t(%d,%d)\t(%dx%d)\n", 392 crtc->crtc_id, 393 crtc->buffer_id, 394 crtc->x, crtc->y, 395 crtc->width, crtc->height); 396 dump_mode(&crtc->mode); 397 398 if (_crtc->props) { 399 printf(" props:\n"); 400 for (j = 0; j < _crtc->props->count_props; j++) 401 dump_prop(dev, _crtc->props_info[j], 402 _crtc->props->props[j], 403 _crtc->props->prop_values[j]); 404 } else { 405 printf(" no properties found\n"); 406 } 407 } 408 printf("\n"); 409} 410 411static void dump_framebuffers(struct device *dev) 412{ 413 drmModeFB *fb; 414 int i; 415 416 printf("Frame buffers:\n"); 417 printf("id\tsize\tpitch\n"); 418 for (i = 0; i < dev->resources->res->count_fbs; i++) { 419 fb = dev->resources->fbs[i].fb; 420 if (!fb) 421 continue; 422 423 printf("%u\t(%ux%u)\t%u\n", 424 fb->fb_id, 425 fb->width, fb->height, 426 fb->pitch); 427 } 428 printf("\n"); 429} 430 431static void dump_planes(struct device *dev) 432{ 433 unsigned int i, j; 434 435 printf("Planes:\n"); 436 printf("id\tcrtc\tfb\tCRTC x,y\tx,y\tgamma size\tpossible crtcs\n"); 437 438 if (!dev->resources->plane_res) 439 return; 440 441 for (i = 0; i < dev->resources->plane_res->count_planes; i++) { 442 struct plane *plane = &dev->resources->planes[i]; 443 drmModePlane *ovr = plane->plane; 444 if (!ovr) 445 continue; 446 447 printf("%d\t%d\t%d\t%d,%d\t\t%d,%d\t%-8d\t0x%08x\n", 448 ovr->plane_id, ovr->crtc_id, ovr->fb_id, 449 ovr->crtc_x, ovr->crtc_y, ovr->x, ovr->y, 450 ovr->gamma_size, ovr->possible_crtcs); 451 452 if (!ovr->count_formats) 453 continue; 454 455 printf(" formats:"); 456 for (j = 0; j < ovr->count_formats; j++) 457 printf(" %4.4s", (char *)&ovr->formats[j]); 458 printf("\n"); 459 460 if (plane->props) { 461 printf(" props:\n"); 462 for (j = 0; j < plane->props->count_props; j++) 463 dump_prop(dev, plane->props_info[j], 464 plane->props->props[j], 465 plane->props->prop_values[j]); 466 } else { 467 printf(" no properties found\n"); 468 } 469 } 470 printf("\n"); 471 472 return; 473} 474 475static void free_resources(struct resources *res) 476{ 477 if (!res) 478 return; 479 480#define free_resource(_res, __res, type, Type) \ 481 do { \ 482 int i; \ 483 if (!(_res)->type##s) \ 484 break; \ 485 for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \ 486 if (!(_res)->type##s[i].type) \ 487 break; \ 488 drmModeFree##Type((_res)->type##s[i].type); \ 489 } \ 490 free((_res)->type##s); \ 491 } while (0) 492 493#define free_properties(_res, __res, type) \ 494 do { \ 495 int i; \ 496 for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \ 497 drmModeFreeObjectProperties(res->type##s[i].props); \ 498 free(res->type##s[i].props_info); \ 499 } \ 500 } while (0) 501 502 if (res->res) { 503 free_properties(res, res, crtc); 504 505 free_resource(res, res, crtc, Crtc); 506 free_resource(res, res, encoder, Encoder); 507 free_resource(res, res, connector, Connector); 508 free_resource(res, res, fb, FB); 509 510 drmModeFreeResources(res->res); 511 } 512 513 if (res->plane_res) { 514 free_properties(res, plane_res, plane); 515 516 free_resource(res, plane_res, plane, Plane); 517 518 drmModeFreePlaneResources(res->plane_res); 519 } 520 521 free(res); 522} 523 524static struct resources *get_resources(struct device *dev) 525{ 526 struct resources *res; 527 528 res = malloc(sizeof *res); 529 if (res == 0) 530 return NULL; 531 532 memset(res, 0, sizeof *res); 533 534 res->res = drmModeGetResources(dev->fd); 535 if (!res->res) { 536 fprintf(stderr, "drmModeGetResources failed: %s\n", 537 strerror(errno)); 538 goto error; 539 } 540 541 res->crtcs = malloc(res->res->count_crtcs * sizeof *res->crtcs); 542 res->encoders = malloc(res->res->count_encoders * sizeof *res->encoders); 543 res->connectors = malloc(res->res->count_connectors * sizeof *res->connectors); 544 res->fbs = malloc(res->res->count_fbs * sizeof *res->fbs); 545 546 if (!res->crtcs || !res->encoders || !res->connectors || !res->fbs) 547 goto error; 548 549 memset(res->crtcs , 0, res->res->count_crtcs * sizeof *res->crtcs); 550 memset(res->encoders, 0, res->res->count_encoders * sizeof *res->encoders); 551 memset(res->connectors, 0, res->res->count_connectors * sizeof *res->connectors); 552 memset(res->fbs, 0, res->res->count_fbs * sizeof *res->fbs); 553 554#define get_resource(_res, __res, type, Type) \ 555 do { \ 556 int i; \ 557 for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \ 558 (_res)->type##s[i].type = \ 559 drmModeGet##Type(dev->fd, (_res)->__res->type##s[i]); \ 560 if (!(_res)->type##s[i].type) \ 561 fprintf(stderr, "could not get %s %i: %s\n", \ 562 #type, (_res)->__res->type##s[i], \ 563 strerror(errno)); \ 564 } \ 565 } while (0) 566 567 get_resource(res, res, crtc, Crtc); 568 get_resource(res, res, encoder, Encoder); 569 get_resource(res, res, connector, Connector); 570 get_resource(res, res, fb, FB); 571 572#define get_properties(_res, __res, type, Type) \ 573 do { \ 574 int i; \ 575 for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \ 576 struct type *obj = &res->type##s[i]; \ 577 unsigned int j; \ 578 obj->props = \ 579 drmModeObjectGetProperties(dev->fd, obj->type->type##_id, \ 580 DRM_MODE_OBJECT_##Type); \ 581 if (!obj->props) { \ 582 fprintf(stderr, \ 583 "could not get %s %i properties: %s\n", \ 584 #type, obj->type->type##_id, \ 585 strerror(errno)); \ 586 continue; \ 587 } \ 588 obj->props_info = malloc(obj->props->count_props * \ 589 sizeof *obj->props_info); \ 590 if (!obj->props_info) \ 591 continue; \ 592 for (j = 0; j < obj->props->count_props; ++j) \ 593 obj->props_info[j] = \ 594 drmModeGetProperty(dev->fd, obj->props->props[j]); \ 595 } \ 596 } while (0) 597 598 get_properties(res, res, crtc, CRTC); 599 get_properties(res, res, connector, CONNECTOR); 600 601 res->plane_res = drmModeGetPlaneResources(dev->fd); 602 if (!res->plane_res) { 603 fprintf(stderr, "drmModeGetPlaneResources failed: %s\n", 604 strerror(errno)); 605 return res; 606 } 607 608 res->planes = malloc(res->plane_res->count_planes * sizeof *res->planes); 609 if (!res->planes) 610 goto error; 611 612 memset(res->planes, 0, res->plane_res->count_planes * sizeof *res->planes); 613 614 get_resource(res, plane_res, plane, Plane); 615 get_properties(res, plane_res, plane, PLANE); 616 617 return res; 618 619error: 620 free_resources(res); 621 return NULL; 622} 623 624/* ----------------------------------------------------------------------------- 625 * Connectors and planes 626 */ 627 628/* 629 * Mode setting with the kernel interfaces is a bit of a chore. 630 * First you have to find the connector in question and make sure the 631 * requested mode is available. 632 * Then you need to find the encoder attached to that connector so you 633 * can bind it with a free crtc. 634 */ 635struct connector_arg { 636 uint32_t id; 637 char mode_str[64]; 638 char format_str[5]; 639 unsigned int fourcc; 640 drmModeModeInfo *mode; 641 drmModeEncoder *encoder; 642 int crtc; 643 unsigned int fb_id[2], current_fb_id; 644 struct timeval start; 645 646 int swap_count; 647}; 648 649struct plane_arg { 650 uint32_t con_id; /* the id of connector to bind to */ 651 bool has_position; 652 int32_t x, y; 653 uint32_t w, h; 654 unsigned int fb_id; 655 char format_str[5]; /* need to leave room for terminating \0 */ 656 unsigned int fourcc; 657}; 658 659static void connector_find_mode(struct device *dev, struct connector_arg *c) 660{ 661 drmModeConnector *connector; 662 int i, j; 663 664 /* First, find the connector & mode */ 665 c->mode = NULL; 666 for (i = 0; i < dev->resources->res->count_connectors; i++) { 667 connector = dev->resources->connectors[i].connector; 668 if (!connector) 669 continue; 670 671 if (!connector->count_modes) 672 continue; 673 674 if (connector->connector_id != c->id) 675 continue; 676 677 for (j = 0; j < connector->count_modes; j++) { 678 c->mode = &connector->modes[j]; 679 if (!strcmp(c->mode->name, c->mode_str)) 680 break; 681 } 682 683 /* Found it, break out */ 684 if (c->mode) 685 break; 686 } 687 688 if (!c->mode) { 689 fprintf(stderr, "failed to find mode \"%s\"\n", c->mode_str); 690 return; 691 } 692 693 /* Now get the encoder */ 694 for (i = 0; i < dev->resources->res->count_encoders; i++) { 695 c->encoder = dev->resources->encoders[i].encoder; 696 if (!c->encoder) 697 continue; 698 699 if (c->encoder->encoder_id == connector->encoder_id) 700 break; 701 } 702 703 if (c->crtc == -1) 704 c->crtc = c->encoder->crtc_id; 705} 706 707/* ----------------------------------------------------------------------------- 708 * Properties 709 */ 710 711struct property_arg { 712 uint32_t obj_id; 713 uint32_t obj_type; 714 char name[DRM_PROP_NAME_LEN+1]; 715 uint32_t prop_id; 716 uint64_t value; 717}; 718 719static void set_property(struct device *dev, struct property_arg *p) 720{ 721 drmModeObjectProperties *props; 722 drmModePropertyRes **props_info; 723 const char *obj_type; 724 int ret; 725 int i; 726 727 p->obj_type = 0; 728 p->prop_id = 0; 729 730#define find_object(_res, __res, type, Type) \ 731 do { \ 732 for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \ 733 struct type *obj = &(_res)->type##s[i]; \ 734 if (obj->type->type##_id != p->obj_id) \ 735 continue; \ 736 p->obj_type = DRM_MODE_OBJECT_##Type; \ 737 obj_type = #Type; \ 738 props = obj->props; \ 739 props_info = obj->props_info; \ 740 } \ 741 } while(0) \ 742 743 find_object(dev->resources, res, crtc, CRTC); 744 if (p->obj_type == 0) 745 find_object(dev->resources, res, connector, CONNECTOR); 746 if (p->obj_type == 0) 747 find_object(dev->resources, plane_res, plane, PLANE); 748 if (p->obj_type == 0) { 749 fprintf(stderr, "Object %i not found, can't set property\n", 750 p->obj_id); 751 return; 752 } 753 754 if (!props) { 755 fprintf(stderr, "%s %i has no properties\n", 756 obj_type, p->obj_id); 757 return; 758 } 759 760 for (i = 0; i < (int)props->count_props; ++i) { 761 if (!props_info[i]) 762 continue; 763 if (strcmp(props_info[i]->name, p->name) == 0) 764 break; 765 } 766 767 if (i == (int)props->count_props) { 768 fprintf(stderr, "%s %i has no %s property\n", 769 obj_type, p->obj_id, p->name); 770 return; 771 } 772 773 p->prop_id = props->props[i]; 774 775 ret = drmModeObjectSetProperty(dev->fd, p->obj_id, p->obj_type, 776 p->prop_id, p->value); 777 if (ret < 0) 778 fprintf(stderr, "failed to set %s %i property %s to %" PRIu64 ": %s\n", 779 obj_type, p->obj_id, p->name, p->value, strerror(errno)); 780} 781 782/* -------------------------------------------------------------------------- */ 783 784static void 785page_flip_handler(int fd, unsigned int frame, 786 unsigned int sec, unsigned int usec, void *data) 787{ 788 struct connector_arg *c; 789 unsigned int new_fb_id; 790 struct timeval end; 791 double t; 792 793 c = data; 794 if (c->current_fb_id == c->fb_id[0]) 795 new_fb_id = c->fb_id[1]; 796 else 797 new_fb_id = c->fb_id[0]; 798 799 drmModePageFlip(fd, c->crtc, new_fb_id, 800 DRM_MODE_PAGE_FLIP_EVENT, c); 801 c->current_fb_id = new_fb_id; 802 c->swap_count++; 803 if (c->swap_count == 60) { 804 gettimeofday(&end, NULL); 805 t = end.tv_sec + end.tv_usec * 1e-6 - 806 (c->start.tv_sec + c->start.tv_usec * 1e-6); 807 fprintf(stderr, "freq: %.02fHz\n", c->swap_count / t); 808 c->swap_count = 0; 809 c->start = end; 810 } 811} 812 813static int 814set_plane(struct device *dev, struct connector_arg *c, struct plane_arg *p) 815{ 816 drmModePlane *ovr; 817 uint32_t handles[4], pitches[4], offsets[4] = {0}; /* we only use [0] */ 818 uint32_t plane_id = 0; 819 struct kms_bo *plane_bo; 820 uint32_t plane_flags = 0; 821 int crtc_x, crtc_y, crtc_w, crtc_h; 822 unsigned int pipe; 823 unsigned int i; 824 825 /* Find an unused plane which can be connected to our CRTC. Find the 826 * CRTC index first, then iterate over available planes. 827 */ 828 for (i = 0; i < (unsigned int)dev->resources->res->count_crtcs; i++) { 829 if (c->crtc == (int)dev->resources->res->crtcs[i]) { 830 pipe = i; 831 break; 832 } 833 } 834 835 if (pipe == (unsigned int)dev->resources->res->count_crtcs) { 836 fprintf(stderr, "CRTC %u not found\n", c->crtc); 837 return -1; 838 } 839 840 for (i = 0; i < dev->resources->plane_res->count_planes && !plane_id; i++) { 841 ovr = dev->resources->planes[i].plane; 842 if (!ovr) 843 continue; 844 845 if ((ovr->possible_crtcs & (1 << pipe)) && !ovr->crtc_id) 846 plane_id = ovr->plane_id; 847 } 848 849 if (!plane_id) { 850 fprintf(stderr, "no unused plane available for CRTC %u\n", c->crtc); 851 return -1; 852 } 853 854 fprintf(stderr, "testing %dx%d@%s overlay plane %u\n", 855 p->w, p->h, p->format_str, plane_id); 856 857 plane_bo = create_test_buffer(dev->kms, p->fourcc, p->w, p->h, handles, 858 pitches, offsets, PATTERN_TILES); 859 if (plane_bo == NULL) 860 return -1; 861 862 /* just use single plane format for now.. */ 863 if (drmModeAddFB2(dev->fd, p->w, p->h, p->fourcc, 864 handles, pitches, offsets, &p->fb_id, plane_flags)) { 865 fprintf(stderr, "failed to add fb: %s\n", strerror(errno)); 866 return -1; 867 } 868 869 if (!p->has_position) { 870 /* Default to the middle of the screen */ 871 crtc_x = (c->mode->hdisplay - p->w) / 2; 872 crtc_y = (c->mode->vdisplay - p->h) / 2; 873 } else { 874 crtc_x = p->x; 875 crtc_y = p->y; 876 } 877 crtc_w = p->w; 878 crtc_h = p->h; 879 880 /* note src coords (last 4 args) are in Q16 format */ 881 if (drmModeSetPlane(dev->fd, plane_id, c->crtc, p->fb_id, 882 plane_flags, crtc_x, crtc_y, crtc_w, crtc_h, 883 0, 0, p->w << 16, p->h << 16)) { 884 fprintf(stderr, "failed to enable plane: %s\n", 885 strerror(errno)); 886 return -1; 887 } 888 889 ovr->crtc_id = c->crtc; 890 891 return 0; 892} 893 894static void set_mode(struct device *dev, struct connector_arg *c, int count, 895 struct plane_arg *p, int plane_count, int page_flip) 896{ 897 struct kms_bo *bo, *other_bo; 898 unsigned int fb_id, other_fb_id; 899 int i, j, ret, width, height, x; 900 uint32_t handles[4], pitches[4], offsets[4] = {0}; /* we only use [0] */ 901 drmEventContext evctx; 902 903 width = 0; 904 height = 0; 905 for (i = 0; i < count; i++) { 906 connector_find_mode(dev, &c[i]); 907 if (c[i].mode == NULL) 908 continue; 909 width += c[i].mode->hdisplay; 910 if (height < c[i].mode->vdisplay) 911 height = c[i].mode->vdisplay; 912 } 913 914 bo = create_test_buffer(dev->kms, c->fourcc, width, height, handles, 915 pitches, offsets, PATTERN_SMPTE); 916 if (bo == NULL) 917 return; 918 919 ret = drmModeAddFB2(dev->fd, width, height, c->fourcc, 920 handles, pitches, offsets, &fb_id, 0); 921 if (ret) { 922 fprintf(stderr, "failed to add fb (%ux%u): %s\n", 923 width, height, strerror(errno)); 924 return; 925 } 926 927 x = 0; 928 for (i = 0; i < count; i++) { 929 if (c[i].mode == NULL) 930 continue; 931 932 printf("setting mode %s@%s on connector %d, crtc %d\n", 933 c[i].mode_str, c[i].format_str, c[i].id, c[i].crtc); 934 935 ret = drmModeSetCrtc(dev->fd, c[i].crtc, fb_id, x, 0, 936 &c[i].id, 1, c[i].mode); 937 938 /* XXX: Actually check if this is needed */ 939 drmModeDirtyFB(dev->fd, fb_id, NULL, 0); 940 941 x += c[i].mode->hdisplay; 942 943 if (ret) { 944 fprintf(stderr, "failed to set mode: %s\n", strerror(errno)); 945 return; 946 } 947 948 /* if we have a plane/overlay to show, set that up now: */ 949 for (j = 0; j < plane_count; j++) 950 if (p[j].con_id == c[i].id) 951 if (set_plane(dev, &c[i], &p[j])) 952 return; 953 } 954 955 if (!page_flip) 956 return; 957 958 other_bo = create_test_buffer(dev->kms, c->fourcc, width, height, handles, 959 pitches, offsets, PATTERN_PLAIN); 960 if (other_bo == NULL) 961 return; 962 963 ret = drmModeAddFB2(dev->fd, width, height, c->fourcc, handles, pitches, offsets, 964 &other_fb_id, 0); 965 if (ret) { 966 fprintf(stderr, "failed to add fb: %s\n", strerror(errno)); 967 return; 968 } 969 970 for (i = 0; i < count; i++) { 971 if (c[i].mode == NULL) 972 continue; 973 974 ret = drmModePageFlip(dev->fd, c[i].crtc, other_fb_id, 975 DRM_MODE_PAGE_FLIP_EVENT, &c[i]); 976 if (ret) { 977 fprintf(stderr, "failed to page flip: %s\n", strerror(errno)); 978 return; 979 } 980 gettimeofday(&c[i].start, NULL); 981 c[i].swap_count = 0; 982 c[i].fb_id[0] = fb_id; 983 c[i].fb_id[1] = other_fb_id; 984 c[i].current_fb_id = other_fb_id; 985 } 986 987 memset(&evctx, 0, sizeof evctx); 988 evctx.version = DRM_EVENT_CONTEXT_VERSION; 989 evctx.vblank_handler = NULL; 990 evctx.page_flip_handler = page_flip_handler; 991 992 while (1) { 993#if 0 994 struct pollfd pfd[2]; 995 996 pfd[0].fd = 0; 997 pfd[0].events = POLLIN; 998 pfd[1].fd = fd; 999 pfd[1].events = POLLIN; 1000 1001 if (poll(pfd, 2, -1) < 0) { 1002 fprintf(stderr, "poll error\n"); 1003 break; 1004 } 1005 1006 if (pfd[0].revents) 1007 break; 1008#else 1009 struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 }; 1010 fd_set fds; 1011 int ret; 1012 1013 FD_ZERO(&fds); 1014 FD_SET(0, &fds); 1015 FD_SET(dev->fd, &fds); 1016 ret = select(dev->fd + 1, &fds, NULL, NULL, &timeout); 1017 1018 if (ret <= 0) { 1019 fprintf(stderr, "select timed out or error (ret %d)\n", 1020 ret); 1021 continue; 1022 } else if (FD_ISSET(0, &fds)) { 1023 break; 1024 } 1025#endif 1026 1027 drmHandleEvent(dev->fd, &evctx); 1028 } 1029 1030 kms_bo_destroy(&bo); 1031 kms_bo_destroy(&other_bo); 1032} 1033 1034#define min(a, b) ((a) < (b) ? (a) : (b)) 1035 1036static int parse_connector(struct connector_arg *c, const char *arg) 1037{ 1038 unsigned int len; 1039 const char *p; 1040 char *endp; 1041 1042 c->crtc = -1; 1043 strcpy(c->format_str, "XR24"); 1044 1045 c->id = strtoul(arg, &endp, 10); 1046 if (*endp == '@') { 1047 arg = endp + 1; 1048 c->crtc = strtoul(arg, &endp, 10); 1049 } 1050 if (*endp != ':') 1051 return -1; 1052 1053 arg = endp + 1; 1054 1055 p = strchrnul(arg, '@'); 1056 len = min(sizeof c->mode_str - 1, (unsigned int)(p - arg)); 1057 strncpy(c->mode_str, arg, len); 1058 c->mode_str[len] = '\0'; 1059 1060 if (*p == '@') { 1061 strncpy(c->format_str, p + 1, 4); 1062 c->format_str[4] = '\0'; 1063 } 1064 1065 c->fourcc = format_fourcc(c->format_str); 1066 if (c->fourcc == 0) { 1067 fprintf(stderr, "unknown format %s\n", c->format_str); 1068 return -1; 1069 } 1070 1071 return 0; 1072} 1073 1074static int parse_plane(struct plane_arg *plane, const char *p) 1075{ 1076 char *end; 1077 1078 memset(plane, 0, sizeof *plane); 1079 1080 plane->con_id = strtoul(p, &end, 10); 1081 if (*end != ':') 1082 return -EINVAL; 1083 1084 p = end + 1; 1085 plane->w = strtoul(p, &end, 10); 1086 if (*end != 'x') 1087 return -EINVAL; 1088 1089 p = end + 1; 1090 plane->h = strtoul(p, &end, 10); 1091 1092 if (*end == '+' || *end == '-') { 1093 plane->x = strtol(end, &end, 10); 1094 if (*end != '+' && *end != '-') 1095 return -EINVAL; 1096 plane->y = strtol(end, &end, 10); 1097 1098 plane->has_position = true; 1099 } 1100 1101 if (*end == '@') { 1102 p = end + 1; 1103 if (strlen(p) != 4) 1104 return -EINVAL; 1105 1106 strcpy(plane->format_str, p); 1107 } else { 1108 strcpy(plane->format_str, "XR24"); 1109 } 1110 1111 plane->fourcc = format_fourcc(plane->format_str); 1112 if (plane->fourcc == 0) { 1113 fprintf(stderr, "unknown format %s\n", plane->format_str); 1114 return -EINVAL; 1115 } 1116 1117 return 0; 1118} 1119 1120static int parse_property(struct property_arg *p, const char *arg) 1121{ 1122 if (sscanf(arg, "%d:%32[^:]:%" SCNu64, &p->obj_id, p->name, &p->value) != 3) 1123 return -1; 1124 1125 p->obj_type = 0; 1126 p->name[DRM_PROP_NAME_LEN] = '\0'; 1127 1128 return 0; 1129} 1130 1131static void usage(char *name) 1132{ 1133 fprintf(stderr, "usage: %s [-cdefMmPpsvw]\n", name); 1134 1135 fprintf(stderr, "\n Query options:\n\n"); 1136 fprintf(stderr, "\t-c\tlist connectors\n"); 1137 fprintf(stderr, "\t-e\tlist encoders\n"); 1138 fprintf(stderr, "\t-f\tlist framebuffers\n"); 1139 fprintf(stderr, "\t-p\tlist CRTCs and planes (pipes)\n"); 1140 1141 fprintf(stderr, "\n Test options:\n\n"); 1142 fprintf(stderr, "\t-P <connector_id>:<w>x<h>[+<x>+<y>][@<format>]\tset a plane\n"); 1143 fprintf(stderr, "\t-s <connector_id>[@<crtc_id>]:<mode>[@<format>]\tset a mode\n"); 1144 fprintf(stderr, "\t-v\ttest vsynced page flipping\n"); 1145 fprintf(stderr, "\t-w <obj_id>:<prop_name>:<value>\tset property\n"); 1146 1147 fprintf(stderr, "\n Generic options:\n\n"); 1148 fprintf(stderr, "\t-d\tdrop master after mode set\n"); 1149 fprintf(stderr, "\t-M module\tuse the given driver\n"); 1150 1151 fprintf(stderr, "\n\tDefault is to dump all info.\n"); 1152 exit(0); 1153} 1154 1155static int page_flipping_supported(void) 1156{ 1157 /*FIXME: generic ioctl needed? */ 1158 return 1; 1159#if 0 1160 int ret, value; 1161 struct drm_i915_getparam gp; 1162 1163 gp.param = I915_PARAM_HAS_PAGEFLIPPING; 1164 gp.value = &value; 1165 1166 ret = drmCommandWriteRead(fd, DRM_I915_GETPARAM, &gp, sizeof(gp)); 1167 if (ret) { 1168 fprintf(stderr, "drm_i915_getparam: %m\n"); 1169 return 0; 1170 } 1171 1172 return *gp.value; 1173#endif 1174} 1175 1176static char optstr[] = "cdefM:P:ps:vw:"; 1177 1178int main(int argc, char **argv) 1179{ 1180 struct device dev; 1181 1182 int c; 1183 int encoders = 0, connectors = 0, crtcs = 0, planes = 0, framebuffers = 0; 1184 int drop_master = 0; 1185 int test_vsync = 0; 1186 const char *modules[] = { "i915", "radeon", "nouveau", "vmwgfx", "omapdrm", "exynos", "tilcdc" }; 1187 char *module = NULL; 1188 unsigned int i; 1189 int count = 0, plane_count = 0; 1190 unsigned int prop_count = 0; 1191 struct connector_arg *con_args = NULL; 1192 struct plane_arg *plane_args = NULL; 1193 struct property_arg *prop_args = NULL; 1194 unsigned int args = 0; 1195 int ret; 1196 1197 opterr = 0; 1198 while ((c = getopt(argc, argv, optstr)) != -1) { 1199 args++; 1200 1201 switch (c) { 1202 case 'c': 1203 connectors = 1; 1204 break; 1205 case 'd': 1206 drop_master = 1; 1207 break; 1208 case 'e': 1209 encoders = 1; 1210 break; 1211 case 'f': 1212 framebuffers = 1; 1213 break; 1214 case 'M': 1215 module = optarg; 1216 /* Preserve the default behaviour of dumping all information. */ 1217 args--; 1218 break; 1219 case 'P': 1220 plane_args = realloc(plane_args, 1221 (plane_count + 1) * sizeof *plane_args); 1222 if (plane_args == NULL) { 1223 fprintf(stderr, "memory allocation failed\n"); 1224 return 1; 1225 } 1226 1227 if (parse_plane(&plane_args[plane_count], optarg) < 0) 1228 usage(argv[0]); 1229 1230 plane_count++; 1231 break; 1232 case 'p': 1233 crtcs = 1; 1234 planes = 1; 1235 break; 1236 case 's': 1237 con_args = realloc(con_args, 1238 (count + 1) * sizeof *con_args); 1239 if (con_args == NULL) { 1240 fprintf(stderr, "memory allocation failed\n"); 1241 return 1; 1242 } 1243 1244 if (parse_connector(&con_args[count], optarg) < 0) 1245 usage(argv[0]); 1246 1247 count++; 1248 break; 1249 case 'v': 1250 test_vsync = 1; 1251 break; 1252 case 'w': 1253 prop_args = realloc(prop_args, 1254 (prop_count + 1) * sizeof *prop_args); 1255 if (prop_args == NULL) { 1256 fprintf(stderr, "memory allocation failed\n"); 1257 return 1; 1258 } 1259 1260 if (parse_property(&prop_args[prop_count], optarg) < 0) 1261 usage(argv[0]); 1262 1263 prop_count++; 1264 break; 1265 default: 1266 usage(argv[0]); 1267 break; 1268 } 1269 } 1270 1271 if (!args) 1272 encoders = connectors = crtcs = planes = framebuffers = 1; 1273 1274 if (module) { 1275 dev.fd = drmOpen(module, NULL); 1276 if (dev.fd < 0) { 1277 fprintf(stderr, "failed to open device '%s'.\n", module); 1278 return 1; 1279 } 1280 } else { 1281 for (i = 0; i < ARRAY_SIZE(modules); i++) { 1282 printf("trying to open device '%s'...", modules[i]); 1283 dev.fd = drmOpen(modules[i], NULL); 1284 if (dev.fd < 0) { 1285 printf("failed.\n"); 1286 } else { 1287 printf("success.\n"); 1288 break; 1289 } 1290 } 1291 1292 if (dev.fd < 0) { 1293 fprintf(stderr, "no device found.\n"); 1294 return 1; 1295 } 1296 } 1297 1298 if (test_vsync && !page_flipping_supported()) { 1299 fprintf(stderr, "page flipping not supported by drm.\n"); 1300 return -1; 1301 } 1302 1303 dev.resources = get_resources(&dev); 1304 if (!dev.resources) { 1305 drmClose(dev.fd); 1306 return 1; 1307 } 1308 1309#define dump_resource(dev, res) if (res) dump_##res(dev) 1310 1311 dump_resource(&dev, encoders); 1312 dump_resource(&dev, connectors); 1313 dump_resource(&dev, crtcs); 1314 dump_resource(&dev, planes); 1315 dump_resource(&dev, framebuffers); 1316 1317 for (i = 0; i < prop_count; ++i) 1318 set_property(&dev, &prop_args[i]); 1319 1320 if (count > 0) { 1321 ret = kms_create(dev.fd, &dev.kms); 1322 if (ret) { 1323 fprintf(stderr, "failed to create kms driver: %s\n", 1324 strerror(-ret)); 1325 return 1; 1326 } 1327 1328 set_mode(&dev, con_args, count, plane_args, plane_count, test_vsync); 1329 if (drop_master) 1330 drmDropMaster(dev.fd); 1331 1332 kms_destroy(&dev.kms); 1333 getchar(); 1334 } 1335 1336 free_resources(dev.resources); 1337 1338 return 0; 1339} 1340