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 <stdio.h> 44#include <stdlib.h> 45#include <stdint.h> 46#include <unistd.h> 47#include <string.h> 48#include <errno.h> 49 50#include "xf86drm.h" 51#include "xf86drmMode.h" 52#include "intel_bufmgr.h" 53 54#ifdef HAVE_CAIRO 55#include <math.h> 56#include <cairo.h> 57#endif 58 59drmModeRes *resources; 60int fd, modes; 61 62#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) 63 64struct type_name { 65 int type; 66 char *name; 67}; 68 69#define type_name_fn(res) \ 70char * res##_str(int type) { \ 71 int i; \ 72 for (i = 0; i < ARRAY_SIZE(res##_names); i++) { \ 73 if (res##_names[i].type == type) \ 74 return res##_names[i].name; \ 75 } \ 76 return "(invalid)"; \ 77} 78 79struct type_name encoder_type_names[] = { 80 { DRM_MODE_ENCODER_NONE, "none" }, 81 { DRM_MODE_ENCODER_DAC, "DAC" }, 82 { DRM_MODE_ENCODER_TMDS, "TMDS" }, 83 { DRM_MODE_ENCODER_LVDS, "LVDS" }, 84 { DRM_MODE_ENCODER_TVDAC, "TVDAC" }, 85}; 86 87type_name_fn(encoder_type) 88 89struct type_name connector_status_names[] = { 90 { DRM_MODE_CONNECTED, "connected" }, 91 { DRM_MODE_DISCONNECTED, "disconnected" }, 92 { DRM_MODE_UNKNOWNCONNECTION, "unknown" }, 93}; 94 95type_name_fn(connector_status) 96 97struct type_name connector_type_names[] = { 98 { DRM_MODE_CONNECTOR_Unknown, "unknown" }, 99 { DRM_MODE_CONNECTOR_VGA, "VGA" }, 100 { DRM_MODE_CONNECTOR_DVII, "DVI-I" }, 101 { DRM_MODE_CONNECTOR_DVID, "DVI-D" }, 102 { DRM_MODE_CONNECTOR_DVIA, "DVI-A" }, 103 { DRM_MODE_CONNECTOR_Composite, "composite" }, 104 { DRM_MODE_CONNECTOR_SVIDEO, "s-video" }, 105 { DRM_MODE_CONNECTOR_LVDS, "LVDS" }, 106 { DRM_MODE_CONNECTOR_Component, "component" }, 107 { DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN" }, 108 { DRM_MODE_CONNECTOR_DisplayPort, "displayport" }, 109 { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" }, 110 { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" }, 111}; 112 113type_name_fn(connector_type) 114 115void dump_encoders(void) 116{ 117 drmModeEncoder *encoder; 118 int i; 119 120 printf("Encoders:\n"); 121 printf("id\tcrtc\ttype\tpossible crtcs\tpossible clones\t\n"); 122 for (i = 0; i < resources->count_encoders; i++) { 123 encoder = drmModeGetEncoder(fd, resources->encoders[i]); 124 125 if (!encoder) { 126 fprintf(stderr, "could not get encoder %i: %s\n", 127 resources->encoders[i], strerror(errno)); 128 continue; 129 } 130 printf("%d\t%d\t%s\t0x%08x\t0x%08x\n", 131 encoder->encoder_id, 132 encoder->crtc_id, 133 encoder_type_str(encoder->encoder_type), 134 encoder->possible_crtcs, 135 encoder->possible_clones); 136 drmModeFreeEncoder(encoder); 137 } 138 printf("\n"); 139} 140 141void dump_mode(drmModeModeInfo *mode) 142{ 143 printf(" %s %.02f %d %d %d %d %d %d %d %d\n", 144 mode->name, 145 (float)mode->vrefresh / 1000, 146 mode->hdisplay, 147 mode->hsync_start, 148 mode->hsync_end, 149 mode->htotal, 150 mode->vdisplay, 151 mode->vsync_start, 152 mode->vsync_end, 153 mode->vtotal); 154} 155 156static void 157dump_props(drmModeConnector *connector) 158{ 159 drmModePropertyPtr props; 160 int i; 161 162 for (i = 0; i < connector->count_props; i++) { 163 props = drmModeGetProperty(fd, connector->props[i]); 164 printf("\t%s, flags %d\n", props->name, props->flags); 165 drmModeFreeProperty(props); 166 } 167} 168 169void dump_connectors(void) 170{ 171 drmModeConnector *connector; 172 int i, j; 173 174 printf("Connectors:\n"); 175 printf("id\tencoder\tstatus\t\ttype\tsize (mm)\tmodes\n"); 176 for (i = 0; i < resources->count_connectors; i++) { 177 connector = drmModeGetConnector(fd, resources->connectors[i]); 178 179 if (!connector) { 180 fprintf(stderr, "could not get connector %i: %s\n", 181 resources->connectors[i], strerror(errno)); 182 continue; 183 } 184 185 printf("%d\t%d\t%s\t%s\t%dx%d\t\t%d\n", 186 connector->connector_id, 187 connector->encoder_id, 188 connector_status_str(connector->connection), 189 connector_type_str(connector->connector_type), 190 connector->mmWidth, connector->mmHeight, 191 connector->count_modes); 192 193 if (!connector->count_modes) 194 continue; 195 196 printf(" modes:\n"); 197 printf(" name refresh (Hz) hdisp hss hse htot vdisp " 198 "vss vse vtot)\n"); 199 for (j = 0; j < connector->count_modes; j++) 200 dump_mode(&connector->modes[j]); 201 202 drmModeFreeConnector(connector); 203 204 printf(" props:\n"); 205 dump_props(connector); 206 } 207 printf("\n"); 208} 209 210void dump_crtcs(void) 211{ 212 drmModeCrtc *crtc; 213 int i; 214 215 printf("CRTCs:\n"); 216 printf("id\tfb\tpos\tsize\n"); 217 for (i = 0; i < resources->count_crtcs; i++) { 218 crtc = drmModeGetCrtc(fd, resources->crtcs[i]); 219 220 if (!crtc) { 221 fprintf(stderr, "could not get crtc %i: %s\n", 222 resources->crtcs[i], strerror(errno)); 223 continue; 224 } 225 printf("%d\t%d\t(%d,%d)\t(%dx%d)\n", 226 crtc->crtc_id, 227 crtc->buffer_id, 228 crtc->x, crtc->y, 229 crtc->width, crtc->height); 230 dump_mode(&crtc->mode); 231 232 drmModeFreeCrtc(crtc); 233 } 234 printf("\n"); 235} 236 237void dump_framebuffers(void) 238{ 239 drmModeFB *fb; 240 int i; 241 242 printf("Frame buffers:\n"); 243 printf("id\tsize\tpitch\n"); 244 for (i = 0; i < resources->count_fbs; i++) { 245 fb = drmModeGetFB(fd, resources->fbs[i]); 246 247 if (!fb) { 248 fprintf(stderr, "could not get fb %i: %s\n", 249 resources->fbs[i], strerror(errno)); 250 continue; 251 } 252 printf("%d\t(%dx%d)\t%d\n", 253 fb->fb_id, 254 fb->width, fb->height); 255 256 drmModeFreeFB(fb); 257 } 258 printf("\n"); 259} 260 261/* 262 * Mode setting with the kernel interfaces is a bit of a chore. 263 * First you have to find the connector in question and make sure the 264 * requested mode is available. 265 * Then you need to find the encoder attached to that connector so you 266 * can bind it with a free crtc. 267 */ 268struct connector { 269 int id; 270 char mode_str[64]; 271 drmModeModeInfo *mode; 272 drmModeEncoder *encoder; 273 int crtc; 274}; 275 276static void 277connector_find_mode(struct connector *c) 278{ 279 drmModeConnector *connector; 280 int i, j, size, ret, width, height; 281 282 /* First, find the connector & mode */ 283 c->mode = NULL; 284 for (i = 0; i < resources->count_connectors; i++) { 285 connector = drmModeGetConnector(fd, resources->connectors[i]); 286 287 if (!connector) { 288 fprintf(stderr, "could not get connector %i: %s\n", 289 resources->connectors[i], strerror(errno)); 290 drmModeFreeConnector(connector); 291 continue; 292 } 293 294 if (!connector->count_modes) { 295 drmModeFreeConnector(connector); 296 continue; 297 } 298 299 if (connector->connector_id != c->id) { 300 drmModeFreeConnector(connector); 301 continue; 302 } 303 304 for (j = 0; j < connector->count_modes; j++) { 305 c->mode = &connector->modes[j]; 306 if (!strcmp(c->mode->name, c->mode_str)) 307 break; 308 } 309 310 /* Found it, break out */ 311 if (c->mode) 312 break; 313 314 drmModeFreeConnector(connector); 315 } 316 317 if (!c->mode) { 318 fprintf(stderr, "failed to find mode \"%s\"\n", c->mode_str); 319 return; 320 } 321 322 /* Now get the encoder */ 323 for (i = 0; i < resources->count_encoders; i++) { 324 c->encoder = drmModeGetEncoder(fd, resources->encoders[i]); 325 326 if (!c->encoder) { 327 fprintf(stderr, "could not get encoder %i: %s\n", 328 resources->encoders[i], strerror(errno)); 329 drmModeFreeEncoder(c->encoder); 330 continue; 331 } 332 333 if (c->encoder->encoder_id == connector->encoder_id) 334 break; 335 336 drmModeFreeEncoder(c->encoder); 337 } 338 339 if (c->crtc == -1) 340 c->crtc = c->encoder->crtc_id; 341} 342 343#ifdef HAVE_CAIRO 344 345static int 346create_test_buffer(drm_intel_bufmgr *bufmgr, 347 int width, int height, int *stride_out, drm_intel_bo **bo_out) 348{ 349 drm_intel_bo *bo; 350 unsigned int *fb_ptr; 351 int size, ret, i, stride; 352 div_t d; 353 cairo_surface_t *surface; 354 cairo_t *cr; 355 char buf[64]; 356 int x, y; 357 358 surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); 359 stride = cairo_image_surface_get_stride(surface); 360 size = stride * height; 361 fb_ptr = (unsigned int *) cairo_image_surface_get_data(surface); 362 363 /* paint the buffer with colored tiles */ 364 for (i = 0; i < width * height; i++) { 365 d = div(i, width); 366 fb_ptr[i] = 0x00130502 * (d.quot >> 6) + 0x000a1120 * (d.rem >> 6); 367 } 368 369 cr = cairo_create(surface); 370 cairo_set_line_cap(cr, CAIRO_LINE_CAP_SQUARE); 371 for (x = 0; x < width; x += 250) 372 for (y = 0; y < height; y += 250) { 373 cairo_set_operator(cr, CAIRO_OPERATOR_OVER); 374 cairo_move_to(cr, x, y - 20); 375 cairo_line_to(cr, x, y + 20); 376 cairo_move_to(cr, x - 20, y); 377 cairo_line_to(cr, x + 20, y); 378 cairo_new_sub_path(cr); 379 cairo_arc(cr, x, y, 10, 0, M_PI * 2); 380 cairo_set_line_width(cr, 4); 381 cairo_set_source_rgb(cr, 0, 0, 0); 382 cairo_stroke_preserve(cr); 383 cairo_set_source_rgb(cr, 1, 1, 1); 384 cairo_set_line_width(cr, 2); 385 cairo_stroke(cr); 386 snprintf(buf, sizeof buf, "%d, %d", x, y); 387 cairo_move_to(cr, x + 20, y + 20); 388 cairo_text_path(cr, buf); 389 cairo_set_source_rgb(cr, 0, 0, 0); 390 cairo_stroke_preserve(cr); 391 cairo_set_source_rgb(cr, 1, 1, 1); 392 cairo_fill(cr); 393 } 394 395 cairo_destroy(cr); 396 397 bo = drm_intel_bo_alloc(bufmgr, "frontbuffer", size, 4096); 398 if (!bo) { 399 fprintf(stderr, "failed to alloc buffer: %s\n", 400 strerror(errno)); 401 return -1; 402 } 403 404 drm_intel_bo_subdata(bo, 0, size, fb_ptr); 405 406 cairo_surface_destroy(surface); 407 408 *bo_out = bo; 409 *stride_out = stride; 410 411 return 0; 412} 413 414#else 415 416static int 417create_test_buffer(drm_intel_bufmgr *bufmgr, 418 int width, int height, int *stride_out, drm_intel_bo **bo_out) 419{ 420 drm_intel_bo *bo; 421 unsigned int *fb_ptr; 422 int size, ret, i, stride; 423 div_t d; 424 425 /* Mode size at 32 bpp */ 426 stride = width * 4; 427 size = stride * height; 428 429 bo = drm_intel_bo_alloc(bufmgr, "frontbuffer", size, 4096); 430 if (!bo) { 431 fprintf(stderr, "failed to alloc buffer: %s\n", 432 strerror(errno)); 433 return -1; 434 } 435 436 ret = drm_intel_gem_bo_map_gtt(bo); 437 if (ret) { 438 fprintf(stderr, "failed to GTT map buffer: %s\n", 439 strerror(errno)); 440 return -1; 441 } 442 443 fb_ptr = bo->virtual; 444 445 /* paint the buffer with colored tiles */ 446 for (i = 0; i < width * height; i++) { 447 d = div(i, width); 448 fb_ptr[i] = 0x00130502 * (d.quot >> 6) + 0x000a1120 * (d.rem >> 6); 449 } 450 drm_intel_gem_bo_unmap_gtt(bo); 451 452 *bo_out = bo; 453 *stride_out = stride; 454 455 return 0; 456} 457 458#endif 459 460static void 461set_mode(struct connector *c, int count) 462{ 463 drmModeConnector *connector; 464 drmModeEncoder *encoder = NULL; 465 struct drm_mode_modeinfo *mode = NULL; 466 drm_intel_bufmgr *bufmgr; 467 drm_intel_bo *bo; 468 unsigned int fb_id; 469 int i, j, ret, width, height, x, stride; 470 471 width = 0; 472 height = 0; 473 for (i = 0; i < count; i++) { 474 connector_find_mode(&c[i]); 475 if (c[i].mode == NULL) 476 continue; 477 width += c[i].mode->hdisplay; 478 if (height < c[i].mode->vdisplay) 479 height = c[i].mode->vdisplay; 480 } 481 482 bufmgr = drm_intel_bufmgr_gem_init(fd, 2<<20); 483 if (!bufmgr) { 484 fprintf(stderr, "failed to init bufmgr: %s\n", strerror(errno)); 485 return; 486 } 487 488 if (create_test_buffer(bufmgr, width, height, &stride, &bo)) 489 return; 490 491 ret = drmModeAddFB(fd, width, height, 32, 32, stride, bo->handle, 492 &fb_id); 493 if (ret) { 494 fprintf(stderr, "failed to add fb: %s\n", strerror(errno)); 495 return; 496 } 497 498 x = 0; 499 for (i = 0; i < count; i++) { 500 int crtc_id; 501 if (c[i].mode == NULL) 502 continue; 503 504 printf("setting mode %s on connector %d, crtc %d\n", 505 c[i].mode_str, c[i].id, c[i].crtc); 506 507 ret = drmModeSetCrtc(fd, c[i].crtc, fb_id, x, 0, 508 &c[i].id, 1, c[i].mode); 509 x += c[i].mode->hdisplay; 510 511 if (ret) { 512 fprintf(stderr, "failed to set mode: %s\n", strerror(errno)); 513 return; 514 } 515 } 516} 517 518extern char *optarg; 519extern int optind, opterr, optopt; 520static char optstr[] = "ecpmfs:"; 521 522void usage(char *name) 523{ 524 fprintf(stderr, "usage: %s [-ecpmf]\n", name); 525 fprintf(stderr, "\t-e\tlist encoders\n"); 526 fprintf(stderr, "\t-c\tlist connectors\n"); 527 fprintf(stderr, "\t-p\tlist CRTCs (pipes)\n"); 528 fprintf(stderr, "\t-m\tlist modes\n"); 529 fprintf(stderr, "\t-f\tlist framebuffers\n"); 530 fprintf(stderr, "\t-s <connector_id>:<mode>\tset a mode\n"); 531 fprintf(stderr, "\t-s <connector_id>@<crtc_id>:<mode>\tset a mode\n"); 532 fprintf(stderr, "\n\tDefault is to dump all info.\n"); 533 exit(0); 534} 535 536#define dump_resource(res) if (res) dump_##res() 537 538int main(int argc, char **argv) 539{ 540 int c; 541 int encoders = 0, connectors = 0, crtcs = 0, framebuffers = 0; 542 char *modules[] = { "i915", "radeon" }; 543 char *modeset = NULL, *mode, *connector; 544 int i, connector_id, count = 0; 545 struct connector con_args[2]; 546 547 opterr = 0; 548 while ((c = getopt(argc, argv, optstr)) != -1) { 549 switch (c) { 550 case 'e': 551 encoders = 1; 552 break; 553 case 'c': 554 connectors = 1; 555 break; 556 case 'p': 557 crtcs = 1; 558 break; 559 case 'm': 560 modes = 1; 561 break; 562 case 'f': 563 framebuffers = 1; 564 break; 565 case 's': 566 modeset = strdup(optarg); 567 con_args[count].crtc = -1; 568 if (sscanf(optarg, "%d:%64s", 569 &con_args[count].id, 570 &con_args[count].mode_str) != 2 && 571 sscanf(optarg, "%d@%d:%64s", 572 &con_args[count].id, 573 &con_args[count].crtc, 574 &con_args[count].mode_str) != 3) 575 usage(argv[0]); 576 count++; 577 break; 578 default: 579 usage(argv[0]); 580 break; 581 } 582 } 583 584 if (argc == 1) 585 encoders = connectors = crtcs = modes = framebuffers = 1; 586 587 for (i = 0; i < ARRAY_SIZE(modules); i++) { 588 printf("trying to load module %s...", modules[i]); 589 fd = drmOpen(modules[i], NULL); 590 if (fd < 0) { 591 printf("failed.\n"); 592 } else { 593 printf("success.\n"); 594 break; 595 } 596 } 597 598 if (i == ARRAY_SIZE(modules)) { 599 fprintf(stderr, "failed to load any modules, aborting.\n"); 600 return -1; 601 } 602 603 resources = drmModeGetResources(fd); 604 if (!resources) { 605 fprintf(stderr, "drmModeGetResources failed: %s\n", 606 strerror(errno)); 607 drmClose(fd); 608 return 1; 609 } 610 611 dump_resource(encoders); 612 dump_resource(connectors); 613 dump_resource(crtcs); 614 dump_resource(framebuffers); 615 616 if (count > 0) { 617 set_mode(con_args, count); 618 getchar(); 619 } 620 621 drmModeFreeResources(resources); 622 623 return 0; 624} 625