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 */
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"
42 #include <assert.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <stdint.h>
46 #include <inttypes.h>
47 #include <unistd.h>
48 #include <string.h>
49 #include <errno.h>
50 #include <sys/poll.h>
51 #include <sys/time.h>
53 #include "xf86drm.h"
54 #include "xf86drmMode.h"
55 #include "drm_fourcc.h"
56 #include "libkms.h"
58 #ifdef HAVE_CAIRO
59 #include <math.h>
60 #include <cairo.h>
61 #endif
63 drmModeRes *resources;
64 int fd, modes;
66 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
68 struct type_name {
69 int type;
70 char *name;
71 };
73 #define type_name_fn(res) \
74 char * res##_str(int type) { \
75 unsigned int i; \
76 for (i = 0; i < ARRAY_SIZE(res##_names); i++) { \
77 if (res##_names[i].type == type) \
78 return res##_names[i].name; \
79 } \
80 return "(invalid)"; \
81 }
83 struct type_name encoder_type_names[] = {
84 { DRM_MODE_ENCODER_NONE, "none" },
85 { DRM_MODE_ENCODER_DAC, "DAC" },
86 { DRM_MODE_ENCODER_TMDS, "TMDS" },
87 { DRM_MODE_ENCODER_LVDS, "LVDS" },
88 { DRM_MODE_ENCODER_TVDAC, "TVDAC" },
89 };
91 type_name_fn(encoder_type)
93 struct type_name connector_status_names[] = {
94 { DRM_MODE_CONNECTED, "connected" },
95 { DRM_MODE_DISCONNECTED, "disconnected" },
96 { DRM_MODE_UNKNOWNCONNECTION, "unknown" },
97 };
99 type_name_fn(connector_status)
101 struct type_name connector_type_names[] = {
102 { DRM_MODE_CONNECTOR_Unknown, "unknown" },
103 { DRM_MODE_CONNECTOR_VGA, "VGA" },
104 { DRM_MODE_CONNECTOR_DVII, "DVI-I" },
105 { DRM_MODE_CONNECTOR_DVID, "DVI-D" },
106 { DRM_MODE_CONNECTOR_DVIA, "DVI-A" },
107 { DRM_MODE_CONNECTOR_Composite, "composite" },
108 { DRM_MODE_CONNECTOR_SVIDEO, "s-video" },
109 { DRM_MODE_CONNECTOR_LVDS, "LVDS" },
110 { DRM_MODE_CONNECTOR_Component, "component" },
111 { DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN" },
112 { DRM_MODE_CONNECTOR_DisplayPort, "displayport" },
113 { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" },
114 { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" },
115 { DRM_MODE_CONNECTOR_TV, "TV" },
116 { DRM_MODE_CONNECTOR_eDP, "embedded displayport" },
117 };
119 type_name_fn(connector_type)
121 void dump_encoders(void)
122 {
123 drmModeEncoder *encoder;
124 int i;
126 printf("Encoders:\n");
127 printf("id\tcrtc\ttype\tpossible crtcs\tpossible clones\t\n");
128 for (i = 0; i < resources->count_encoders; i++) {
129 encoder = drmModeGetEncoder(fd, resources->encoders[i]);
131 if (!encoder) {
132 fprintf(stderr, "could not get encoder %i: %s\n",
133 resources->encoders[i], strerror(errno));
134 continue;
135 }
136 printf("%d\t%d\t%s\t0x%08x\t0x%08x\n",
137 encoder->encoder_id,
138 encoder->crtc_id,
139 encoder_type_str(encoder->encoder_type),
140 encoder->possible_crtcs,
141 encoder->possible_clones);
142 drmModeFreeEncoder(encoder);
143 }
144 printf("\n");
145 }
147 void dump_mode(drmModeModeInfo *mode)
148 {
149 printf("\t%s %d %d %d %d %d %d %d %d %d\n",
150 mode->name,
151 mode->vrefresh,
152 mode->hdisplay,
153 mode->hsync_start,
154 mode->hsync_end,
155 mode->htotal,
156 mode->vdisplay,
157 mode->vsync_start,
158 mode->vsync_end,
159 mode->vtotal);
160 }
162 static void
163 dump_blob(uint32_t blob_id)
164 {
165 uint32_t i;
166 unsigned char *blob_data;
167 drmModePropertyBlobPtr blob;
169 blob = drmModeGetPropertyBlob(fd, blob_id);
170 if (!blob)
171 return;
173 blob_data = blob->data;
175 for (i = 0; i < blob->length; i++) {
176 if (i % 16 == 0)
177 printf("\n\t\t\t");
178 printf("%.2hhx", blob_data[i]);
179 }
180 printf("\n");
182 drmModeFreePropertyBlob(blob);
183 }
185 static void
186 dump_prop(uint32_t prop_id, uint64_t value)
187 {
188 int i;
189 drmModePropertyPtr prop;
191 prop = drmModeGetProperty(fd, prop_id);
193 printf("\t%d", prop_id);
194 if (!prop) {
195 printf("\n");
196 return;
197 }
199 printf(" %s:\n", prop->name);
201 printf("\t\tflags:");
202 if (prop->flags & DRM_MODE_PROP_PENDING)
203 printf(" pending");
204 if (prop->flags & DRM_MODE_PROP_RANGE)
205 printf(" range");
206 if (prop->flags & DRM_MODE_PROP_IMMUTABLE)
207 printf(" immutable");
208 if (prop->flags & DRM_MODE_PROP_ENUM)
209 printf(" enum");
210 if (prop->flags & DRM_MODE_PROP_BLOB)
211 printf(" blob");
212 printf("\n");
214 if (prop->flags & DRM_MODE_PROP_RANGE) {
215 printf("\t\tvalues:");
216 for (i = 0; i < prop->count_values; i++)
217 printf(" %"PRIu64, prop->values[i]);
218 printf("\n");
219 }
221 if (prop->flags & DRM_MODE_PROP_ENUM) {
222 printf("\t\tenums:");
223 for (i = 0; i < prop->count_enums; i++)
224 printf(" %s=%llu", prop->enums[i].name,
225 prop->enums[i].value);
226 printf("\n");
227 } else {
228 assert(prop->count_enums == 0);
229 }
231 if (prop->flags & DRM_MODE_PROP_BLOB) {
232 printf("\t\tblobs:\n");
233 for (i = 0; i < prop->count_blobs; i++)
234 dump_blob(prop->blob_ids[i]);
235 printf("\n");
236 } else {
237 assert(prop->count_blobs == 0);
238 }
240 printf("\t\tvalue:");
241 if (prop->flags & DRM_MODE_PROP_BLOB)
242 dump_blob(value);
243 else
244 printf(" %"PRIu64"\n", value);
246 drmModeFreeProperty(prop);
247 }
249 void dump_connectors(void)
250 {
251 drmModeConnector *connector;
252 int i, j;
254 printf("Connectors:\n");
255 printf("id\tencoder\tstatus\t\ttype\tsize (mm)\tmodes\tencoders\n");
256 for (i = 0; i < resources->count_connectors; i++) {
257 connector = drmModeGetConnector(fd, resources->connectors[i]);
259 if (!connector) {
260 fprintf(stderr, "could not get connector %i: %s\n",
261 resources->connectors[i], strerror(errno));
262 continue;
263 }
265 printf("%d\t%d\t%s\t%s\t%dx%d\t\t%d\t",
266 connector->connector_id,
267 connector->encoder_id,
268 connector_status_str(connector->connection),
269 connector_type_str(connector->connector_type),
270 connector->mmWidth, connector->mmHeight,
271 connector->count_modes);
273 for (j = 0; j < connector->count_encoders; j++)
274 printf("%s%d", j > 0 ? ", " : "", connector->encoders[j]);
275 printf("\n");
277 if (connector->count_modes) {
278 printf(" modes:\n");
279 printf("\tname refresh (Hz) hdisp hss hse htot vdisp "
280 "vss vse vtot)\n");
281 for (j = 0; j < connector->count_modes; j++)
282 dump_mode(&connector->modes[j]);
284 printf(" props:\n");
285 for (j = 0; j < connector->count_props; j++)
286 dump_prop(connector->props[j],
287 connector->prop_values[j]);
288 }
290 drmModeFreeConnector(connector);
291 }
292 printf("\n");
293 }
295 void dump_crtcs(void)
296 {
297 drmModeCrtc *crtc;
298 int i;
300 printf("CRTCs:\n");
301 printf("id\tfb\tpos\tsize\n");
302 for (i = 0; i < resources->count_crtcs; i++) {
303 crtc = drmModeGetCrtc(fd, resources->crtcs[i]);
305 if (!crtc) {
306 fprintf(stderr, "could not get crtc %i: %s\n",
307 resources->crtcs[i], strerror(errno));
308 continue;
309 }
310 printf("%d\t%d\t(%d,%d)\t(%dx%d)\n",
311 crtc->crtc_id,
312 crtc->buffer_id,
313 crtc->x, crtc->y,
314 crtc->width, crtc->height);
315 dump_mode(&crtc->mode);
317 drmModeFreeCrtc(crtc);
318 }
319 printf("\n");
320 }
322 void dump_framebuffers(void)
323 {
324 drmModeFB *fb;
325 int i;
327 printf("Frame buffers:\n");
328 printf("id\tsize\tpitch\n");
329 for (i = 0; i < resources->count_fbs; i++) {
330 fb = drmModeGetFB(fd, resources->fbs[i]);
332 if (!fb) {
333 fprintf(stderr, "could not get fb %i: %s\n",
334 resources->fbs[i], strerror(errno));
335 continue;
336 }
337 printf("%u\t(%ux%u)\t%u\n",
338 fb->fb_id,
339 fb->width, fb->height,
340 fb->pitch);
342 drmModeFreeFB(fb);
343 }
344 printf("\n");
345 }
347 static void dump_planes(void)
348 {
349 drmModePlaneRes *plane_resources;
350 drmModePlane *ovr;
351 unsigned int i, j;
353 plane_resources = drmModeGetPlaneResources(fd);
354 if (!plane_resources) {
355 fprintf(stderr, "drmModeGetPlaneResources failed: %s\n",
356 strerror(errno));
357 return;
358 }
360 printf("Planes:\n");
361 printf("id\tcrtc\tfb\tCRTC x,y\tx,y\tgamma size\n");
362 for (i = 0; i < plane_resources->count_planes; i++) {
363 ovr = drmModeGetPlane(fd, plane_resources->planes[i]);
364 if (!ovr) {
365 fprintf(stderr, "drmModeGetPlane failed: %s\n",
366 strerror(errno));
367 continue;
368 }
370 printf("%d\t%d\t%d\t%d,%d\t\t%d,%d\t%d\n",
371 ovr->plane_id, ovr->crtc_id, ovr->fb_id,
372 ovr->crtc_x, ovr->crtc_y, ovr->x, ovr->y,
373 ovr->gamma_size);
375 if (!ovr->count_formats)
376 continue;
378 printf(" formats:");
379 for (j = 0; j < ovr->count_formats; j++)
380 printf(" %4.4s", (char *)&ovr->formats[j]);
381 printf("\n");
383 drmModeFreePlane(ovr);
384 }
385 printf("\n");
387 drmModeFreePlaneResources(plane_resources);
388 return;
389 }
391 /*
392 * Mode setting with the kernel interfaces is a bit of a chore.
393 * First you have to find the connector in question and make sure the
394 * requested mode is available.
395 * Then you need to find the encoder attached to that connector so you
396 * can bind it with a free crtc.
397 */
398 struct connector {
399 uint32_t id;
400 char mode_str[64];
401 drmModeModeInfo *mode;
402 drmModeEncoder *encoder;
403 int crtc;
404 int pipe;
405 unsigned int fb_id[2], current_fb_id;
406 struct timeval start;
408 int swap_count;
409 };
411 struct plane {
412 uint32_t con_id; /* the id of connector to bind to */
413 uint32_t w, h;
414 unsigned int fb_id;
415 char format_str[5]; /* need to leave room for terminating \0 */
416 };
418 static void
419 connector_find_mode(struct connector *c)
420 {
421 drmModeConnector *connector;
422 int i, j;
424 /* First, find the connector & mode */
425 c->mode = NULL;
426 for (i = 0; i < resources->count_connectors; i++) {
427 connector = drmModeGetConnector(fd, resources->connectors[i]);
429 if (!connector) {
430 fprintf(stderr, "could not get connector %i: %s\n",
431 resources->connectors[i], strerror(errno));
432 drmModeFreeConnector(connector);
433 continue;
434 }
436 if (!connector->count_modes) {
437 drmModeFreeConnector(connector);
438 continue;
439 }
441 if (connector->connector_id != c->id) {
442 drmModeFreeConnector(connector);
443 continue;
444 }
446 for (j = 0; j < connector->count_modes; j++) {
447 c->mode = &connector->modes[j];
448 if (!strcmp(c->mode->name, c->mode_str))
449 break;
450 }
452 /* Found it, break out */
453 if (c->mode)
454 break;
456 drmModeFreeConnector(connector);
457 }
459 if (!c->mode) {
460 fprintf(stderr, "failed to find mode \"%s\"\n", c->mode_str);
461 return;
462 }
464 /* Now get the encoder */
465 for (i = 0; i < resources->count_encoders; i++) {
466 c->encoder = drmModeGetEncoder(fd, resources->encoders[i]);
468 if (!c->encoder) {
469 fprintf(stderr, "could not get encoder %i: %s\n",
470 resources->encoders[i], strerror(errno));
471 drmModeFreeEncoder(c->encoder);
472 continue;
473 }
475 if (c->encoder->encoder_id == connector->encoder_id)
476 break;
478 drmModeFreeEncoder(c->encoder);
479 }
481 if (c->crtc == -1)
482 c->crtc = c->encoder->crtc_id;
484 /* and figure out which crtc index it is: */
485 for (i = 0; i < resources->count_crtcs; i++) {
486 if (c->crtc == resources->crtcs[i]) {
487 c->pipe = i;
488 break;
489 }
490 }
492 }
494 static struct kms_bo *
495 allocate_buffer(struct kms_driver *kms,
496 int width, int height, int *stride)
497 {
498 struct kms_bo *bo;
499 unsigned bo_attribs[] = {
500 KMS_WIDTH, 0,
501 KMS_HEIGHT, 0,
502 KMS_BO_TYPE, KMS_BO_TYPE_SCANOUT_X8R8G8B8,
503 KMS_TERMINATE_PROP_LIST
504 };
505 int ret;
507 bo_attribs[1] = width;
508 bo_attribs[3] = height;
510 ret = kms_bo_create(kms, bo_attribs, &bo);
511 if (ret) {
512 fprintf(stderr, "failed to alloc buffer: %s\n",
513 strerror(-ret));
514 return NULL;
515 }
517 ret = kms_bo_get_prop(bo, KMS_PITCH, stride);
518 if (ret) {
519 fprintf(stderr, "failed to retreive buffer stride: %s\n",
520 strerror(-ret));
521 kms_bo_destroy(&bo);
522 return NULL;
523 }
525 return bo;
526 }
528 static void
529 make_pwetty(void *data, int width, int height, int stride)
530 {
531 #ifdef HAVE_CAIRO
532 cairo_surface_t *surface;
533 cairo_t *cr;
534 int x, y;
536 surface = cairo_image_surface_create_for_data(data,
537 CAIRO_FORMAT_ARGB32,
538 width, height,
539 stride);
540 cr = cairo_create(surface);
541 cairo_surface_destroy(surface);
543 cairo_set_line_cap(cr, CAIRO_LINE_CAP_SQUARE);
544 for (x = 0; x < width; x += 250)
545 for (y = 0; y < height; y += 250) {
546 char buf[64];
548 cairo_move_to(cr, x, y - 20);
549 cairo_line_to(cr, x, y + 20);
550 cairo_move_to(cr, x - 20, y);
551 cairo_line_to(cr, x + 20, y);
552 cairo_new_sub_path(cr);
553 cairo_arc(cr, x, y, 10, 0, M_PI * 2);
554 cairo_set_line_width(cr, 4);
555 cairo_set_source_rgb(cr, 0, 0, 0);
556 cairo_stroke_preserve(cr);
557 cairo_set_source_rgb(cr, 1, 1, 1);
558 cairo_set_line_width(cr, 2);
559 cairo_stroke(cr);
561 snprintf(buf, sizeof buf, "%d, %d", x, y);
562 cairo_move_to(cr, x + 20, y + 20);
563 cairo_text_path(cr, buf);
564 cairo_set_source_rgb(cr, 0, 0, 0);
565 cairo_stroke_preserve(cr);
566 cairo_set_source_rgb(cr, 1, 1, 1);
567 cairo_fill(cr);
568 }
570 cairo_destroy(cr);
571 #endif
572 }
574 static int
575 create_test_buffer(struct kms_driver *kms,
576 int width, int height, int *stride_out,
577 struct kms_bo **bo_out)
578 {
579 struct kms_bo *bo;
580 int ret, i, j, stride;
581 void *virtual;
583 bo = allocate_buffer(kms, width, height, &stride);
584 if (!bo)
585 return -1;
587 ret = kms_bo_map(bo, &virtual);
588 if (ret) {
589 fprintf(stderr, "failed to map buffer: %s\n",
590 strerror(-ret));
591 kms_bo_destroy(&bo);
592 return -1;
593 }
595 /* paint the buffer with colored tiles */
596 for (j = 0; j < height; j++) {
597 uint32_t *fb_ptr = (uint32_t*)((char*)virtual + j * stride);
598 for (i = 0; i < width; i++) {
599 div_t d = div(i, width);
600 fb_ptr[i] =
601 0x00130502 * (d.quot >> 6) +
602 0x000a1120 * (d.rem >> 6);
603 }
604 }
606 make_pwetty(virtual, width, height, stride);
608 kms_bo_unmap(bo);
610 *bo_out = bo;
611 *stride_out = stride;
612 return 0;
613 }
615 static int
616 create_grey_buffer(struct kms_driver *kms,
617 int width, int height, int *stride_out,
618 struct kms_bo **bo_out)
619 {
620 struct kms_bo *bo;
621 int size, ret, stride;
622 void *virtual;
624 bo = allocate_buffer(kms, width, height, &stride);
625 if (!bo)
626 return -1;
628 ret = kms_bo_map(bo, &virtual);
629 if (ret) {
630 fprintf(stderr, "failed to map buffer: %s\n",
631 strerror(-ret));
632 kms_bo_destroy(&bo);
633 return -1;
634 }
636 size = stride * height;
637 memset(virtual, 0x77, size);
638 kms_bo_unmap(bo);
640 *bo_out = bo;
641 *stride_out = stride;
643 return 0;
644 }
646 void
647 page_flip_handler(int fd, unsigned int frame,
648 unsigned int sec, unsigned int usec, void *data)
649 {
650 struct connector *c;
651 unsigned int new_fb_id;
652 struct timeval end;
653 double t;
655 c = data;
656 if (c->current_fb_id == c->fb_id[0])
657 new_fb_id = c->fb_id[1];
658 else
659 new_fb_id = c->fb_id[0];
661 drmModePageFlip(fd, c->crtc, new_fb_id,
662 DRM_MODE_PAGE_FLIP_EVENT, c);
663 c->current_fb_id = new_fb_id;
664 c->swap_count++;
665 if (c->swap_count == 60) {
666 gettimeofday(&end, NULL);
667 t = end.tv_sec + end.tv_usec * 1e-6 -
668 (c->start.tv_sec + c->start.tv_usec * 1e-6);
669 fprintf(stderr, "freq: %.02fHz\n", c->swap_count / t);
670 c->swap_count = 0;
671 c->start = end;
672 }
673 }
675 /* swap these for big endian.. */
676 #define RED 2
677 #define GREEN 1
678 #define BLUE 0
680 static void
681 fill420(unsigned char *y, unsigned char *u, unsigned char *v,
682 int cs /*chroma pixel stride */,
683 int n, int width, int height, int stride)
684 {
685 int i, j;
687 /* paint the buffer with colored tiles, in blocks of 2x2 */
688 for (j = 0; j < height; j+=2) {
689 unsigned char *y1p = y + j * stride;
690 unsigned char *y2p = y1p + stride;
691 unsigned char *up = u + (j/2) * stride * cs / 2;
692 unsigned char *vp = v + (j/2) * stride * cs / 2;
694 for (i = 0; i < width; i+=2) {
695 div_t d = div(n+i+j, width);
696 uint32_t rgb = 0x00130502 * (d.quot >> 6) + 0x000a1120 * (d.rem >> 6);
697 unsigned char *rgbp = (unsigned char *)&rgb;
698 unsigned char y = (0.299 * rgbp[RED]) + (0.587 * rgbp[GREEN]) + (0.114 * rgbp[BLUE]);
700 *(y2p++) = *(y1p++) = y;
701 *(y2p++) = *(y1p++) = y;
703 *up = (rgbp[BLUE] - y) * 0.565 + 128;
704 *vp = (rgbp[RED] - y) * 0.713 + 128;
705 up += cs;
706 vp += cs;
707 }
708 }
709 }
711 static void
712 fill422(unsigned char *virtual, int n, int width, int height, int stride)
713 {
714 int i, j;
715 /* paint the buffer with colored tiles */
716 for (j = 0; j < height; j++) {
717 uint8_t *ptr = (uint8_t*)((char*)virtual + j * stride);
718 for (i = 0; i < width; i++) {
719 div_t d = div(n+i+j, width);
720 uint32_t rgb = 0x00130502 * (d.quot >> 6) + 0x000a1120 * (d.rem >> 6);
721 unsigned char *rgbp = (unsigned char *)&rgb;
722 unsigned char y = (0.299 * rgbp[RED]) + (0.587 * rgbp[GREEN]) + (0.114 * rgbp[BLUE]);
724 *(ptr++) = y;
725 *(ptr++) = (rgbp[BLUE] - y) * 0.565 + 128;
726 *(ptr++) = y;
727 *(ptr++) = (rgbp[RED] - y) * 0.713 + 128;
728 }
729 }
730 }
732 static void
733 fill1555(unsigned char *virtual, int n, int width, int height, int stride)
734 {
735 int i, j;
736 /* paint the buffer with colored tiles */
737 for (j = 0; j < height; j++) {
738 uint16_t *ptr = (uint16_t*)((char*)virtual + j * stride);
739 for (i = 0; i < width; i++) {
740 div_t d = div(n+i+j, width);
741 uint32_t rgb = 0x00130502 * (d.quot >> 6) + 0x000a1120 * (d.rem >> 6);
742 unsigned char *rgbp = (unsigned char *)&rgb;
744 *(ptr++) = 0x8000 |
745 (rgbp[RED] >> 3) << 10 |
746 (rgbp[GREEN] >> 3) << 5 |
747 (rgbp[BLUE] >> 3);
748 }
749 }
750 }
752 static int
753 set_plane(struct kms_driver *kms, struct connector *c, struct plane *p)
754 {
755 drmModePlaneRes *plane_resources;
756 drmModePlane *ovr;
757 uint32_t handles[4], pitches[4], offsets[4] = {0}; /* we only use [0] */
758 uint32_t plane_id = 0;
759 struct kms_bo *plane_bo;
760 uint32_t plane_flags = 0, format;
761 int ret, crtc_x, crtc_y, crtc_w, crtc_h;
762 unsigned int i;
764 /* find an unused plane which can be connected to our crtc */
765 plane_resources = drmModeGetPlaneResources(fd);
766 if (!plane_resources) {
767 fprintf(stderr, "drmModeGetPlaneResources failed: %s\n",
768 strerror(errno));
769 return -1;
770 }
772 for (i = 0; i < plane_resources->count_planes && !plane_id; i++) {
773 ovr = drmModeGetPlane(fd, plane_resources->planes[i]);
774 if (!ovr) {
775 fprintf(stderr, "drmModeGetPlane failed: %s\n",
776 strerror(errno));
777 return -1;
778 }
780 if ((ovr->possible_crtcs & (1 << c->pipe)) && !ovr->crtc_id)
781 plane_id = ovr->plane_id;
783 drmModeFreePlane(ovr);
784 }
786 fprintf(stderr, "testing %dx%d@%s overlay plane\n",
787 p->w, p->h, p->format_str);
789 if (!plane_id) {
790 fprintf(stderr, "failed to find plane!\n");
791 return -1;
792 }
794 if (!strcmp(p->format_str, "XR24")) {
795 if (create_test_buffer(kms, p->w, p->h, &pitches[0], &plane_bo))
796 return -1;
797 kms_bo_get_prop(plane_bo, KMS_HANDLE, &handles[0]);
798 format = DRM_FORMAT_XRGB8888;
799 } else {
800 void *virtual;
802 /* TODO: this always allocates a buffer for 32bpp RGB.. but for
803 * YUV formats, we don't use all of it.. since 4bytes/pixel is
804 * worst case, so live with it for now and just don't use all
805 * the buffer:
806 */
807 plane_bo = allocate_buffer(kms, p->w, p->h, &pitches[0]);
808 if (!plane_bo)
809 return -1;
811 ret = kms_bo_map(plane_bo, &virtual);
812 if (ret) {
813 fprintf(stderr, "failed to map buffer: %s\n",
814 strerror(-ret));
815 kms_bo_destroy(&plane_bo);
816 return -1;
817 }
819 /* just testing a limited # of formats to test single
820 * and multi-planar path.. would be nice to add more..
821 */
822 if (!strcmp(p->format_str, "YUYV")) {
823 pitches[0] = p->w * 2;
824 offsets[0] = 0;
825 kms_bo_get_prop(plane_bo, KMS_HANDLE, &handles[0]);
827 fill422(virtual, 0, p->w, p->h, pitches[0]);
829 format = DRM_FORMAT_YUYV;
830 } else if (!strcmp(p->format_str, "NV12")) {
831 pitches[0] = p->w;
832 offsets[0] = 0;
833 kms_bo_get_prop(plane_bo, KMS_HANDLE, &handles[0]);
834 pitches[1] = p->w;
835 offsets[1] = p->w * p->h;
836 kms_bo_get_prop(plane_bo, KMS_HANDLE, &handles[1]);
838 fill420(virtual, virtual+offsets[1], virtual+offsets[1]+1,
839 2, 0, p->w, p->h, pitches[0]);
841 format = DRM_FORMAT_NV12;
842 } else if (!strcmp(p->format_str, "YV12")) {
843 pitches[0] = p->w;
844 offsets[0] = 0;
845 kms_bo_get_prop(plane_bo, KMS_HANDLE, &handles[0]);
846 pitches[1] = p->w / 2;
847 offsets[1] = p->w * p->h;
848 kms_bo_get_prop(plane_bo, KMS_HANDLE, &handles[1]);
849 pitches[2] = p->w / 2;
850 offsets[2] = offsets[1] + (p->w * p->h) / 4;
851 kms_bo_get_prop(plane_bo, KMS_HANDLE, &handles[2]);
853 fill420(virtual, virtual+offsets[1], virtual+offsets[2],
854 1, 0, p->w, p->h, pitches[0]);
856 format = DRM_FORMAT_YVU420;
857 } else if (!strcmp(p->format_str, "XR15")) {
858 pitches[0] = p->w * 2;
859 offsets[0] = 0;
860 kms_bo_get_prop(plane_bo, KMS_HANDLE, &handles[0]);
862 fill1555(virtual, 0, p->w, p->h, pitches[0]);
864 format = DRM_FORMAT_XRGB1555;
865 } else if (!strcmp(p->format_str, "AR15")) {
866 pitches[0] = p->w * 2;
867 offsets[0] = 0;
868 kms_bo_get_prop(plane_bo, KMS_HANDLE, &handles[0]);
870 fill1555(virtual, 0, p->w, p->h, pitches[0]);
872 format = DRM_FORMAT_ARGB1555;
873 } else {
874 fprintf(stderr, "Unknown format: %s\n", p->format_str);
875 return -1;
876 }
878 kms_bo_unmap(plane_bo);
879 }
881 /* just use single plane format for now.. */
882 if (drmModeAddFB2(fd, p->w, p->h, format,
883 handles, pitches, offsets, &p->fb_id, plane_flags)) {
884 fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
885 return -1;
886 }
888 /* ok, boring.. but for now put in middle of screen: */
889 crtc_x = c->mode->hdisplay / 3;
890 crtc_y = c->mode->vdisplay / 3;
891 crtc_w = crtc_x;
892 crtc_h = crtc_y;
894 /* note src coords (last 4 args) are in Q16 format */
895 if (drmModeSetPlane(fd, plane_id, c->crtc, p->fb_id,
896 plane_flags, crtc_x, crtc_y, crtc_w, crtc_h,
897 0, 0, p->w << 16, p->h << 16)) {
898 fprintf(stderr, "failed to enable plane: %s\n",
899 strerror(errno));
900 return -1;
901 }
903 return 0;
904 }
906 static void
907 set_mode(struct connector *c, int count, struct plane *p, int plane_count,
908 int page_flip)
909 {
910 struct kms_driver *kms;
911 struct kms_bo *bo, *other_bo;
912 unsigned int fb_id, other_fb_id;
913 int i, j, ret, width, height, x, stride;
914 unsigned handle;
915 drmEventContext evctx;
917 width = 0;
918 height = 0;
919 for (i = 0; i < count; i++) {
920 connector_find_mode(&c[i]);
921 if (c[i].mode == NULL)
922 continue;
923 width += c[i].mode->hdisplay;
924 if (height < c[i].mode->vdisplay)
925 height = c[i].mode->vdisplay;
926 }
928 ret = kms_create(fd, &kms);
929 if (ret) {
930 fprintf(stderr, "failed to create kms driver: %s\n",
931 strerror(-ret));
932 return;
933 }
935 if (create_test_buffer(kms, width, height, &stride, &bo))
936 return;
938 kms_bo_get_prop(bo, KMS_HANDLE, &handle);
939 ret = drmModeAddFB(fd, width, height, 24, 32, stride, handle, &fb_id);
940 if (ret) {
941 fprintf(stderr, "failed to add fb (%ux%u): %s\n",
942 width, height, strerror(errno));
943 return;
944 }
946 x = 0;
947 for (i = 0; i < count; i++) {
948 if (c[i].mode == NULL)
949 continue;
951 printf("setting mode %s on connector %d, crtc %d\n",
952 c[i].mode_str, c[i].id, c[i].crtc);
954 ret = drmModeSetCrtc(fd, c[i].crtc, fb_id, x, 0,
955 &c[i].id, 1, c[i].mode);
957 /* XXX: Actually check if this is needed */
958 drmModeDirtyFB(fd, fb_id, NULL, 0);
960 x += c[i].mode->hdisplay;
962 if (ret) {
963 fprintf(stderr, "failed to set mode: %s\n", strerror(errno));
964 return;
965 }
967 /* if we have a plane/overlay to show, set that up now: */
968 for (j = 0; j < plane_count; j++)
969 if (p[j].con_id == c[i].id)
970 if (set_plane(kms, &c[i], &p[j]))
971 return;
972 }
974 if (!page_flip)
975 return;
977 if (create_grey_buffer(kms, width, height, &stride, &other_bo))
978 return;
980 kms_bo_get_prop(other_bo, KMS_HANDLE, &handle);
981 ret = drmModeAddFB(fd, width, height, 32, 32, stride, handle,
982 &other_fb_id);
983 if (ret) {
984 fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
985 return;
986 }
988 for (i = 0; i < count; i++) {
989 if (c[i].mode == NULL)
990 continue;
992 ret = drmModePageFlip(fd, c[i].crtc, other_fb_id,
993 DRM_MODE_PAGE_FLIP_EVENT, &c[i]);
994 if (ret) {
995 fprintf(stderr, "failed to page flip: %s\n", strerror(errno));
996 return;
997 }
998 gettimeofday(&c[i].start, NULL);
999 c[i].swap_count = 0;
1000 c[i].fb_id[0] = fb_id;
1001 c[i].fb_id[1] = other_fb_id;
1002 c[i].current_fb_id = other_fb_id;
1003 }
1005 memset(&evctx, 0, sizeof evctx);
1006 evctx.version = DRM_EVENT_CONTEXT_VERSION;
1007 evctx.vblank_handler = NULL;
1008 evctx.page_flip_handler = page_flip_handler;
1010 while (1) {
1011 #if 0
1012 struct pollfd pfd[2];
1014 pfd[0].fd = 0;
1015 pfd[0].events = POLLIN;
1016 pfd[1].fd = fd;
1017 pfd[1].events = POLLIN;
1019 if (poll(pfd, 2, -1) < 0) {
1020 fprintf(stderr, "poll error\n");
1021 break;
1022 }
1024 if (pfd[0].revents)
1025 break;
1026 #else
1027 struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 };
1028 fd_set fds;
1029 int ret;
1031 FD_ZERO(&fds);
1032 FD_SET(0, &fds);
1033 FD_SET(fd, &fds);
1034 ret = select(fd + 1, &fds, NULL, NULL, &timeout);
1036 if (ret <= 0) {
1037 fprintf(stderr, "select timed out or error (ret %d)\n",
1038 ret);
1039 continue;
1040 } else if (FD_ISSET(0, &fds)) {
1041 break;
1042 }
1043 #endif
1045 drmHandleEvent(fd, &evctx);
1046 }
1048 kms_bo_destroy(&bo);
1049 kms_bo_destroy(&other_bo);
1050 kms_destroy(&kms);
1051 }
1053 extern char *optarg;
1054 extern int optind, opterr, optopt;
1055 static char optstr[] = "ecpmfs:P:v";
1057 void usage(char *name)
1058 {
1059 fprintf(stderr, "usage: %s [-ecpmf]\n", name);
1060 fprintf(stderr, "\t-e\tlist encoders\n");
1061 fprintf(stderr, "\t-c\tlist connectors\n");
1062 fprintf(stderr, "\t-p\tlist CRTCs and planes (pipes)\n");
1063 fprintf(stderr, "\t-m\tlist modes\n");
1064 fprintf(stderr, "\t-f\tlist framebuffers\n");
1065 fprintf(stderr, "\t-v\ttest vsynced page flipping\n");
1066 fprintf(stderr, "\t-s <connector_id>:<mode>\tset a mode\n");
1067 fprintf(stderr, "\t-s <connector_id>@<crtc_id>:<mode>\tset a mode\n");
1068 fprintf(stderr, "\t-P <connector_id>:<w>x<h>\tset a plane\n");
1069 fprintf(stderr, "\t-P <connector_id>:<w>x<h>@<format>\tset a plane\n");
1070 fprintf(stderr, "\n\tDefault is to dump all info.\n");
1071 exit(0);
1072 }
1074 #define dump_resource(res) if (res) dump_##res()
1076 static int page_flipping_supported(void)
1077 {
1078 /*FIXME: generic ioctl needed? */
1079 return 1;
1080 #if 0
1081 int ret, value;
1082 struct drm_i915_getparam gp;
1084 gp.param = I915_PARAM_HAS_PAGEFLIPPING;
1085 gp.value = &value;
1087 ret = drmCommandWriteRead(fd, DRM_I915_GETPARAM, &gp, sizeof(gp));
1088 if (ret) {
1089 fprintf(stderr, "drm_i915_getparam: %m\n");
1090 return 0;
1091 }
1093 return *gp.value;
1094 #endif
1095 }
1097 int main(int argc, char **argv)
1098 {
1099 int c;
1100 int encoders = 0, connectors = 0, crtcs = 0, planes = 0, framebuffers = 0;
1101 int test_vsync = 0;
1102 char *modules[] = { "i915", "radeon", "nouveau", "vmwgfx", "omapdrm" };
1103 unsigned int i;
1104 int count = 0, plane_count = 0;
1105 struct connector con_args[2];
1106 struct plane plane_args[2] = {0};
1108 opterr = 0;
1109 while ((c = getopt(argc, argv, optstr)) != -1) {
1110 switch (c) {
1111 case 'e':
1112 encoders = 1;
1113 break;
1114 case 'c':
1115 connectors = 1;
1116 break;
1117 case 'p':
1118 crtcs = 1;
1119 planes = 1;
1120 break;
1121 case 'm':
1122 modes = 1;
1123 break;
1124 case 'f':
1125 framebuffers = 1;
1126 break;
1127 case 'v':
1128 test_vsync = 1;
1129 break;
1130 case 's':
1131 con_args[count].crtc = -1;
1132 if (sscanf(optarg, "%d:%64s",
1133 &con_args[count].id,
1134 con_args[count].mode_str) != 2 &&
1135 sscanf(optarg, "%d@%d:%64s",
1136 &con_args[count].id,
1137 &con_args[count].crtc,
1138 con_args[count].mode_str) != 3)
1139 usage(argv[0]);
1140 count++;
1141 break;
1142 case 'P':
1143 strcpy(plane_args[plane_count].format_str, "XR24");
1144 if (sscanf(optarg, "%d:%dx%d@%4s",
1145 &plane_args[plane_count].con_id,
1146 &plane_args[plane_count].w,
1147 &plane_args[plane_count].h,
1148 plane_args[plane_count].format_str) != 4 &&
1149 sscanf(optarg, "%d:%dx%d",
1150 &plane_args[plane_count].con_id,
1151 &plane_args[plane_count].w,
1152 &plane_args[plane_count].h) != 3)
1153 usage(argv[0]);
1154 plane_count++;
1155 break;
1156 default:
1157 usage(argv[0]);
1158 break;
1159 }
1160 }
1162 if (argc == 1)
1163 encoders = connectors = crtcs = planes = modes = framebuffers = 1;
1165 for (i = 0; i < ARRAY_SIZE(modules); i++) {
1166 printf("trying to load module %s...", modules[i]);
1167 fd = drmOpen(modules[i], NULL);
1168 if (fd < 0) {
1169 printf("failed.\n");
1170 } else {
1171 printf("success.\n");
1172 break;
1173 }
1174 }
1176 if (test_vsync && !page_flipping_supported()) {
1177 fprintf(stderr, "page flipping not supported by drm.\n");
1178 return -1;
1179 }
1181 if (i == ARRAY_SIZE(modules)) {
1182 fprintf(stderr, "failed to load any modules, aborting.\n");
1183 return -1;
1184 }
1186 resources = drmModeGetResources(fd);
1187 if (!resources) {
1188 fprintf(stderr, "drmModeGetResources failed: %s\n",
1189 strerror(errno));
1190 drmClose(fd);
1191 return 1;
1192 }
1194 dump_resource(encoders);
1195 dump_resource(connectors);
1196 dump_resource(crtcs);
1197 dump_resource(planes);
1198 dump_resource(framebuffers);
1200 if (count > 0) {
1201 set_mode(con_args, count, plane_args, plane_count, test_vsync);
1202 getchar();
1203 }
1205 drmModeFreeResources(resources);
1207 return 0;
1208 }