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 
59 drmModeRes *resources;
60 int fd, modes;
61 
62 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
63 
64 struct type_name {
65 	int type;
66 	char *name;
67 };
68 
69 #define type_name_fn(res) \
70 char * 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 
79 struct 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 
87 type_name_fn(encoder_type)
88 
89 struct type_name connector_status_names[] = {
90 	{ DRM_MODE_CONNECTED, "connected" },
91 	{ DRM_MODE_DISCONNECTED, "disconnected" },
92 	{ DRM_MODE_UNKNOWNCONNECTION, "unknown" },
93 };
94 
95 type_name_fn(connector_status)
96 
97 struct 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 
type_name_fn(connector_type)113 type_name_fn(connector_type)
114 
115 void 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 
dump_mode(drmModeModeInfo * mode)141 void 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 
156 static void
dump_props(drmModeConnector * connector)157 dump_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 
dump_connectors(void)169 void 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 
dump_crtcs(void)210 void 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 
dump_framebuffers(void)237 void 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  */
268 struct connector {
269 	int id;
270 	char mode_str[64];
271 	drmModeModeInfo *mode;
272 	drmModeEncoder *encoder;
273 	int crtc;
274 };
275 
276 static void
connector_find_mode(struct connector * c)277 connector_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 
345 static int
create_test_buffer(drm_intel_bufmgr * bufmgr,int width,int height,int * stride_out,drm_intel_bo ** bo_out)346 create_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 
416 static int
create_test_buffer(drm_intel_bufmgr * bufmgr,int width,int height,int * stride_out,drm_intel_bo ** bo_out)417 create_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 
460 static void
set_mode(struct connector * c,int count)461 set_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 
518 extern char *optarg;
519 extern int optind, opterr, optopt;
520 static char optstr[] = "ecpmfs:";
521 
usage(char * name)522 void 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 
main(int argc,char ** argv)538 int 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