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_BITMASK)
211 printf(" bitmask");
212 if (prop->flags & DRM_MODE_PROP_BLOB)
213 printf(" blob");
214 printf("\n");
216 if (prop->flags & DRM_MODE_PROP_RANGE) {
217 printf("\t\tvalues:");
218 for (i = 0; i < prop->count_values; i++)
219 printf(" %"PRIu64, prop->values[i]);
220 printf("\n");
221 }
223 if (prop->flags & DRM_MODE_PROP_ENUM) {
224 printf("\t\tenums:");
225 for (i = 0; i < prop->count_enums; i++)
226 printf(" %s=%llu", prop->enums[i].name,
227 prop->enums[i].value);
228 printf("\n");
229 } else if (prop->flags & DRM_MODE_PROP_BITMASK) {
230 printf("\t\tvalues:");
231 for (i = 0; i < prop->count_enums; i++)
232 printf(" %s=0x%llx", prop->enums[i].name,
233 (1LL << prop->enums[i].value));
234 printf("\n");
235 } else {
236 assert(prop->count_enums == 0);
237 }
239 if (prop->flags & DRM_MODE_PROP_BLOB) {
240 printf("\t\tblobs:\n");
241 for (i = 0; i < prop->count_blobs; i++)
242 dump_blob(prop->blob_ids[i]);
243 printf("\n");
244 } else {
245 assert(prop->count_blobs == 0);
246 }
248 printf("\t\tvalue:");
249 if (prop->flags & DRM_MODE_PROP_BLOB)
250 dump_blob(value);
251 else
252 printf(" %"PRIu64"\n", value);
254 drmModeFreeProperty(prop);
255 }
257 void dump_connectors(void)
258 {
259 drmModeConnector *connector;
260 int i, j;
262 printf("Connectors:\n");
263 printf("id\tencoder\tstatus\t\ttype\tsize (mm)\tmodes\tencoders\n");
264 for (i = 0; i < resources->count_connectors; i++) {
265 connector = drmModeGetConnector(fd, resources->connectors[i]);
267 if (!connector) {
268 fprintf(stderr, "could not get connector %i: %s\n",
269 resources->connectors[i], strerror(errno));
270 continue;
271 }
273 printf("%d\t%d\t%s\t%s\t%dx%d\t\t%d\t",
274 connector->connector_id,
275 connector->encoder_id,
276 connector_status_str(connector->connection),
277 connector_type_str(connector->connector_type),
278 connector->mmWidth, connector->mmHeight,
279 connector->count_modes);
281 for (j = 0; j < connector->count_encoders; j++)
282 printf("%s%d", j > 0 ? ", " : "", connector->encoders[j]);
283 printf("\n");
285 if (connector->count_modes) {
286 printf(" modes:\n");
287 printf("\tname refresh (Hz) hdisp hss hse htot vdisp "
288 "vss vse vtot)\n");
289 for (j = 0; j < connector->count_modes; j++)
290 dump_mode(&connector->modes[j]);
292 printf(" props:\n");
293 for (j = 0; j < connector->count_props; j++)
294 dump_prop(connector->props[j],
295 connector->prop_values[j]);
296 }
298 drmModeFreeConnector(connector);
299 }
300 printf("\n");
301 }
303 void dump_crtcs(void)
304 {
305 drmModeCrtc *crtc;
306 drmModeObjectPropertiesPtr props;
307 int i;
308 uint32_t j;
310 printf("CRTCs:\n");
311 printf("id\tfb\tpos\tsize\n");
312 for (i = 0; i < resources->count_crtcs; i++) {
313 crtc = drmModeGetCrtc(fd, resources->crtcs[i]);
315 if (!crtc) {
316 fprintf(stderr, "could not get crtc %i: %s\n",
317 resources->crtcs[i], strerror(errno));
318 continue;
319 }
320 printf("%d\t%d\t(%d,%d)\t(%dx%d)\n",
321 crtc->crtc_id,
322 crtc->buffer_id,
323 crtc->x, crtc->y,
324 crtc->width, crtc->height);
325 dump_mode(&crtc->mode);
327 printf(" props:\n");
328 props = drmModeObjectGetProperties(fd, crtc->crtc_id,
329 DRM_MODE_OBJECT_CRTC);
330 if (props) {
331 for (j = 0; j < props->count_props; j++)
332 dump_prop(props->props[j],
333 props->prop_values[j]);
334 drmModeFreeObjectProperties(props);
335 } else {
336 printf("\tcould not get crtc properties: %s\n",
337 strerror(errno));
338 }
340 drmModeFreeCrtc(crtc);
341 }
342 printf("\n");
343 }
345 void dump_framebuffers(void)
346 {
347 drmModeFB *fb;
348 int i;
350 printf("Frame buffers:\n");
351 printf("id\tsize\tpitch\n");
352 for (i = 0; i < resources->count_fbs; i++) {
353 fb = drmModeGetFB(fd, resources->fbs[i]);
355 if (!fb) {
356 fprintf(stderr, "could not get fb %i: %s\n",
357 resources->fbs[i], strerror(errno));
358 continue;
359 }
360 printf("%u\t(%ux%u)\t%u\n",
361 fb->fb_id,
362 fb->width, fb->height,
363 fb->pitch);
365 drmModeFreeFB(fb);
366 }
367 printf("\n");
368 }
370 static void dump_planes(void)
371 {
372 drmModePlaneRes *plane_resources;
373 drmModePlane *ovr;
374 unsigned int i, j;
376 plane_resources = drmModeGetPlaneResources(fd);
377 if (!plane_resources) {
378 fprintf(stderr, "drmModeGetPlaneResources failed: %s\n",
379 strerror(errno));
380 return;
381 }
383 printf("Planes:\n");
384 printf("id\tcrtc\tfb\tCRTC x,y\tx,y\tgamma size\n");
385 for (i = 0; i < plane_resources->count_planes; i++) {
386 ovr = drmModeGetPlane(fd, plane_resources->planes[i]);
387 if (!ovr) {
388 fprintf(stderr, "drmModeGetPlane failed: %s\n",
389 strerror(errno));
390 continue;
391 }
393 printf("%d\t%d\t%d\t%d,%d\t\t%d,%d\t%d\n",
394 ovr->plane_id, ovr->crtc_id, ovr->fb_id,
395 ovr->crtc_x, ovr->crtc_y, ovr->x, ovr->y,
396 ovr->gamma_size);
398 if (!ovr->count_formats)
399 continue;
401 printf(" formats:");
402 for (j = 0; j < ovr->count_formats; j++)
403 printf(" %4.4s", (char *)&ovr->formats[j]);
404 printf("\n");
406 drmModeFreePlane(ovr);
407 }
408 printf("\n");
410 drmModeFreePlaneResources(plane_resources);
411 return;
412 }
414 /*
415 * Mode setting with the kernel interfaces is a bit of a chore.
416 * First you have to find the connector in question and make sure the
417 * requested mode is available.
418 * Then you need to find the encoder attached to that connector so you
419 * can bind it with a free crtc.
420 */
421 struct connector {
422 uint32_t id;
423 char mode_str[64];
424 drmModeModeInfo *mode;
425 drmModeEncoder *encoder;
426 int crtc;
427 int pipe;
428 unsigned int fb_id[2], current_fb_id;
429 struct timeval start;
431 int swap_count;
432 };
434 struct plane {
435 uint32_t con_id; /* the id of connector to bind to */
436 uint32_t w, h;
437 unsigned int fb_id;
438 char format_str[5]; /* need to leave room for terminating \0 */
439 };
441 static void
442 connector_find_mode(struct connector *c)
443 {
444 drmModeConnector *connector;
445 int i, j;
447 /* First, find the connector & mode */
448 c->mode = NULL;
449 for (i = 0; i < resources->count_connectors; i++) {
450 connector = drmModeGetConnector(fd, resources->connectors[i]);
452 if (!connector) {
453 fprintf(stderr, "could not get connector %i: %s\n",
454 resources->connectors[i], strerror(errno));
455 drmModeFreeConnector(connector);
456 continue;
457 }
459 if (!connector->count_modes) {
460 drmModeFreeConnector(connector);
461 continue;
462 }
464 if (connector->connector_id != c->id) {
465 drmModeFreeConnector(connector);
466 continue;
467 }
469 for (j = 0; j < connector->count_modes; j++) {
470 c->mode = &connector->modes[j];
471 if (!strcmp(c->mode->name, c->mode_str))
472 break;
473 }
475 /* Found it, break out */
476 if (c->mode)
477 break;
479 drmModeFreeConnector(connector);
480 }
482 if (!c->mode) {
483 fprintf(stderr, "failed to find mode \"%s\"\n", c->mode_str);
484 return;
485 }
487 /* Now get the encoder */
488 for (i = 0; i < resources->count_encoders; i++) {
489 c->encoder = drmModeGetEncoder(fd, resources->encoders[i]);
491 if (!c->encoder) {
492 fprintf(stderr, "could not get encoder %i: %s\n",
493 resources->encoders[i], strerror(errno));
494 drmModeFreeEncoder(c->encoder);
495 continue;
496 }
498 if (c->encoder->encoder_id == connector->encoder_id)
499 break;
501 drmModeFreeEncoder(c->encoder);
502 }
504 if (c->crtc == -1)
505 c->crtc = c->encoder->crtc_id;
507 /* and figure out which crtc index it is: */
508 for (i = 0; i < resources->count_crtcs; i++) {
509 if (c->crtc == resources->crtcs[i]) {
510 c->pipe = i;
511 break;
512 }
513 }
515 }
517 static struct kms_bo *
518 allocate_buffer(struct kms_driver *kms,
519 int width, int height, int *stride)
520 {
521 struct kms_bo *bo;
522 unsigned bo_attribs[] = {
523 KMS_WIDTH, 0,
524 KMS_HEIGHT, 0,
525 KMS_BO_TYPE, KMS_BO_TYPE_SCANOUT_X8R8G8B8,
526 KMS_TERMINATE_PROP_LIST
527 };
528 int ret;
530 bo_attribs[1] = width;
531 bo_attribs[3] = height;
533 ret = kms_bo_create(kms, bo_attribs, &bo);
534 if (ret) {
535 fprintf(stderr, "failed to alloc buffer: %s\n",
536 strerror(-ret));
537 return NULL;
538 }
540 ret = kms_bo_get_prop(bo, KMS_PITCH, stride);
541 if (ret) {
542 fprintf(stderr, "failed to retreive buffer stride: %s\n",
543 strerror(-ret));
544 kms_bo_destroy(&bo);
545 return NULL;
546 }
548 return bo;
549 }
551 static void
552 make_pwetty(void *data, int width, int height, int stride)
553 {
554 #ifdef HAVE_CAIRO
555 cairo_surface_t *surface;
556 cairo_t *cr;
557 int x, y;
559 surface = cairo_image_surface_create_for_data(data,
560 CAIRO_FORMAT_ARGB32,
561 width, height,
562 stride);
563 cr = cairo_create(surface);
564 cairo_surface_destroy(surface);
566 cairo_set_line_cap(cr, CAIRO_LINE_CAP_SQUARE);
567 for (x = 0; x < width; x += 250)
568 for (y = 0; y < height; y += 250) {
569 char buf[64];
571 cairo_move_to(cr, x, y - 20);
572 cairo_line_to(cr, x, y + 20);
573 cairo_move_to(cr, x - 20, y);
574 cairo_line_to(cr, x + 20, y);
575 cairo_new_sub_path(cr);
576 cairo_arc(cr, x, y, 10, 0, M_PI * 2);
577 cairo_set_line_width(cr, 4);
578 cairo_set_source_rgb(cr, 0, 0, 0);
579 cairo_stroke_preserve(cr);
580 cairo_set_source_rgb(cr, 1, 1, 1);
581 cairo_set_line_width(cr, 2);
582 cairo_stroke(cr);
584 snprintf(buf, sizeof buf, "%d, %d", x, y);
585 cairo_move_to(cr, x + 20, y + 20);
586 cairo_text_path(cr, buf);
587 cairo_set_source_rgb(cr, 0, 0, 0);
588 cairo_stroke_preserve(cr);
589 cairo_set_source_rgb(cr, 1, 1, 1);
590 cairo_fill(cr);
591 }
593 cairo_destroy(cr);
594 #endif
595 }
597 static int
598 create_test_buffer(struct kms_driver *kms,
599 int width, int height, int *stride_out,
600 struct kms_bo **bo_out)
601 {
602 struct kms_bo *bo;
603 int ret, i, j, stride;
604 void *virtual;
606 bo = allocate_buffer(kms, width, height, &stride);
607 if (!bo)
608 return -1;
610 ret = kms_bo_map(bo, &virtual);
611 if (ret) {
612 fprintf(stderr, "failed to map buffer: %s\n",
613 strerror(-ret));
614 kms_bo_destroy(&bo);
615 return -1;
616 }
618 /* paint the buffer with colored tiles */
619 for (j = 0; j < height; j++) {
620 uint32_t *fb_ptr = (uint32_t*)((char*)virtual + j * stride);
621 for (i = 0; i < width; i++) {
622 div_t d = div(i, width);
623 fb_ptr[i] =
624 0x00130502 * (d.quot >> 6) +
625 0x000a1120 * (d.rem >> 6);
626 }
627 }
629 make_pwetty(virtual, width, height, stride);
631 kms_bo_unmap(bo);
633 *bo_out = bo;
634 *stride_out = stride;
635 return 0;
636 }
638 static int
639 create_grey_buffer(struct kms_driver *kms,
640 int width, int height, int *stride_out,
641 struct kms_bo **bo_out)
642 {
643 struct kms_bo *bo;
644 int size, ret, stride;
645 void *virtual;
647 bo = allocate_buffer(kms, width, height, &stride);
648 if (!bo)
649 return -1;
651 ret = kms_bo_map(bo, &virtual);
652 if (ret) {
653 fprintf(stderr, "failed to map buffer: %s\n",
654 strerror(-ret));
655 kms_bo_destroy(&bo);
656 return -1;
657 }
659 size = stride * height;
660 memset(virtual, 0x77, size);
661 kms_bo_unmap(bo);
663 *bo_out = bo;
664 *stride_out = stride;
666 return 0;
667 }
669 void
670 page_flip_handler(int fd, unsigned int frame,
671 unsigned int sec, unsigned int usec, void *data)
672 {
673 struct connector *c;
674 unsigned int new_fb_id;
675 struct timeval end;
676 double t;
678 c = data;
679 if (c->current_fb_id == c->fb_id[0])
680 new_fb_id = c->fb_id[1];
681 else
682 new_fb_id = c->fb_id[0];
684 drmModePageFlip(fd, c->crtc, new_fb_id,
685 DRM_MODE_PAGE_FLIP_EVENT, c);
686 c->current_fb_id = new_fb_id;
687 c->swap_count++;
688 if (c->swap_count == 60) {
689 gettimeofday(&end, NULL);
690 t = end.tv_sec + end.tv_usec * 1e-6 -
691 (c->start.tv_sec + c->start.tv_usec * 1e-6);
692 fprintf(stderr, "freq: %.02fHz\n", c->swap_count / t);
693 c->swap_count = 0;
694 c->start = end;
695 }
696 }
698 /* swap these for big endian.. */
699 #define RED 2
700 #define GREEN 1
701 #define BLUE 0
703 static void
704 fill420(unsigned char *y, unsigned char *u, unsigned char *v,
705 int cs /*chroma pixel stride */,
706 int n, int width, int height, int stride)
707 {
708 int i, j;
710 /* paint the buffer with colored tiles, in blocks of 2x2 */
711 for (j = 0; j < height; j+=2) {
712 unsigned char *y1p = y + j * stride;
713 unsigned char *y2p = y1p + stride;
714 unsigned char *up = u + (j/2) * stride * cs / 2;
715 unsigned char *vp = v + (j/2) * stride * cs / 2;
717 for (i = 0; i < width; i+=2) {
718 div_t d = div(n+i+j, width);
719 uint32_t rgb = 0x00130502 * (d.quot >> 6) + 0x000a1120 * (d.rem >> 6);
720 unsigned char *rgbp = (unsigned char *)&rgb;
721 unsigned char y = (0.299 * rgbp[RED]) + (0.587 * rgbp[GREEN]) + (0.114 * rgbp[BLUE]);
723 *(y2p++) = *(y1p++) = y;
724 *(y2p++) = *(y1p++) = y;
726 *up = (rgbp[BLUE] - y) * 0.565 + 128;
727 *vp = (rgbp[RED] - y) * 0.713 + 128;
728 up += cs;
729 vp += cs;
730 }
731 }
732 }
734 static void
735 fill422(unsigned char *virtual, int n, int width, int height, int stride)
736 {
737 int i, j;
738 /* paint the buffer with colored tiles */
739 for (j = 0; j < height; j++) {
740 uint8_t *ptr = (uint8_t*)((char*)virtual + j * stride);
741 for (i = 0; i < width; i++) {
742 div_t d = div(n+i+j, width);
743 uint32_t rgb = 0x00130502 * (d.quot >> 6) + 0x000a1120 * (d.rem >> 6);
744 unsigned char *rgbp = (unsigned char *)&rgb;
745 unsigned char y = (0.299 * rgbp[RED]) + (0.587 * rgbp[GREEN]) + (0.114 * rgbp[BLUE]);
747 *(ptr++) = y;
748 *(ptr++) = (rgbp[BLUE] - y) * 0.565 + 128;
749 *(ptr++) = y;
750 *(ptr++) = (rgbp[RED] - y) * 0.713 + 128;
751 }
752 }
753 }
755 static void
756 fill1555(unsigned char *virtual, int n, int width, int height, int stride)
757 {
758 int i, j;
759 /* paint the buffer with colored tiles */
760 for (j = 0; j < height; j++) {
761 uint16_t *ptr = (uint16_t*)((char*)virtual + j * stride);
762 for (i = 0; i < width; i++) {
763 div_t d = div(n+i+j, width);
764 uint32_t rgb = 0x00130502 * (d.quot >> 6) + 0x000a1120 * (d.rem >> 6);
765 unsigned char *rgbp = (unsigned char *)&rgb;
767 *(ptr++) = 0x8000 |
768 (rgbp[RED] >> 3) << 10 |
769 (rgbp[GREEN] >> 3) << 5 |
770 (rgbp[BLUE] >> 3);
771 }
772 }
773 }
775 static int
776 set_plane(struct kms_driver *kms, struct connector *c, struct plane *p)
777 {
778 drmModePlaneRes *plane_resources;
779 drmModePlane *ovr;
780 uint32_t handles[4], pitches[4], offsets[4] = {0}; /* we only use [0] */
781 uint32_t plane_id = 0;
782 struct kms_bo *plane_bo;
783 uint32_t plane_flags = 0, format;
784 int ret, crtc_x, crtc_y, crtc_w, crtc_h;
785 unsigned int i;
787 /* find an unused plane which can be connected to our crtc */
788 plane_resources = drmModeGetPlaneResources(fd);
789 if (!plane_resources) {
790 fprintf(stderr, "drmModeGetPlaneResources failed: %s\n",
791 strerror(errno));
792 return -1;
793 }
795 for (i = 0; i < plane_resources->count_planes && !plane_id; i++) {
796 ovr = drmModeGetPlane(fd, plane_resources->planes[i]);
797 if (!ovr) {
798 fprintf(stderr, "drmModeGetPlane failed: %s\n",
799 strerror(errno));
800 return -1;
801 }
803 if ((ovr->possible_crtcs & (1 << c->pipe)) && !ovr->crtc_id)
804 plane_id = ovr->plane_id;
806 drmModeFreePlane(ovr);
807 }
809 fprintf(stderr, "testing %dx%d@%s overlay plane\n",
810 p->w, p->h, p->format_str);
812 if (!plane_id) {
813 fprintf(stderr, "failed to find plane!\n");
814 return -1;
815 }
817 if (!strcmp(p->format_str, "XR24")) {
818 if (create_test_buffer(kms, p->w, p->h, &pitches[0], &plane_bo))
819 return -1;
820 kms_bo_get_prop(plane_bo, KMS_HANDLE, &handles[0]);
821 format = DRM_FORMAT_XRGB8888;
822 } else {
823 void *virtual;
825 /* TODO: this always allocates a buffer for 32bpp RGB.. but for
826 * YUV formats, we don't use all of it.. since 4bytes/pixel is
827 * worst case, so live with it for now and just don't use all
828 * the buffer:
829 */
830 plane_bo = allocate_buffer(kms, p->w, p->h, &pitches[0]);
831 if (!plane_bo)
832 return -1;
834 ret = kms_bo_map(plane_bo, &virtual);
835 if (ret) {
836 fprintf(stderr, "failed to map buffer: %s\n",
837 strerror(-ret));
838 kms_bo_destroy(&plane_bo);
839 return -1;
840 }
842 /* just testing a limited # of formats to test single
843 * and multi-planar path.. would be nice to add more..
844 */
845 if (!strcmp(p->format_str, "YUYV")) {
846 pitches[0] = p->w * 2;
847 offsets[0] = 0;
848 kms_bo_get_prop(plane_bo, KMS_HANDLE, &handles[0]);
850 fill422(virtual, 0, p->w, p->h, pitches[0]);
852 format = DRM_FORMAT_YUYV;
853 } else if (!strcmp(p->format_str, "NV12")) {
854 pitches[0] = p->w;
855 offsets[0] = 0;
856 kms_bo_get_prop(plane_bo, KMS_HANDLE, &handles[0]);
857 pitches[1] = p->w;
858 offsets[1] = p->w * p->h;
859 kms_bo_get_prop(plane_bo, KMS_HANDLE, &handles[1]);
861 fill420(virtual, virtual+offsets[1], virtual+offsets[1]+1,
862 2, 0, p->w, p->h, pitches[0]);
864 format = DRM_FORMAT_NV12;
865 } else if (!strcmp(p->format_str, "YV12")) {
866 pitches[0] = p->w;
867 offsets[0] = 0;
868 kms_bo_get_prop(plane_bo, KMS_HANDLE, &handles[0]);
869 pitches[1] = p->w / 2;
870 offsets[1] = p->w * p->h;
871 kms_bo_get_prop(plane_bo, KMS_HANDLE, &handles[1]);
872 pitches[2] = p->w / 2;
873 offsets[2] = offsets[1] + (p->w * p->h) / 4;
874 kms_bo_get_prop(plane_bo, KMS_HANDLE, &handles[2]);
876 fill420(virtual, virtual+offsets[1], virtual+offsets[2],
877 1, 0, p->w, p->h, pitches[0]);
879 format = DRM_FORMAT_YVU420;
880 } else if (!strcmp(p->format_str, "XR15")) {
881 pitches[0] = p->w * 2;
882 offsets[0] = 0;
883 kms_bo_get_prop(plane_bo, KMS_HANDLE, &handles[0]);
885 fill1555(virtual, 0, p->w, p->h, pitches[0]);
887 format = DRM_FORMAT_XRGB1555;
888 } else if (!strcmp(p->format_str, "AR15")) {
889 pitches[0] = p->w * 2;
890 offsets[0] = 0;
891 kms_bo_get_prop(plane_bo, KMS_HANDLE, &handles[0]);
893 fill1555(virtual, 0, p->w, p->h, pitches[0]);
895 format = DRM_FORMAT_ARGB1555;
896 } else {
897 fprintf(stderr, "Unknown format: %s\n", p->format_str);
898 return -1;
899 }
901 kms_bo_unmap(plane_bo);
902 }
904 /* just use single plane format for now.. */
905 if (drmModeAddFB2(fd, p->w, p->h, format,
906 handles, pitches, offsets, &p->fb_id, plane_flags)) {
907 fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
908 return -1;
909 }
911 /* ok, boring.. but for now put in middle of screen: */
912 crtc_x = c->mode->hdisplay / 3;
913 crtc_y = c->mode->vdisplay / 3;
914 crtc_w = crtc_x;
915 crtc_h = crtc_y;
917 /* note src coords (last 4 args) are in Q16 format */
918 if (drmModeSetPlane(fd, plane_id, c->crtc, p->fb_id,
919 plane_flags, crtc_x, crtc_y, crtc_w, crtc_h,
920 0, 0, p->w << 16, p->h << 16)) {
921 fprintf(stderr, "failed to enable plane: %s\n",
922 strerror(errno));
923 return -1;
924 }
926 return 0;
927 }
929 static void
930 set_mode(struct connector *c, int count, struct plane *p, int plane_count,
931 int page_flip)
932 {
933 struct kms_driver *kms;
934 struct kms_bo *bo, *other_bo;
935 unsigned int fb_id, other_fb_id;
936 int i, j, ret, width, height, x, stride;
937 unsigned handle;
938 drmEventContext evctx;
940 width = 0;
941 height = 0;
942 for (i = 0; i < count; i++) {
943 connector_find_mode(&c[i]);
944 if (c[i].mode == NULL)
945 continue;
946 width += c[i].mode->hdisplay;
947 if (height < c[i].mode->vdisplay)
948 height = c[i].mode->vdisplay;
949 }
951 ret = kms_create(fd, &kms);
952 if (ret) {
953 fprintf(stderr, "failed to create kms driver: %s\n",
954 strerror(-ret));
955 return;
956 }
958 if (create_test_buffer(kms, width, height, &stride, &bo))
959 return;
961 kms_bo_get_prop(bo, KMS_HANDLE, &handle);
962 ret = drmModeAddFB(fd, width, height, 24, 32, stride, handle, &fb_id);
963 if (ret) {
964 fprintf(stderr, "failed to add fb (%ux%u): %s\n",
965 width, height, strerror(errno));
966 return;
967 }
969 x = 0;
970 for (i = 0; i < count; i++) {
971 if (c[i].mode == NULL)
972 continue;
974 printf("setting mode %s on connector %d, crtc %d\n",
975 c[i].mode_str, c[i].id, c[i].crtc);
977 ret = drmModeSetCrtc(fd, c[i].crtc, fb_id, x, 0,
978 &c[i].id, 1, c[i].mode);
980 /* XXX: Actually check if this is needed */
981 drmModeDirtyFB(fd, fb_id, NULL, 0);
983 x += c[i].mode->hdisplay;
985 if (ret) {
986 fprintf(stderr, "failed to set mode: %s\n", strerror(errno));
987 return;
988 }
990 /* if we have a plane/overlay to show, set that up now: */
991 for (j = 0; j < plane_count; j++)
992 if (p[j].con_id == c[i].id)
993 if (set_plane(kms, &c[i], &p[j]))
994 return;
995 }
997 if (!page_flip)
998 return;
1000 if (create_grey_buffer(kms, width, height, &stride, &other_bo))
1001 return;
1003 kms_bo_get_prop(other_bo, KMS_HANDLE, &handle);
1004 ret = drmModeAddFB(fd, width, height, 32, 32, stride, handle,
1005 &other_fb_id);
1006 if (ret) {
1007 fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
1008 return;
1009 }
1011 for (i = 0; i < count; i++) {
1012 if (c[i].mode == NULL)
1013 continue;
1015 ret = drmModePageFlip(fd, c[i].crtc, other_fb_id,
1016 DRM_MODE_PAGE_FLIP_EVENT, &c[i]);
1017 if (ret) {
1018 fprintf(stderr, "failed to page flip: %s\n", strerror(errno));
1019 return;
1020 }
1021 gettimeofday(&c[i].start, NULL);
1022 c[i].swap_count = 0;
1023 c[i].fb_id[0] = fb_id;
1024 c[i].fb_id[1] = other_fb_id;
1025 c[i].current_fb_id = other_fb_id;
1026 }
1028 memset(&evctx, 0, sizeof evctx);
1029 evctx.version = DRM_EVENT_CONTEXT_VERSION;
1030 evctx.vblank_handler = NULL;
1031 evctx.page_flip_handler = page_flip_handler;
1033 while (1) {
1034 #if 0
1035 struct pollfd pfd[2];
1037 pfd[0].fd = 0;
1038 pfd[0].events = POLLIN;
1039 pfd[1].fd = fd;
1040 pfd[1].events = POLLIN;
1042 if (poll(pfd, 2, -1) < 0) {
1043 fprintf(stderr, "poll error\n");
1044 break;
1045 }
1047 if (pfd[0].revents)
1048 break;
1049 #else
1050 struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 };
1051 fd_set fds;
1052 int ret;
1054 FD_ZERO(&fds);
1055 FD_SET(0, &fds);
1056 FD_SET(fd, &fds);
1057 ret = select(fd + 1, &fds, NULL, NULL, &timeout);
1059 if (ret <= 0) {
1060 fprintf(stderr, "select timed out or error (ret %d)\n",
1061 ret);
1062 continue;
1063 } else if (FD_ISSET(0, &fds)) {
1064 break;
1065 }
1066 #endif
1068 drmHandleEvent(fd, &evctx);
1069 }
1071 kms_bo_destroy(&bo);
1072 kms_bo_destroy(&other_bo);
1073 kms_destroy(&kms);
1074 }
1076 extern char *optarg;
1077 extern int optind, opterr, optopt;
1078 static char optstr[] = "ecpmfs:P:v";
1080 void usage(char *name)
1081 {
1082 fprintf(stderr, "usage: %s [-ecpmf]\n", name);
1083 fprintf(stderr, "\t-e\tlist encoders\n");
1084 fprintf(stderr, "\t-c\tlist connectors\n");
1085 fprintf(stderr, "\t-p\tlist CRTCs and planes (pipes)\n");
1086 fprintf(stderr, "\t-m\tlist modes\n");
1087 fprintf(stderr, "\t-f\tlist framebuffers\n");
1088 fprintf(stderr, "\t-v\ttest vsynced page flipping\n");
1089 fprintf(stderr, "\t-s <connector_id>:<mode>\tset a mode\n");
1090 fprintf(stderr, "\t-s <connector_id>@<crtc_id>:<mode>\tset a mode\n");
1091 fprintf(stderr, "\t-P <connector_id>:<w>x<h>\tset a plane\n");
1092 fprintf(stderr, "\t-P <connector_id>:<w>x<h>@<format>\tset a plane\n");
1093 fprintf(stderr, "\n\tDefault is to dump all info.\n");
1094 exit(0);
1095 }
1097 #define dump_resource(res) if (res) dump_##res()
1099 static int page_flipping_supported(void)
1100 {
1101 /*FIXME: generic ioctl needed? */
1102 return 1;
1103 #if 0
1104 int ret, value;
1105 struct drm_i915_getparam gp;
1107 gp.param = I915_PARAM_HAS_PAGEFLIPPING;
1108 gp.value = &value;
1110 ret = drmCommandWriteRead(fd, DRM_I915_GETPARAM, &gp, sizeof(gp));
1111 if (ret) {
1112 fprintf(stderr, "drm_i915_getparam: %m\n");
1113 return 0;
1114 }
1116 return *gp.value;
1117 #endif
1118 }
1120 int main(int argc, char **argv)
1121 {
1122 int c;
1123 int encoders = 0, connectors = 0, crtcs = 0, planes = 0, framebuffers = 0;
1124 int test_vsync = 0;
1125 char *modules[] = { "i915", "radeon", "nouveau", "vmwgfx", "omapdrm", "exynos" };
1126 unsigned int i;
1127 int count = 0, plane_count = 0;
1128 struct connector con_args[2];
1129 struct plane plane_args[2] = {0};
1131 opterr = 0;
1132 while ((c = getopt(argc, argv, optstr)) != -1) {
1133 switch (c) {
1134 case 'e':
1135 encoders = 1;
1136 break;
1137 case 'c':
1138 connectors = 1;
1139 break;
1140 case 'p':
1141 crtcs = 1;
1142 planes = 1;
1143 break;
1144 case 'm':
1145 modes = 1;
1146 break;
1147 case 'f':
1148 framebuffers = 1;
1149 break;
1150 case 'v':
1151 test_vsync = 1;
1152 break;
1153 case 's':
1154 con_args[count].crtc = -1;
1155 if (sscanf(optarg, "%d:%64s",
1156 &con_args[count].id,
1157 con_args[count].mode_str) != 2 &&
1158 sscanf(optarg, "%d@%d:%64s",
1159 &con_args[count].id,
1160 &con_args[count].crtc,
1161 con_args[count].mode_str) != 3)
1162 usage(argv[0]);
1163 count++;
1164 break;
1165 case 'P':
1166 strcpy(plane_args[plane_count].format_str, "XR24");
1167 if (sscanf(optarg, "%d:%dx%d@%4s",
1168 &plane_args[plane_count].con_id,
1169 &plane_args[plane_count].w,
1170 &plane_args[plane_count].h,
1171 plane_args[plane_count].format_str) != 4 &&
1172 sscanf(optarg, "%d:%dx%d",
1173 &plane_args[plane_count].con_id,
1174 &plane_args[plane_count].w,
1175 &plane_args[plane_count].h) != 3)
1176 usage(argv[0]);
1177 plane_count++;
1178 break;
1179 default:
1180 usage(argv[0]);
1181 break;
1182 }
1183 }
1185 if (argc == 1)
1186 encoders = connectors = crtcs = planes = modes = framebuffers = 1;
1188 for (i = 0; i < ARRAY_SIZE(modules); i++) {
1189 printf("trying to load module %s...", modules[i]);
1190 fd = drmOpen(modules[i], NULL);
1191 if (fd < 0) {
1192 printf("failed.\n");
1193 } else {
1194 printf("success.\n");
1195 break;
1196 }
1197 }
1199 if (test_vsync && !page_flipping_supported()) {
1200 fprintf(stderr, "page flipping not supported by drm.\n");
1201 return -1;
1202 }
1204 if (i == ARRAY_SIZE(modules)) {
1205 fprintf(stderr, "failed to load any modules, aborting.\n");
1206 return -1;
1207 }
1209 resources = drmModeGetResources(fd);
1210 if (!resources) {
1211 fprintf(stderr, "drmModeGetResources failed: %s\n",
1212 strerror(errno));
1213 drmClose(fd);
1214 return 1;
1215 }
1217 dump_resource(encoders);
1218 dump_resource(connectors);
1219 dump_resource(crtcs);
1220 dump_resource(planes);
1221 dump_resource(framebuffers);
1223 if (count > 0) {
1224 set_mode(con_args, count, plane_args, plane_count, test_vsync);
1225 getchar();
1226 }
1228 drmModeFreeResources(resources);
1230 return 0;
1231 }