Home | History | Annotate | Download | only in exynos
      1 /*
      2  * Copyright (C) 2013 Samsung Electronics Co.Ltd
      3  * Authors:
      4  *	Inki Dae <inki.dae (at) 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 #include <time.h>
     22 #include <unistd.h>
     23 
     24 #include <sys/mman.h>
     25 #include <linux/stddef.h>
     26 
     27 #include <xf86drm.h>
     28 #include <xf86drmMode.h>
     29 #include <libkms.h>
     30 #include <drm_fourcc.h>
     31 
     32 #include "exynos_drm.h"
     33 #include "exynos_drmif.h"
     34 #include "exynos_fimg2d.h"
     35 
     36 #define DRM_MODULE_NAME		"exynos"
     37 
     38 static unsigned int screen_width, screen_height;
     39 
     40 struct connector {
     41 	uint32_t id;
     42 	char mode_str[64];
     43 	drmModeModeInfo *mode;
     44 	drmModeEncoder *encoder;
     45 	int crtc;
     46 };
     47 
     48 static void connector_find_mode(int fd, struct connector *c,
     49 				drmModeRes *resources)
     50 {
     51 	drmModeConnector *connector;
     52 	int i, j;
     53 
     54 	/* First, find the connector & mode */
     55 	c->mode = NULL;
     56 	for (i = 0; i < resources->count_connectors; i++) {
     57 		connector = drmModeGetConnector(fd, resources->connectors[i]);
     58 
     59 		if (!connector) {
     60 			fprintf(stderr, "could not get connector %i: %s\n",
     61 				resources->connectors[i], strerror(errno));
     62 			drmModeFreeConnector(connector);
     63 			continue;
     64 		}
     65 
     66 		if (!connector->count_modes) {
     67 			drmModeFreeConnector(connector);
     68 			continue;
     69 		}
     70 
     71 		if (connector->connector_id != c->id) {
     72 			drmModeFreeConnector(connector);
     73 			continue;
     74 		}
     75 
     76 		for (j = 0; j < connector->count_modes; j++) {
     77 			c->mode = &connector->modes[j];
     78 			if (!strcmp(c->mode->name, c->mode_str))
     79 				break;
     80 		}
     81 
     82 		/* Found it, break out */
     83 		if (c->mode)
     84 			break;
     85 
     86 		drmModeFreeConnector(connector);
     87 	}
     88 
     89 	if (!c->mode) {
     90 		fprintf(stderr, "failed to find mode \"%s\"\n", c->mode_str);
     91 		return;
     92 	}
     93 
     94 	/* Now get the encoder */
     95 	for (i = 0; i < resources->count_encoders; i++) {
     96 		c->encoder = drmModeGetEncoder(fd, resources->encoders[i]);
     97 
     98 		if (!c->encoder) {
     99 			fprintf(stderr, "could not get encoder %i: %s\n",
    100 				resources->encoders[i], strerror(errno));
    101 			drmModeFreeEncoder(c->encoder);
    102 			continue;
    103 		}
    104 
    105 		if (c->encoder->encoder_id  == connector->encoder_id)
    106 			break;
    107 
    108 		drmModeFreeEncoder(c->encoder);
    109 	}
    110 
    111 	if (c->crtc == -1)
    112 		c->crtc = c->encoder->crtc_id;
    113 }
    114 
    115 static int drm_set_crtc(struct exynos_device *dev, struct connector *c,
    116 			unsigned int fb_id)
    117 {
    118 	int ret;
    119 
    120 	ret = drmModeSetCrtc(dev->fd, c->crtc,
    121 			fb_id, 0, 0, &c->id, 1, c->mode);
    122 	if (ret)
    123 		drmMsg("failed to set mode: %s\n", strerror(errno));
    124 
    125 	return ret;
    126 }
    127 
    128 static struct exynos_bo *exynos_create_buffer(struct exynos_device *dev,
    129 						unsigned long size,
    130 						unsigned int flags)
    131 {
    132 	struct exynos_bo *bo;
    133 
    134 	bo = exynos_bo_create(dev, size, flags);
    135 	if (!bo)
    136 		return bo;
    137 
    138 	if (!exynos_bo_map(bo)) {
    139 		exynos_bo_destroy(bo);
    140 		return NULL;
    141 	}
    142 
    143 	return bo;
    144 }
    145 
    146 /* Allocate buffer and fill it with checkerboard pattern, where the tiles *
    147  * have a random color. The caller has to free the buffer.                */
    148 static void *create_checkerboard_pattern(unsigned int num_tiles_x,
    149 						unsigned int num_tiles_y, unsigned int tile_size)
    150 {
    151 	unsigned int *buf;
    152 	unsigned int x, y, i, j;
    153 	const unsigned int stride = num_tiles_x * tile_size;
    154 
    155 	if (posix_memalign((void*)&buf, 64, num_tiles_y * tile_size * stride * 4) != 0)
    156 		return NULL;
    157 
    158 	for (x = 0; x < num_tiles_x; ++x) {
    159 		for (y = 0; y < num_tiles_y; ++y) {
    160 			const unsigned int color = 0xff000000 + (random() & 0xffffff);
    161 
    162 			for (i = 0; i < tile_size; ++i) {
    163 				for (j = 0; j < tile_size; ++j) {
    164 					buf[x * tile_size + y * stride * tile_size + i + j * stride] = color;
    165 				}
    166 			}
    167 		}
    168 	}
    169 
    170 	return buf;
    171 }
    172 
    173 static void exynos_destroy_buffer(struct exynos_bo *bo)
    174 {
    175 	exynos_bo_destroy(bo);
    176 }
    177 
    178 static void wait_for_user_input(int last)
    179 {
    180 	printf("press <ENTER> to %s\n", last ? "exit test application" :
    181 			"skip to next test");
    182 
    183 	getchar();
    184 }
    185 
    186 static int g2d_solid_fill_test(struct exynos_device *dev, struct exynos_bo *dst)
    187 {
    188 	struct g2d_context *ctx;
    189 	struct g2d_image img = {0};
    190 	unsigned int count, img_w, img_h;
    191 	int ret = 0;
    192 
    193 	ctx = g2d_init(dev->fd);
    194 	if (!ctx)
    195 		return -EFAULT;
    196 
    197 	img.bo[0] = dst->handle;
    198 
    199 	printf("solid fill test.\n");
    200 
    201 	srand(time(NULL));
    202 	img_w = screen_width;
    203 	img_h = screen_height;
    204 
    205 	for (count = 0; count < 2; count++) {
    206 		unsigned int x, y, w, h;
    207 
    208 		x = rand() % (img_w / 2);
    209 		y = rand() % (img_h / 2);
    210 		w = rand() % (img_w - x);
    211 		h = rand() % (img_h - y);
    212 
    213 		img.width = img_w;
    214 		img.height = img_h;
    215 		img.stride = img.width * 4;
    216 		img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
    217 		img.color = 0xff000000 + (random() & 0xffffff);
    218 
    219 		ret = g2d_solid_fill(ctx, &img, x, y, w, h);
    220 		if (ret < 0)
    221 			goto err_fini;
    222 
    223 		ret = g2d_exec(ctx);
    224 		if (ret < 0)
    225 			break;
    226 	}
    227 
    228 err_fini:
    229 	g2d_fini(ctx);
    230 
    231 	return ret;
    232 }
    233 
    234 static int g2d_copy_test(struct exynos_device *dev, struct exynos_bo *src,
    235 				struct exynos_bo *dst,
    236 				enum e_g2d_buf_type type)
    237 {
    238 	struct g2d_context *ctx;
    239 	struct g2d_image src_img = {0}, dst_img = {0};
    240 	unsigned int src_x, src_y, dst_x, dst_y, img_w, img_h;
    241 	unsigned long userptr, size;
    242 	int ret;
    243 
    244 	ctx = g2d_init(dev->fd);
    245 	if (!ctx)
    246 		return -EFAULT;
    247 
    248 	dst_img.bo[0] = dst->handle;
    249 
    250 	src_x = 0;
    251 	src_y = 0;
    252 	dst_x = 0;
    253 	dst_y = 0;
    254 	img_w = screen_width;
    255 	img_h = screen_height;
    256 
    257 	switch (type) {
    258 	case G2D_IMGBUF_GEM:
    259 		src_img.bo[0] = src->handle;
    260 		break;
    261 	case G2D_IMGBUF_USERPTR:
    262 		size = img_w * img_h * 4;
    263 
    264 		userptr = (unsigned long)malloc(size);
    265 		if (!userptr) {
    266 			fprintf(stderr, "failed to allocate userptr.\n");
    267 			return -EFAULT;
    268 		}
    269 
    270 		src_img.user_ptr[0].userptr = userptr;
    271 		src_img.user_ptr[0].size = size;
    272 		break;
    273 	case G2D_IMGBUF_COLOR:
    274 	default:
    275 		ret = -EFAULT;
    276 		goto fail;
    277 	}
    278 
    279 	printf("copy test with %s.\n",
    280 			type == G2D_IMGBUF_GEM ? "gem" : "userptr");
    281 
    282 	src_img.width = img_w;
    283 	src_img.height = img_h;
    284 	src_img.stride = src_img.width * 4;
    285 	src_img.buf_type = type;
    286 	src_img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
    287 	src_img.color = 0xffff0000;
    288 	ret = g2d_solid_fill(ctx, &src_img, src_x, src_y, img_w, img_h);
    289 	if (ret < 0)
    290 		goto err_free_userptr;
    291 
    292 	dst_img.width = img_w;
    293 	dst_img.height = img_h;
    294 	dst_img.stride = dst_img.width * 4;
    295 	dst_img.buf_type = G2D_IMGBUF_GEM;
    296 	dst_img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
    297 
    298 	ret = g2d_copy(ctx, &src_img, &dst_img, src_x, src_y, dst_x, dst_y,
    299 			img_w - 4, img_h - 4);
    300 	if (ret < 0)
    301 		goto err_free_userptr;
    302 
    303 	g2d_exec(ctx);
    304 
    305 err_free_userptr:
    306 	if (type == G2D_IMGBUF_USERPTR)
    307 		if (userptr)
    308 			free((void *)userptr);
    309 
    310 fail:
    311 	g2d_fini(ctx);
    312 
    313 	return ret;
    314 }
    315 
    316 static int g2d_move_test(struct exynos_device *dev,
    317 				struct exynos_bo *tmp,
    318 				struct exynos_bo *buf,
    319 				enum e_g2d_buf_type type)
    320 {
    321 	struct g2d_context *ctx;
    322 	struct g2d_image img = {0}, tmp_img = {0};
    323 	unsigned int img_w, img_h, count;
    324 	int cur_x, cur_y;
    325 	void *checkerboard;
    326 	int ret;
    327 
    328 	static const struct g2d_step {
    329 		int x, y;
    330 	} steps[] = {
    331 		{ 1,  0}, { 0,  1},
    332 		{-1,  0}, { 0, -1},
    333 		{ 1,  1}, {-1, -1},
    334 		{ 1, -1}, {-1,  1},
    335 		{ 2,  1}, { 1,  2},
    336 		{-2, -1}, {-1, -2},
    337 		{ 2, -1}, { 1, -2},
    338 		{-2,  1}, {-1,  2}
    339 	};
    340 	static const unsigned int num_steps =
    341 		sizeof(steps) / sizeof(struct g2d_step);
    342 
    343 	ctx = g2d_init(dev->fd);
    344 	if (!ctx)
    345 		return -EFAULT;
    346 
    347 	img.bo[0] = buf->handle;
    348 
    349 	/* create pattern of half the screen size */
    350 	checkerboard = create_checkerboard_pattern(screen_width / 64, screen_height / 64, 32);
    351 	if (!checkerboard) {
    352 		ret = -EFAULT;
    353 		goto fail;
    354 	}
    355 
    356 	img_w = (screen_width / 64) * 32;
    357 	img_h = (screen_height / 64) * 32;
    358 
    359 	switch (type) {
    360 	case G2D_IMGBUF_GEM:
    361 		memcpy(tmp->vaddr, checkerboard, img_w * img_h * 4);
    362 		tmp_img.bo[0] = tmp->handle;
    363 		break;
    364 	case G2D_IMGBUF_USERPTR:
    365 		tmp_img.user_ptr[0].userptr = (unsigned long)checkerboard;
    366 		tmp_img.user_ptr[0].size = img_w * img_h * 4;
    367 		break;
    368 	case G2D_IMGBUF_COLOR:
    369 	default:
    370 		ret = -EFAULT;
    371 		goto fail;
    372 	}
    373 
    374 	/* solid fill framebuffer with white color */
    375 	img.width = screen_width;
    376 	img.height = screen_height;
    377 	img.stride = screen_width * 4;
    378 	img.buf_type = G2D_IMGBUF_GEM;
    379 	img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
    380 	img.color = 0xffffffff;
    381 
    382 	/* put checkerboard pattern in the center of the framebuffer */
    383 	cur_x = (screen_width - img_w) / 2;
    384 	cur_y = (screen_height - img_h) / 2;
    385 	tmp_img.width = img_w;
    386 	tmp_img.height = img_h;
    387 	tmp_img.stride = img_w * 4;
    388 	tmp_img.buf_type = type;
    389 	tmp_img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
    390 
    391 	ret = g2d_solid_fill(ctx, &img, 0, 0, screen_width, screen_height) ||
    392 		g2d_copy(ctx, &tmp_img, &img, 0, 0, cur_x, cur_y, img_w, img_h);
    393 
    394 	if (!ret)
    395 		ret = g2d_exec(ctx);
    396 	if (ret < 0)
    397 			goto fail;
    398 
    399 	printf("move test with %s.\n",
    400 			type == G2D_IMGBUF_GEM ? "gem" : "userptr");
    401 
    402 	srand(time(NULL));
    403 	for (count = 0; count < 256; ++count) {
    404 		const struct g2d_step *s;
    405 
    406 		/* select step and validate it */
    407 		while (1) {
    408 			s = &steps[random() % num_steps];
    409 
    410 			if (cur_x + s->x < 0 || cur_y + s->y < 0 ||
    411 				cur_x + img_w + s->x >= screen_width ||
    412 				cur_y + img_h + s->y >= screen_height)
    413 				continue;
    414 			else
    415 				break;
    416 		}
    417 
    418 		ret = g2d_move(ctx, &img, cur_x, cur_y, cur_x + s->x, cur_y + s->y,
    419 			img_w, img_h);
    420 		if (!ret)
    421 			ret = g2d_exec(ctx);
    422 
    423 		if (ret < 0)
    424 			goto fail;
    425 
    426 		cur_x += s->x;
    427 		cur_y += s->y;
    428 
    429 		usleep(100000);
    430 	}
    431 
    432 fail:
    433 	g2d_fini(ctx);
    434 
    435 	free(checkerboard);
    436 
    437 	return ret;
    438 }
    439 
    440 static int g2d_copy_with_scale_test(struct exynos_device *dev,
    441 					struct exynos_bo *src,
    442 					struct exynos_bo *dst,
    443 					enum e_g2d_buf_type type)
    444 {
    445 	struct g2d_context *ctx;
    446 	struct g2d_image src_img = {0}, dst_img = {0};
    447 	unsigned int src_x, src_y, img_w, img_h;
    448 	unsigned long userptr, size;
    449 	int ret;
    450 
    451 	ctx = g2d_init(dev->fd);
    452 	if (!ctx)
    453 		return -EFAULT;
    454 
    455 	dst_img.bo[0] = dst->handle;
    456 
    457 	src_x = 0;
    458 	src_y = 0;
    459 	img_w = screen_width;
    460 	img_h = screen_height;
    461 
    462 	switch (type) {
    463 	case G2D_IMGBUF_GEM:
    464 		src_img.bo[0] = src->handle;
    465 		break;
    466 	case G2D_IMGBUF_USERPTR:
    467 		size = img_w * img_h * 4;
    468 
    469 		userptr = (unsigned long)malloc(size);
    470 		if (!userptr) {
    471 			fprintf(stderr, "failed to allocate userptr.\n");
    472 			return -EFAULT;
    473 		}
    474 
    475 		src_img.user_ptr[0].userptr = userptr;
    476 		src_img.user_ptr[0].size = size;
    477 		break;
    478 	case G2D_IMGBUF_COLOR:
    479 	default:
    480 		ret = -EFAULT;
    481 		goto fail;
    482 	}
    483 
    484 	printf("copy and scale 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.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
    492 	src_img.color = 0xffffffff;
    493 	ret = g2d_solid_fill(ctx, &src_img, src_x, src_y, img_w ,  img_h);
    494 	if (ret < 0)
    495 		goto err_free_userptr;
    496 
    497 	src_img.color = 0xff00ff00;
    498 	ret = g2d_solid_fill(ctx, &src_img, 5, 5, 100, 100);
    499 	if (ret < 0)
    500 		goto err_free_userptr;
    501 
    502 	dst_img.width = img_w;
    503 	dst_img.height = img_h;
    504 	dst_img.buf_type = G2D_IMGBUF_GEM;
    505 	dst_img.stride = dst_img.width * 4;
    506 	dst_img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
    507 
    508 	ret = g2d_copy_with_scale(ctx, &src_img, &dst_img, 5, 5, 100, 100,
    509 					100, 100, 200, 200, 0);
    510 	if (ret < 0)
    511 		goto err_free_userptr;
    512 
    513 	g2d_exec(ctx);
    514 
    515 err_free_userptr:
    516 	if (type == G2D_IMGBUF_USERPTR)
    517 		if (userptr)
    518 			free((void *)userptr);
    519 
    520 fail:
    521 	g2d_fini(ctx);
    522 
    523 	return 0;
    524 }
    525 
    526 static int g2d_blend_test(struct exynos_device *dev,
    527 					struct exynos_bo *src,
    528 					struct exynos_bo *dst,
    529 					enum e_g2d_buf_type type)
    530 {
    531 	struct g2d_context *ctx;
    532 	struct g2d_image src_img = {0}, dst_img = {0};
    533 	unsigned int src_x, src_y, dst_x, dst_y, img_w, img_h;
    534 	unsigned long userptr, size;
    535 	int ret;
    536 
    537 	ctx = g2d_init(dev->fd);
    538 	if (!ctx)
    539 		return -EFAULT;
    540 
    541 	dst_img.bo[0] = dst->handle;
    542 
    543 	src_x = 0;
    544 	src_y = 0;
    545 	dst_x = 0;
    546 	dst_y = 0;
    547 	img_w = screen_width;
    548 	img_h = screen_height;
    549 
    550 	switch (type) {
    551 	case G2D_IMGBUF_GEM:
    552 		src_img.bo[0] = src->handle;
    553 		break;
    554 	case G2D_IMGBUF_USERPTR:
    555 		size = img_w * img_h * 4;
    556 
    557 		userptr = (unsigned long)malloc(size);
    558 		if (!userptr) {
    559 			fprintf(stderr, "failed to allocate userptr.\n");
    560 			return -EFAULT;
    561 		}
    562 
    563 		src_img.user_ptr[0].userptr = userptr;
    564 		src_img.user_ptr[0].size = size;
    565 		break;
    566 	case G2D_IMGBUF_COLOR:
    567 	default:
    568 		ret = -EFAULT;
    569 		goto fail;
    570 	}
    571 
    572 	printf("blend test with %s.\n",
    573 			type == G2D_IMGBUF_GEM ? "gem" : "userptr");
    574 
    575 	src_img.width = img_w;
    576 	src_img.height = img_h;
    577 	src_img.stride = src_img.width * 4;
    578 	src_img.buf_type = type;
    579 	src_img.select_mode = G2D_SELECT_MODE_NORMAL;
    580 	src_img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
    581 	src_img.color = 0xffffffff;
    582 	ret = g2d_solid_fill(ctx, &src_img, src_x, src_y, img_w, img_h);
    583 	if (ret < 0)
    584 		goto err_free_userptr;
    585 
    586 	src_img.color = 0x770000ff;
    587 	ret = g2d_solid_fill(ctx, &src_img, 5, 5, 200, 200);
    588 	if (ret < 0)
    589 		goto err_free_userptr;
    590 
    591 	dst_img.width = img_w;
    592 	dst_img.height = img_h;
    593 	dst_img.stride = dst_img.width * 4;
    594 	dst_img.buf_type = G2D_IMGBUF_GEM;
    595 	dst_img.select_mode = G2D_SELECT_MODE_NORMAL;
    596 	dst_img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
    597 	dst_img.color = 0xffffffff;
    598 	ret = g2d_solid_fill(ctx, &dst_img, dst_x, dst_y, img_w, img_h);
    599 	if (ret < 0)
    600 		goto err_free_userptr;
    601 
    602 	dst_img.color = 0x77ff0000;
    603 	ret = g2d_solid_fill(ctx, &dst_img, 105, 105, 200, 200);
    604 	if (ret < 0)
    605 		goto err_free_userptr;
    606 
    607 	ret = g2d_blend(ctx, &src_img, &dst_img, 5, 5, 105, 105, 200, 200,
    608 			G2D_OP_OVER);
    609 	if (ret < 0)
    610 		goto err_free_userptr;
    611 
    612 	g2d_exec(ctx);
    613 
    614 err_free_userptr:
    615 	if (type == G2D_IMGBUF_USERPTR)
    616 		if (userptr)
    617 			free((void *)userptr);
    618 
    619 fail:
    620 	g2d_fini(ctx);
    621 
    622 	return 0;
    623 }
    624 
    625 static int g2d_checkerboard_test(struct exynos_device *dev,
    626 					struct exynos_bo *src,
    627 					struct exynos_bo *dst,
    628 					enum e_g2d_buf_type type)
    629 {
    630 	struct g2d_context *ctx;
    631 	struct g2d_image src_img = {0}, dst_img = {0};
    632 	unsigned int src_x, src_y, dst_x, dst_y, img_w, img_h;
    633 	void *checkerboard = NULL;
    634 	int ret;
    635 
    636 	ctx = g2d_init(dev->fd);
    637 	if (!ctx)
    638 		return -EFAULT;
    639 
    640 	dst_img.bo[0] = dst->handle;
    641 
    642 	src_x = 0;
    643 	src_y = 0;
    644 	dst_x = 0;
    645 	dst_y = 0;
    646 
    647 	checkerboard = create_checkerboard_pattern(screen_width / 32, screen_height / 32, 32);
    648 	if (checkerboard == NULL) {
    649 		ret = -1;
    650 		goto fail;
    651 	}
    652 
    653 	img_w = screen_width - (screen_width % 32);
    654 	img_h = screen_height - (screen_height % 32);
    655 
    656 	switch (type) {
    657 	case G2D_IMGBUF_GEM:
    658 		memcpy(src->vaddr, checkerboard, img_w * img_h * 4);
    659 		src_img.bo[0] = src->handle;
    660 		break;
    661 	case G2D_IMGBUF_USERPTR:
    662 		src_img.user_ptr[0].userptr = (unsigned long)checkerboard;
    663 		src_img.user_ptr[0].size = img_w * img_h * 4;
    664 		break;
    665 	case G2D_IMGBUF_COLOR:
    666 	default:
    667 		ret = -EFAULT;
    668 		goto fail;
    669 	}
    670 
    671 	printf("checkerboard test with %s.\n",
    672 			type == G2D_IMGBUF_GEM ? "gem" : "userptr");
    673 
    674 	src_img.width = img_w;
    675 	src_img.height = img_h;
    676 	src_img.stride = src_img.width * 4;
    677 	src_img.buf_type = type;
    678 	src_img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
    679 
    680 	dst_img.width = screen_width;
    681 	dst_img.height = screen_height;
    682 	dst_img.stride = dst_img.width * 4;
    683 	dst_img.buf_type = G2D_IMGBUF_GEM;
    684 	dst_img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
    685 	src_img.color = 0xff000000;
    686 	ret = g2d_solid_fill(ctx, &dst_img, src_x, src_y, screen_width, screen_height);
    687 	if (ret < 0)
    688 		goto fail;
    689 
    690 	ret = g2d_copy(ctx, &src_img, &dst_img, src_x, src_y, dst_x, dst_y,
    691 			img_w, img_h);
    692 	if (ret < 0)
    693 		goto fail;
    694 
    695 	g2d_exec(ctx);
    696 
    697 fail:
    698 	free(checkerboard);
    699 	g2d_fini(ctx);
    700 
    701 	return ret;
    702 }
    703 
    704 static void usage(char *name)
    705 {
    706 	fprintf(stderr, "usage: %s [-s]\n", name);
    707 	fprintf(stderr, "-s <connector_id>@<crtc_id>:<mode>\n");
    708 	exit(0);
    709 }
    710 
    711 extern char *optarg;
    712 static const char optstr[] = "s:";
    713 
    714 int main(int argc, char **argv)
    715 {
    716 	struct exynos_device *dev;
    717 	struct exynos_bo *bo, *src;
    718 	struct connector con;
    719 	unsigned int fb_id;
    720 	uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
    721 	drmModeRes *resources;
    722 	int ret, fd, c;
    723 
    724 	memset(&con, 0, sizeof(struct connector));
    725 
    726 	if (argc != 3) {
    727 		usage(argv[0]);
    728 		return -EINVAL;
    729 	}
    730 
    731 	while ((c = getopt(argc, argv, optstr)) != -1) {
    732 		switch (c) {
    733 		case 's':
    734 			con.crtc = -1;
    735 			if (sscanf(optarg, "%d:0x%64s",
    736 						&con.id,
    737 						con.mode_str) != 2 &&
    738 					sscanf(optarg, "%d@%d:%64s",
    739 						&con.id,
    740 						&con.crtc,
    741 						con.mode_str) != 3)
    742 				usage(argv[0]);
    743 			break;
    744 		default:
    745 			usage(argv[0]);
    746 			break;
    747 		}
    748 	}
    749 
    750 	fd = drmOpen(DRM_MODULE_NAME, NULL);
    751 	if (fd < 0) {
    752 		fprintf(stderr, "failed to open.\n");
    753 		return fd;
    754 	}
    755 
    756 	dev = exynos_device_create(fd);
    757 	if (!dev) {
    758 		drmClose(dev->fd);
    759 		return -EFAULT;
    760 	}
    761 
    762 	resources = drmModeGetResources(dev->fd);
    763 	if (!resources) {
    764 		fprintf(stderr, "drmModeGetResources failed: %s\n",
    765 				strerror(errno));
    766 		ret = -EFAULT;
    767 		goto err_drm_close;
    768 	}
    769 
    770 	connector_find_mode(dev->fd, &con, resources);
    771 	drmModeFreeResources(resources);
    772 
    773 	if (!con.mode) {
    774 		fprintf(stderr, "failed to find usable connector\n");
    775 		ret = -EFAULT;
    776 		goto err_drm_close;
    777 	}
    778 
    779 	screen_width = con.mode->hdisplay;
    780 	screen_height = con.mode->vdisplay;
    781 
    782 	if (screen_width == 0 || screen_height == 0) {
    783 		fprintf(stderr, "failed to find sane resolution on connector\n");
    784 		ret = -EFAULT;
    785 		goto err_drm_close;
    786 	}
    787 
    788 	printf("screen width = %d, screen height = %d\n", screen_width,
    789 			screen_height);
    790 
    791 	bo = exynos_create_buffer(dev, screen_width * screen_height * 4, 0);
    792 	if (!bo) {
    793 		ret = -EFAULT;
    794 		goto err_drm_close;
    795 	}
    796 
    797 	handles[0] = bo->handle;
    798 	pitches[0] = screen_width * 4;
    799 	offsets[0] = 0;
    800 
    801 	ret = drmModeAddFB2(dev->fd, screen_width, screen_height,
    802 				DRM_FORMAT_XRGB8888, handles,
    803 				pitches, offsets, &fb_id, 0);
    804 	if (ret < 0)
    805 		goto err_destroy_buffer;
    806 
    807 	memset(bo->vaddr, 0xff, screen_width * screen_height * 4);
    808 
    809 	ret = drm_set_crtc(dev, &con, fb_id);
    810 	if (ret < 0)
    811 		goto err_rm_fb;
    812 
    813 	ret = g2d_solid_fill_test(dev, bo);
    814 	if (ret < 0) {
    815 		fprintf(stderr, "failed to solid fill operation.\n");
    816 		goto err_rm_fb;
    817 	}
    818 
    819 	wait_for_user_input(0);
    820 
    821 	src = exynos_create_buffer(dev, screen_width * screen_height * 4, 0);
    822 	if (!src) {
    823 		ret = -EFAULT;
    824 		goto err_rm_fb;
    825 	}
    826 
    827 	ret = g2d_copy_test(dev, src, bo, G2D_IMGBUF_GEM);
    828 	if (ret < 0) {
    829 		fprintf(stderr, "failed to test copy operation.\n");
    830 		goto err_free_src;
    831 	}
    832 
    833 	wait_for_user_input(0);
    834 
    835 	ret = g2d_move_test(dev, src, bo, G2D_IMGBUF_GEM);
    836 	if (ret < 0) {
    837 		fprintf(stderr, "failed to test move operation.\n");
    838 		goto err_free_src;
    839 	}
    840 
    841 	wait_for_user_input(0);
    842 
    843 	ret = g2d_copy_with_scale_test(dev, src, bo, G2D_IMGBUF_GEM);
    844 	if (ret < 0) {
    845 		fprintf(stderr, "failed to test copy and scale operation.\n");
    846 		goto err_free_src;
    847 	}
    848 
    849 	wait_for_user_input(0);
    850 
    851 	ret = g2d_checkerboard_test(dev, src, bo, G2D_IMGBUF_GEM);
    852 	if (ret < 0) {
    853 		fprintf(stderr, "failed to issue checkerboard test.\n");
    854 		goto err_free_src;
    855 	}
    856 
    857 	wait_for_user_input(1);
    858 
    859 	/*
    860 	 * The blend test uses the userptr functionality of exynos-drm, which
    861 	 * is currently not safe to use. If the kernel hasn't been build with
    862 	 * exynos-iommu support, then the blend test is going to produce (kernel)
    863 	 * memory corruption, eventually leading to a system crash.
    864 	 *
    865 	 * Disable the test for now, until the kernel code has been sanitized.
    866 	 */
    867 #if 0
    868 	ret  = g2d_blend_test(dev, src, bo, G2D_IMGBUF_USERPTR);
    869 	if (ret < 0)
    870 		fprintf(stderr, "failed to test blend operation.\n");
    871 
    872 	getchar();
    873 #endif
    874 
    875 err_free_src:
    876 	if (src)
    877 		exynos_destroy_buffer(src);
    878 
    879 err_rm_fb:
    880 	drmModeRmFB(dev->fd, fb_id);
    881 
    882 err_destroy_buffer:
    883 	exynos_destroy_buffer(bo);
    884 
    885 err_drm_close:
    886 	drmClose(dev->fd);
    887 	exynos_device_destroy(dev);
    888 
    889 	return 0;
    890 }
    891