1 /*
2  * Copyright (C) 2013 Samsung Electronics Co.Ltd
3  * Authors:
4  *	Inki Dae <inki.dae@samsung.com>
5  *
6  * This program is free software; you can redistribute  it and/or modify it
7  * under  the terms of  the GNU General  Public License as published by the
8  * Free Software Foundation;  either version 2 of the  License, or (at your
9  * option) any later version.
10  *
11  */
12 
13 #ifdef HAVE_CONFIG_H
14 #include "config.h"
15 #endif
16 
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <string.h>
20 #include <errno.h>
21 
22 #include <sys/mman.h>
23 #include <linux/stddef.h>
24 
25 #include <xf86drm.h>
26 #include <xf86drmMode.h>
27 #include <libkms.h>
28 #include <drm_fourcc.h>
29 
30 #include "exynos_drm.h"
31 #include "exynos_drmif.h"
32 #include "fimg2d.h"
33 
34 #define DRM_MODULE_NAME		"exynos"
35 #define MAX_TEST_CASE		8
36 
37 static unsigned int screen_width, screen_height;
38 
39 /*
40  * A structure to test fimg2d hw.
41  *
42  * @solid_fild: fill given color data to source buffer.
43  * @copy: copy source to destination buffer.
44  * @copy_with_scale: copy source to destination buffer scaling up or
45  *	down properly.
46  * @blend: blend source to destination buffer.
47  */
48 struct fimg2d_test_case {
49 	int (*solid_fill)(struct exynos_device *dev, struct exynos_bo *dst);
50 	int (*copy)(struct exynos_device *dev, struct exynos_bo *src,
51 			struct exynos_bo *dst, enum e_g2d_buf_type);
52 	int (*copy_with_scale)(struct exynos_device *dev,
53 				struct exynos_bo *src, struct exynos_bo *dst,
54 				enum e_g2d_buf_type);
55 	int (*blend)(struct exynos_device *dev,
56 				struct exynos_bo *src, struct exynos_bo *dst,
57 				enum e_g2d_buf_type);
58 };
59 
60 struct connector {
61 	uint32_t id;
62 	char mode_str[64];
63 	char format_str[5];
64 	unsigned int fourcc;
65 	drmModeModeInfo *mode;
66 	drmModeEncoder *encoder;
67 	int crtc;
68 	int pipe;
69 	int plane_zpos;
70 	unsigned int fb_id[2], current_fb_id;
71 	struct timeval start;
72 
73 	int swap_count;
74 };
75 
connector_find_mode(int fd,struct connector * c,drmModeRes * resources)76 static void connector_find_mode(int fd, struct connector *c,
77 				drmModeRes *resources)
78 {
79 	drmModeConnector *connector;
80 	int i, j;
81 
82 	/* First, find the connector & mode */
83 	c->mode = NULL;
84 	for (i = 0; i < resources->count_connectors; i++) {
85 		connector = drmModeGetConnector(fd, resources->connectors[i]);
86 
87 		if (!connector) {
88 			fprintf(stderr, "could not get connector %i: %s\n",
89 				resources->connectors[i], strerror(errno));
90 			drmModeFreeConnector(connector);
91 			continue;
92 		}
93 
94 		if (!connector->count_modes) {
95 			drmModeFreeConnector(connector);
96 			continue;
97 		}
98 
99 		if (connector->connector_id != c->id) {
100 			drmModeFreeConnector(connector);
101 			continue;
102 		}
103 
104 		for (j = 0; j < connector->count_modes; j++) {
105 			c->mode = &connector->modes[j];
106 			if (!strcmp(c->mode->name, c->mode_str))
107 				break;
108 		}
109 
110 		/* Found it, break out */
111 		if (c->mode)
112 			break;
113 
114 		drmModeFreeConnector(connector);
115 	}
116 
117 	if (!c->mode) {
118 		fprintf(stderr, "failed to find mode \"%s\"\n", c->mode_str);
119 		return;
120 	}
121 
122 	/* Now get the encoder */
123 	for (i = 0; i < resources->count_encoders; i++) {
124 		c->encoder = drmModeGetEncoder(fd, resources->encoders[i]);
125 
126 		if (!c->encoder) {
127 			fprintf(stderr, "could not get encoder %i: %s\n",
128 				resources->encoders[i], strerror(errno));
129 			drmModeFreeEncoder(c->encoder);
130 			continue;
131 		}
132 
133 		if (c->encoder->encoder_id  == connector->encoder_id)
134 			break;
135 
136 		drmModeFreeEncoder(c->encoder);
137 	}
138 
139 	if (c->crtc == -1)
140 		c->crtc = c->encoder->crtc_id;
141 }
142 
connector_find_plane(int fd,unsigned int * plane_id)143 static int connector_find_plane(int fd, unsigned int *plane_id)
144 {
145 	drmModePlaneRes *plane_resources;
146 	drmModePlane *ovr;
147 	int i;
148 
149 	plane_resources = drmModeGetPlaneResources(fd);
150 	if (!plane_resources) {
151 		fprintf(stderr, "drmModeGetPlaneResources failed: %s\n",
152 			strerror(errno));
153 		return -1;
154 	}
155 
156 	for (i = 0; i < plane_resources->count_planes; i++) {
157 		plane_id[i] = 0;
158 
159 		ovr = drmModeGetPlane(fd, plane_resources->planes[i]);
160 		if (!ovr) {
161 			fprintf(stderr, "drmModeGetPlane failed: %s\n",
162 				strerror(errno));
163 			continue;
164 		}
165 
166 		if (ovr->possible_crtcs & (1 << 0))
167 			plane_id[i] = ovr->plane_id;
168 		drmModeFreePlane(ovr);
169 	}
170 
171 	return 0;
172 }
173 
drm_set_crtc(struct exynos_device * dev,struct connector * c,unsigned int fb_id)174 static int drm_set_crtc(struct exynos_device *dev, struct connector *c,
175 			unsigned int fb_id)
176 {
177 	int ret;
178 
179 	ret = drmModeSetCrtc(dev->fd, c->crtc,
180 			fb_id, 0, 0, &c->id, 1, c->mode);
181 	if (ret) {
182 		drmMsg("failed to set mode: %s\n", strerror(errno));
183 		goto err;
184 	}
185 
186 	return 0;
187 
188 err:
189 	return ret;
190 }
191 
exynos_create_buffer(struct exynos_device * dev,unsigned long size,unsigned int flags)192 static struct exynos_bo *exynos_create_buffer(struct exynos_device *dev,
193 						unsigned long size,
194 						unsigned int flags)
195 {
196 	struct exynos_bo *bo;
197 
198 	bo = exynos_bo_create(dev, size, flags);
199 	if (!bo)
200 		return bo;
201 
202 	if (!exynos_bo_map(bo)) {
203 		exynos_bo_destroy(bo);
204 		return NULL;
205 	}
206 
207 	return bo;
208 }
209 
exynos_destroy_buffer(struct exynos_bo * bo)210 static void exynos_destroy_buffer(struct exynos_bo *bo)
211 {
212 	exynos_bo_destroy(bo);
213 }
214 
g2d_solid_fill_test(struct exynos_device * dev,struct exynos_bo * dst)215 static int g2d_solid_fill_test(struct exynos_device *dev, struct exynos_bo *dst)
216 {
217 	struct g2d_context *ctx;
218 	struct g2d_image img;
219 	unsigned int count, img_w, img_h;
220 	int ret = 0;
221 
222 	ctx = g2d_init(dev->fd);
223 	if (!ctx)
224 		return -EFAULT;
225 
226 	memset(&img, 0, sizeof(struct g2d_image));
227 	img.bo[0] = dst->handle;
228 
229 	printf("soild fill test.\n");
230 
231 	srand(time(NULL));
232 	img_w = screen_width;
233 	img_h = screen_height;
234 
235 	for (count = 0; count < 2; count++) {
236 		unsigned int x, y, w, h;
237 
238 		x = rand() % (img_w / 2);
239 		y = rand() % (img_h / 2);
240 		w = rand() % (img_w - x);
241 		h = rand() % (img_h - y);
242 
243 		img.width = img_w;
244 		img.height = img_h;
245 		img.stride = img.width * 4;
246 		img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
247 		img.color = 0xff000000 + (random() & 0xffffff);
248 
249 		ret = g2d_solid_fill(ctx, &img, x, y, w, h);
250 		if (ret < 0)
251 			goto err_fini;
252 
253 		ret = g2d_exec(ctx);
254 		if (ret < 0)
255 			break;
256 	}
257 
258 err_fini:
259 	g2d_fini(ctx);
260 
261 	return ret;
262 }
263 
g2d_copy_test(struct exynos_device * dev,struct exynos_bo * src,struct exynos_bo * dst,enum e_g2d_buf_type type)264 static int g2d_copy_test(struct exynos_device *dev, struct exynos_bo *src,
265 				struct exynos_bo *dst,
266 				enum e_g2d_buf_type type)
267 {
268 	struct g2d_context *ctx;
269 	struct g2d_image src_img, dst_img;
270 	unsigned int count;
271 	unsigned int src_x, src_y, dst_x, dst_y, img_w, img_h;
272 	unsigned long userptr, size;
273 	int ret;
274 
275 	ctx = g2d_init(dev->fd);
276 	if (!ctx)
277 		return -EFAULT;
278 
279 	memset(&src_img, 0, sizeof(struct g2d_image));
280 	memset(&dst_img, 0, sizeof(struct g2d_image));
281 	dst_img.bo[0] = dst->handle;
282 
283 	src_x = 0;
284 	src_y = 0;
285 	dst_x = 0;
286 	dst_y = 0;
287 	img_w = screen_width;
288 	img_h = screen_height;
289 
290 	switch (type) {
291 	case G2D_IMGBUF_GEM:
292 		src_img.bo[0] = src->handle;
293 		break;
294 	case G2D_IMGBUF_USERPTR:
295 		size = img_w * img_h * 4;
296 
297 		userptr = (unsigned long)malloc(size);
298 		if (!userptr) {
299 			fprintf(stderr, "failed to allocate userptr.\n");
300 			return -EFAULT;
301 		}
302 
303 		src_img.user_ptr[0].userptr = userptr;
304 		src_img.user_ptr[0].size = size;
305 		break;
306 	default:
307 		type = G2D_IMGBUF_GEM;
308 		break;
309 	}
310 
311 	printf("copy test with %s.\n",
312 			type == G2D_IMGBUF_GEM ? "gem" : "userptr");
313 
314 	src_img.width = img_w;
315 	src_img.height = img_h;
316 	src_img.stride = src_img.width * 4;
317 	src_img.buf_type = type;
318 	src_img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
319 	src_img.color = 0xffff0000;
320 	ret = g2d_solid_fill(ctx, &src_img, src_x, src_y, img_w, img_h);
321 	if (ret < 0)
322 		goto err_free_userptr;
323 
324 	dst_img.width = img_w;
325 	dst_img.height = img_h;
326 	dst_img.stride = dst_img.width * 4;
327 	dst_img.buf_type = G2D_IMGBUF_GEM;
328 	dst_img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
329 
330 	ret = g2d_copy(ctx, &src_img, &dst_img, src_x, src_y, dst_x, dst_y,
331 			img_w - 4, img_h - 4);
332 	if (ret < 0)
333 		goto err_free_userptr;
334 
335 	g2d_exec(ctx);
336 
337 err_free_userptr:
338 	if (type == G2D_IMGBUF_USERPTR)
339 		if (userptr)
340 			free((void *)userptr);
341 
342 	g2d_fini(ctx);
343 
344 	return ret;
345 }
346 
g2d_copy_with_scale_test(struct exynos_device * dev,struct exynos_bo * src,struct exynos_bo * dst,enum e_g2d_buf_type type)347 static int g2d_copy_with_scale_test(struct exynos_device *dev,
348 					struct exynos_bo *src,
349 					struct exynos_bo *dst,
350 					enum e_g2d_buf_type type)
351 {
352 	struct g2d_context *ctx;
353 	struct g2d_image src_img, dst_img;
354 	unsigned int count;
355 	unsigned int src_x, src_y, dst_x, dst_y, img_w, img_h;
356 	unsigned long userptr, size;
357 	int ret;
358 
359 	ctx = g2d_init(dev->fd);
360 	if (!ctx)
361 		return -EFAULT;
362 
363 	memset(&src_img, 0, sizeof(struct g2d_image));
364 	memset(&dst_img, 0, sizeof(struct g2d_image));
365 	dst_img.bo[0] = dst->handle;
366 
367 	src_x = 0;
368 	src_y = 0;
369 	dst_x = 0;
370 	dst_y = 0;
371 	img_w = screen_width;
372 	img_h = screen_height;
373 
374 	switch (type) {
375 	case G2D_IMGBUF_GEM:
376 		src_img.bo[0] = src->handle;
377 		break;
378 	case G2D_IMGBUF_USERPTR:
379 		size = img_w * img_h * 4;
380 
381 		userptr = (unsigned long)malloc(size);
382 		if (!userptr) {
383 			fprintf(stderr, "failed to allocate userptr.\n");
384 			return -EFAULT;
385 		}
386 
387 		src_img.user_ptr[0].userptr = userptr;
388 		src_img.user_ptr[0].size = size;
389 		break;
390 	default:
391 		type = G2D_IMGBUF_GEM;
392 		break;
393 	}
394 
395 	printf("copy and scale test with %s.\n",
396 			type == G2D_IMGBUF_GEM ? "gem" : "userptr");
397 
398 	src_img.width = img_w;
399 	src_img.height = img_h;
400 	src_img.stride = src_img.width * 4;
401 	src_img.buf_type = type;
402 	src_img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
403 	src_img.color = 0xffffffff;
404 	ret = g2d_solid_fill(ctx, &src_img, src_x, src_y, img_w ,  img_h);
405 	if (ret < 0)
406 		goto err_free_userptr;
407 
408 	src_img.color = 0xff00ff00;
409 	ret = g2d_solid_fill(ctx, &src_img, 5, 5, 100, 100);
410 	if (ret < 0)
411 		goto err_free_userptr;
412 
413 	dst_img.width = img_w;
414 	dst_img.height = img_h;
415 	dst_img.buf_type = G2D_IMGBUF_GEM;
416 	dst_img.stride = dst_img.width * 4;
417 	dst_img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
418 
419 	ret = g2d_copy_with_scale(ctx, &src_img, &dst_img, 5, 5, 100, 100,
420 					100, 100, 200, 200, 0);
421 	if (ret < 0)
422 		goto err_free_userptr;
423 
424 	g2d_exec(ctx);
425 
426 err_free_userptr:
427 	if (type == G2D_IMGBUF_USERPTR)
428 		if (userptr)
429 			free((void *)userptr);
430 
431 	g2d_fini(ctx);
432 
433 	return 0;
434 }
435 
g2d_blend_test(struct exynos_device * dev,struct exynos_bo * src,struct exynos_bo * dst,enum e_g2d_buf_type type)436 static int g2d_blend_test(struct exynos_device *dev,
437 					struct exynos_bo *src,
438 					struct exynos_bo *dst,
439 					enum e_g2d_buf_type type)
440 {
441 	struct g2d_context *ctx;
442 	struct g2d_image src_img, dst_img;
443 	unsigned int count;
444 	unsigned int src_x, src_y, dst_x, dst_y, img_w, img_h;
445 	unsigned long userptr, size;
446 	int ret;
447 
448 	ctx = g2d_init(dev->fd);
449 	if (!ctx)
450 		return -EFAULT;
451 
452 	memset(&src_img, 0, sizeof(struct g2d_image));
453 	memset(&dst_img, 0, sizeof(struct g2d_image));
454 	dst_img.bo[0] = dst->handle;
455 
456 	src_x = 0;
457 	src_y = 0;
458 	dst_x = 0;
459 	dst_y = 0;
460 	img_w = screen_width;
461 	img_h = screen_height;
462 
463 	switch (type) {
464 	case G2D_IMGBUF_GEM:
465 		src_img.bo[0] = src->handle;
466 		break;
467 	case G2D_IMGBUF_USERPTR:
468 		size = img_w * img_h * 4;
469 
470 		userptr = (unsigned long)malloc(size);
471 		if (!userptr) {
472 			fprintf(stderr, "failed to allocate userptr.\n");
473 			return -EFAULT;
474 		}
475 
476 		src_img.user_ptr[0].userptr = userptr;
477 		src_img.user_ptr[0].size = size;
478 		break;
479 	default:
480 		type = G2D_IMGBUF_GEM;
481 		break;
482 	}
483 
484 	printf("blend test with %s.\n",
485 			type == G2D_IMGBUF_GEM ? "gem" : "userptr");
486 
487 	src_img.width = img_w;
488 	src_img.height = img_h;
489 	src_img.stride = src_img.width * 4;
490 	src_img.buf_type = type;
491 	src_img.select_mode = G2D_SELECT_MODE_NORMAL;
492 	src_img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
493 	src_img.color = 0xffffffff;
494 	ret = g2d_solid_fill(ctx, &src_img, src_x, src_y, img_w, img_h);
495 	if (ret < 0)
496 		goto err_free_userptr;
497 
498 	src_img.color = 0x770000ff;
499 	ret = g2d_solid_fill(ctx, &src_img, 5, 5, 200, 200);
500 	if (ret < 0)
501 		goto err_free_userptr;
502 
503 	dst_img.width = img_w;
504 	dst_img.height = img_h;
505 	dst_img.stride = dst_img.width * 4;
506 	dst_img.buf_type = G2D_IMGBUF_GEM;
507 	dst_img.select_mode = G2D_SELECT_MODE_NORMAL;
508 	dst_img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
509 	dst_img.color = 0xffffffff;
510 	ret = g2d_solid_fill(ctx, &dst_img, dst_x, dst_y, img_w, img_h);
511 	if (ret < 0)
512 		goto err_free_userptr;
513 
514 	dst_img.color = 0x77ff0000;
515 	ret = g2d_solid_fill(ctx, &dst_img, 105, 105, 200, 200);
516 	if (ret < 0)
517 		goto err_free_userptr;
518 
519 	ret = g2d_blend(ctx, &src_img, &dst_img, 5, 5, 105, 105, 200, 200,
520 			G2D_OP_OVER);
521 	if (ret < 0)
522 		goto err_free_userptr;
523 
524 	g2d_exec(ctx);
525 
526 err_free_userptr:
527 	if (type == G2D_IMGBUF_USERPTR)
528 		if (userptr)
529 			free((void *)userptr);
530 
531 	g2d_fini(ctx);
532 
533 	return 0;
534 }
535 
536 static struct fimg2d_test_case test_case = {
537 	.solid_fill = &g2d_solid_fill_test,
538 	.copy = &g2d_copy_test,
539 	.copy_with_scale = &g2d_copy_with_scale_test,
540 	.blend = &g2d_blend_test,
541 };
542 
usage(char * name)543 static void usage(char *name)
544 {
545 	fprintf(stderr, "usage: %s [-s]\n", name);
546 	fprintf(stderr, "-s <connector_id>@<crtc_id>:<mode>\n");
547 	exit(0);
548 }
549 
550 extern char *optarg;
551 static const char optstr[] = "s:";
552 
main(int argc,char ** argv)553 int main(int argc, char **argv)
554 {
555 	struct exynos_device *dev;
556 	struct exynos_bo *bo, *src;
557 	struct connector con;
558 	char *modeset = NULL;
559 	unsigned int fb_id;
560 	uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
561 	drmModeRes *resources;
562 	int ret, fd, c;
563 
564 	memset(&con, 0, sizeof(struct connector));
565 
566 	if (argc != 3) {
567 		usage(argv[0]);
568 		return -EINVAL;
569 	}
570 
571 	while ((c = getopt(argc, argv, optstr)) != -1) {
572 		switch (c) {
573 		case 's':
574 			modeset = strdup(optarg);
575 			con.crtc = -1;
576 			if (sscanf(optarg, "%d:0x%64s",
577 						&con.id,
578 						con.mode_str) != 2 &&
579 					sscanf(optarg, "%d@%d:%64s",
580 						&con.id,
581 						&con.crtc,
582 						con.mode_str) != 3)
583 				usage(argv[0]);
584 			break;
585 		default:
586 			usage(argv[0]);
587 			return -EINVAL;
588 		}
589 	}
590 
591 	fd = drmOpen(DRM_MODULE_NAME, NULL);
592 	if (fd < 0) {
593 		fprintf(stderr, "failed to open.\n");
594 		return fd;
595 	}
596 
597 	dev = exynos_device_create(fd);
598 	if (!dev) {
599 		drmClose(dev->fd);
600 		return -EFAULT;
601 	}
602 
603 	resources = drmModeGetResources(dev->fd);
604 	if (!resources) {
605 		fprintf(stderr, "drmModeGetResources failed: %s\n",
606 				strerror(errno));
607 		ret = -EFAULT;
608 		goto err_drm_close;
609 	}
610 
611 	connector_find_mode(dev->fd, &con, resources);
612 	drmModeFreeResources(resources);
613 
614 	screen_width = con.mode->hdisplay;
615 	screen_height = con.mode->vdisplay;
616 
617 	printf("screen width  = %d, screen height = %d\n", screen_width,
618 			screen_height);
619 
620 	bo = exynos_create_buffer(dev, screen_width * screen_height * 4, 0);
621 	if (!bo) {
622 		ret = -EFAULT;
623 		goto err_drm_close;
624 	}
625 
626 	handles[0] = bo->handle;
627 	pitches[0] = screen_width * 4;
628 	offsets[0] = 0;
629 
630 	ret = drmModeAddFB2(dev->fd, screen_width, screen_height,
631 				DRM_FORMAT_RGBA8888, handles,
632 				pitches, offsets, &fb_id, 0);
633 	if (ret < 0)
634 		goto err_destroy_buffer;
635 
636 	con.plane_zpos = -1;
637 
638 	memset(bo->vaddr, 0xff, screen_width * screen_height * 4);
639 
640 	ret = drm_set_crtc(dev, &con, fb_id);
641 	if (ret < 0)
642 		goto err_rm_fb;
643 
644 	ret = test_case.solid_fill(dev, bo);
645 	if (ret < 0) {
646 		fprintf(stderr, "failed to solid fill operation.\n");
647 		goto err_rm_fb;
648 	}
649 
650 	getchar();
651 
652 	src = exynos_create_buffer(dev, screen_width * screen_height * 4, 0);
653 	if (!src) {
654 		ret = -EFAULT;
655 		goto err_rm_fb;
656 	}
657 
658 	ret = test_case.copy(dev, src, bo, G2D_IMGBUF_GEM);
659 	if (ret < 0) {
660 		fprintf(stderr, "failed to test copy operation.\n");
661 		goto err_free_src;
662 	}
663 
664 	getchar();
665 
666 	ret = test_case.copy_with_scale(dev, src, bo, G2D_IMGBUF_GEM);
667 	if (ret < 0) {
668 		fprintf(stderr, "failed to test copy and scale operation.\n");
669 		goto err_free_src;
670 	}
671 
672 	getchar();
673 
674 	ret  = test_case.blend(dev, src, bo, G2D_IMGBUF_USERPTR);
675 	if (ret < 0)
676 		fprintf(stderr, "failed to test blend operation.\n");
677 
678 	getchar();
679 
680 err_free_src:
681 	if (src)
682 		exynos_destroy_buffer(src);
683 
684 err_rm_fb:
685 	drmModeRmFB(dev->fd, fb_id);
686 
687 err_destroy_buffer:
688 	exynos_destroy_buffer(bo);
689 
690 err_drm_close:
691 	drmClose(dev->fd);
692 	exynos_device_destroy(dev);
693 
694 	return 0;
695 }
696