1e88f27b3Smrg/*
2e88f27b3Smrg * Copyright (C) 2013 Samsung Electronics Co.Ltd
3e88f27b3Smrg * Authors:
4e88f27b3Smrg *	Inki Dae <inki.dae@samsung.com>
5e88f27b3Smrg *
600a23bdaSmrg * Permission is hereby granted, free of charge, to any person obtaining a
700a23bdaSmrg * copy of this software and associated documentation files (the "Software"),
800a23bdaSmrg * to deal in the Software without restriction, including without limitation
900a23bdaSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1000a23bdaSmrg * and/or sell copies of the Software, and to permit persons to whom the
1100a23bdaSmrg * Software is furnished to do so, subject to the following conditions:
12e88f27b3Smrg *
1300a23bdaSmrg * The above copyright notice and this permission notice (including the next
1400a23bdaSmrg * paragraph) shall be included in all copies or substantial portions of the
1500a23bdaSmrg * Software.
1600a23bdaSmrg *
1700a23bdaSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1800a23bdaSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1900a23bdaSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
2000a23bdaSmrg * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
2100a23bdaSmrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
2200a23bdaSmrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
2300a23bdaSmrg * OTHER DEALINGS IN THE SOFTWARE.
24e88f27b3Smrg */
25e88f27b3Smrg
26e88f27b3Smrg#include <stdlib.h>
27e88f27b3Smrg#include <stdio.h>
28e88f27b3Smrg#include <string.h>
29e88f27b3Smrg#include <errno.h>
30e6188e58Smrg#include <time.h>
31e6188e58Smrg#include <unistd.h>
32e88f27b3Smrg
33e88f27b3Smrg#include <sys/mman.h>
34e88f27b3Smrg
35e88f27b3Smrg#include <xf86drm.h>
36e88f27b3Smrg#include <xf86drmMode.h>
37e88f27b3Smrg#include <drm_fourcc.h>
38e88f27b3Smrg
39e88f27b3Smrg#include "exynos_drm.h"
40e88f27b3Smrg#include "exynos_drmif.h"
41e6188e58Smrg#include "exynos_fimg2d.h"
42e88f27b3Smrg
43e88f27b3Smrg#define DRM_MODULE_NAME		"exynos"
44e88f27b3Smrg
45e88f27b3Smrgstatic unsigned int screen_width, screen_height;
46e88f27b3Smrg
47e88f27b3Smrgstruct connector {
48e88f27b3Smrg	uint32_t id;
49e88f27b3Smrg	char mode_str[64];
50e88f27b3Smrg	drmModeModeInfo *mode;
51e88f27b3Smrg	drmModeEncoder *encoder;
52e88f27b3Smrg	int crtc;
53e88f27b3Smrg};
54e88f27b3Smrg
55e88f27b3Smrgstatic void connector_find_mode(int fd, struct connector *c,
56e88f27b3Smrg				drmModeRes *resources)
57e88f27b3Smrg{
58e88f27b3Smrg	drmModeConnector *connector;
59e88f27b3Smrg	int i, j;
60e88f27b3Smrg
61e88f27b3Smrg	/* First, find the connector & mode */
62e88f27b3Smrg	c->mode = NULL;
63e88f27b3Smrg	for (i = 0; i < resources->count_connectors; i++) {
64e88f27b3Smrg		connector = drmModeGetConnector(fd, resources->connectors[i]);
65e88f27b3Smrg
66e88f27b3Smrg		if (!connector) {
67e88f27b3Smrg			fprintf(stderr, "could not get connector %i: %s\n",
68e88f27b3Smrg				resources->connectors[i], strerror(errno));
69e88f27b3Smrg			continue;
70e88f27b3Smrg		}
71e88f27b3Smrg
72e88f27b3Smrg		if (!connector->count_modes) {
73e88f27b3Smrg			drmModeFreeConnector(connector);
74e88f27b3Smrg			continue;
75e88f27b3Smrg		}
76e88f27b3Smrg
77e88f27b3Smrg		if (connector->connector_id != c->id) {
78e88f27b3Smrg			drmModeFreeConnector(connector);
79e88f27b3Smrg			continue;
80e88f27b3Smrg		}
81e88f27b3Smrg
82e88f27b3Smrg		for (j = 0; j < connector->count_modes; j++) {
83e88f27b3Smrg			c->mode = &connector->modes[j];
84e88f27b3Smrg			if (!strcmp(c->mode->name, c->mode_str))
85e88f27b3Smrg				break;
86e88f27b3Smrg		}
87e88f27b3Smrg
88e88f27b3Smrg		/* Found it, break out */
89e88f27b3Smrg		if (c->mode)
90e88f27b3Smrg			break;
91e88f27b3Smrg
92e88f27b3Smrg		drmModeFreeConnector(connector);
93e88f27b3Smrg	}
94e88f27b3Smrg
95e88f27b3Smrg	if (!c->mode) {
96e88f27b3Smrg		fprintf(stderr, "failed to find mode \"%s\"\n", c->mode_str);
97e88f27b3Smrg		return;
98e88f27b3Smrg	}
99e88f27b3Smrg
100e88f27b3Smrg	/* Now get the encoder */
101e88f27b3Smrg	for (i = 0; i < resources->count_encoders; i++) {
102e88f27b3Smrg		c->encoder = drmModeGetEncoder(fd, resources->encoders[i]);
103e88f27b3Smrg
104e88f27b3Smrg		if (!c->encoder) {
105e88f27b3Smrg			fprintf(stderr, "could not get encoder %i: %s\n",
106e88f27b3Smrg				resources->encoders[i], strerror(errno));
107e88f27b3Smrg			continue;
108e88f27b3Smrg		}
109e88f27b3Smrg
110e88f27b3Smrg		if (c->encoder->encoder_id  == connector->encoder_id)
111e88f27b3Smrg			break;
112e88f27b3Smrg
113e88f27b3Smrg		drmModeFreeEncoder(c->encoder);
114e88f27b3Smrg	}
115e88f27b3Smrg
116e88f27b3Smrg	if (c->crtc == -1)
117e88f27b3Smrg		c->crtc = c->encoder->crtc_id;
118e88f27b3Smrg}
119e88f27b3Smrg
120e88f27b3Smrgstatic int drm_set_crtc(struct exynos_device *dev, struct connector *c,
121e88f27b3Smrg			unsigned int fb_id)
122e88f27b3Smrg{
123e88f27b3Smrg	int ret;
124e88f27b3Smrg
125e88f27b3Smrg	ret = drmModeSetCrtc(dev->fd, c->crtc,
126e88f27b3Smrg			fb_id, 0, 0, &c->id, 1, c->mode);
127e6188e58Smrg	if (ret)
128e88f27b3Smrg		drmMsg("failed to set mode: %s\n", strerror(errno));
129e88f27b3Smrg
130e88f27b3Smrg	return ret;
131e88f27b3Smrg}
132e88f27b3Smrg
133e88f27b3Smrgstatic struct exynos_bo *exynos_create_buffer(struct exynos_device *dev,
134e88f27b3Smrg						unsigned long size,
135e88f27b3Smrg						unsigned int flags)
136e88f27b3Smrg{
137e88f27b3Smrg	struct exynos_bo *bo;
138e88f27b3Smrg
139e88f27b3Smrg	bo = exynos_bo_create(dev, size, flags);
140e88f27b3Smrg	if (!bo)
141e88f27b3Smrg		return bo;
142e88f27b3Smrg
143e88f27b3Smrg	if (!exynos_bo_map(bo)) {
144e88f27b3Smrg		exynos_bo_destroy(bo);
145e88f27b3Smrg		return NULL;
146e88f27b3Smrg	}
147e88f27b3Smrg
148e88f27b3Smrg	return bo;
149e88f27b3Smrg}
150e88f27b3Smrg
151e6188e58Smrg/* Allocate buffer and fill it with checkerboard pattern, where the tiles *
152e6188e58Smrg * have a random color. The caller has to free the buffer.                */
153e6188e58Smrgstatic void *create_checkerboard_pattern(unsigned int num_tiles_x,
154e6188e58Smrg						unsigned int num_tiles_y, unsigned int tile_size)
155e6188e58Smrg{
156e6188e58Smrg	unsigned int *buf;
157e6188e58Smrg	unsigned int x, y, i, j;
158e6188e58Smrg	const unsigned int stride = num_tiles_x * tile_size;
159e6188e58Smrg
160e6188e58Smrg	if (posix_memalign((void*)&buf, 64, num_tiles_y * tile_size * stride * 4) != 0)
161e6188e58Smrg		return NULL;
162e6188e58Smrg
163e6188e58Smrg	for (x = 0; x < num_tiles_x; ++x) {
164e6188e58Smrg		for (y = 0; y < num_tiles_y; ++y) {
165e6188e58Smrg			const unsigned int color = 0xff000000 + (random() & 0xffffff);
166e6188e58Smrg
167e6188e58Smrg			for (i = 0; i < tile_size; ++i) {
168e6188e58Smrg				for (j = 0; j < tile_size; ++j) {
169e6188e58Smrg					buf[x * tile_size + y * stride * tile_size + i + j * stride] = color;
170e6188e58Smrg				}
171e6188e58Smrg			}
172e6188e58Smrg		}
173e6188e58Smrg	}
174e6188e58Smrg
175e6188e58Smrg	return buf;
176e6188e58Smrg}
177e6188e58Smrg
178e88f27b3Smrgstatic void exynos_destroy_buffer(struct exynos_bo *bo)
179e88f27b3Smrg{
180e88f27b3Smrg	exynos_bo_destroy(bo);
181e88f27b3Smrg}
182e88f27b3Smrg
183e6188e58Smrgstatic void wait_for_user_input(int last)
184e6188e58Smrg{
185e6188e58Smrg	printf("press <ENTER> to %s\n", last ? "exit test application" :
186e6188e58Smrg			"skip to next test");
187e6188e58Smrg
188e6188e58Smrg	getchar();
189e6188e58Smrg}
190e6188e58Smrg
191e88f27b3Smrgstatic int g2d_solid_fill_test(struct exynos_device *dev, struct exynos_bo *dst)
192e88f27b3Smrg{
193e88f27b3Smrg	struct g2d_context *ctx;
194e6188e58Smrg	struct g2d_image img = {0};
195e88f27b3Smrg	unsigned int count, img_w, img_h;
196e88f27b3Smrg	int ret = 0;
197e88f27b3Smrg
198e88f27b3Smrg	ctx = g2d_init(dev->fd);
199e88f27b3Smrg	if (!ctx)
200e88f27b3Smrg		return -EFAULT;
201e88f27b3Smrg
202e88f27b3Smrg	img.bo[0] = dst->handle;
203e88f27b3Smrg
204e6188e58Smrg	printf("solid fill test.\n");
205e88f27b3Smrg
206e88f27b3Smrg	srand(time(NULL));
207e88f27b3Smrg	img_w = screen_width;
208e88f27b3Smrg	img_h = screen_height;
209e88f27b3Smrg
210e88f27b3Smrg	for (count = 0; count < 2; count++) {
211e88f27b3Smrg		unsigned int x, y, w, h;
212e88f27b3Smrg
213e88f27b3Smrg		x = rand() % (img_w / 2);
214e88f27b3Smrg		y = rand() % (img_h / 2);
215e88f27b3Smrg		w = rand() % (img_w - x);
216e88f27b3Smrg		h = rand() % (img_h - y);
217e88f27b3Smrg
218e88f27b3Smrg		img.width = img_w;
219e88f27b3Smrg		img.height = img_h;
220e88f27b3Smrg		img.stride = img.width * 4;
221e88f27b3Smrg		img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
222e88f27b3Smrg		img.color = 0xff000000 + (random() & 0xffffff);
223e88f27b3Smrg
224e88f27b3Smrg		ret = g2d_solid_fill(ctx, &img, x, y, w, h);
225e88f27b3Smrg		if (ret < 0)
226e88f27b3Smrg			goto err_fini;
227e88f27b3Smrg
228e88f27b3Smrg		ret = g2d_exec(ctx);
229e88f27b3Smrg		if (ret < 0)
230e88f27b3Smrg			break;
231e88f27b3Smrg	}
232e88f27b3Smrg
233e88f27b3Smrgerr_fini:
234e88f27b3Smrg	g2d_fini(ctx);
235e88f27b3Smrg
236e88f27b3Smrg	return ret;
237e88f27b3Smrg}
238e88f27b3Smrg
239e88f27b3Smrgstatic int g2d_copy_test(struct exynos_device *dev, struct exynos_bo *src,
240e88f27b3Smrg				struct exynos_bo *dst,
241e88f27b3Smrg				enum e_g2d_buf_type type)
242e88f27b3Smrg{
243e88f27b3Smrg	struct g2d_context *ctx;
244e6188e58Smrg	struct g2d_image src_img = {0}, dst_img = {0};
245e88f27b3Smrg	unsigned int src_x, src_y, dst_x, dst_y, img_w, img_h;
246e88f27b3Smrg	unsigned long userptr, size;
247e88f27b3Smrg	int ret;
248e88f27b3Smrg
249e88f27b3Smrg	ctx = g2d_init(dev->fd);
250e88f27b3Smrg	if (!ctx)
251e88f27b3Smrg		return -EFAULT;
252e88f27b3Smrg
253e88f27b3Smrg	dst_img.bo[0] = dst->handle;
254e88f27b3Smrg
255e88f27b3Smrg	src_x = 0;
256e88f27b3Smrg	src_y = 0;
257e88f27b3Smrg	dst_x = 0;
258e88f27b3Smrg	dst_y = 0;
259e88f27b3Smrg	img_w = screen_width;
260e88f27b3Smrg	img_h = screen_height;
261e88f27b3Smrg
262e88f27b3Smrg	switch (type) {
263e88f27b3Smrg	case G2D_IMGBUF_GEM:
264e88f27b3Smrg		src_img.bo[0] = src->handle;
265e88f27b3Smrg		break;
266e88f27b3Smrg	case G2D_IMGBUF_USERPTR:
267e88f27b3Smrg		size = img_w * img_h * 4;
268e88f27b3Smrg
269e88f27b3Smrg		userptr = (unsigned long)malloc(size);
270e88f27b3Smrg		if (!userptr) {
271e88f27b3Smrg			fprintf(stderr, "failed to allocate userptr.\n");
272d8807b2fSmrg			ret = -EFAULT;
273d8807b2fSmrg			goto fail;
274e88f27b3Smrg		}
275e88f27b3Smrg
276e88f27b3Smrg		src_img.user_ptr[0].userptr = userptr;
277e88f27b3Smrg		src_img.user_ptr[0].size = size;
278e88f27b3Smrg		break;
279e6188e58Smrg	case G2D_IMGBUF_COLOR:
280e88f27b3Smrg	default:
281e6188e58Smrg		ret = -EFAULT;
282e6188e58Smrg		goto fail;
283e88f27b3Smrg	}
284e88f27b3Smrg
285e88f27b3Smrg	printf("copy test with %s.\n",
286e88f27b3Smrg			type == G2D_IMGBUF_GEM ? "gem" : "userptr");
287e88f27b3Smrg
288e88f27b3Smrg	src_img.width = img_w;
289e88f27b3Smrg	src_img.height = img_h;
290e88f27b3Smrg	src_img.stride = src_img.width * 4;
291e88f27b3Smrg	src_img.buf_type = type;
292e88f27b3Smrg	src_img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
293e88f27b3Smrg	src_img.color = 0xffff0000;
294e88f27b3Smrg	ret = g2d_solid_fill(ctx, &src_img, src_x, src_y, img_w, img_h);
295e88f27b3Smrg	if (ret < 0)
296e88f27b3Smrg		goto err_free_userptr;
297e88f27b3Smrg
298e88f27b3Smrg	dst_img.width = img_w;
299e88f27b3Smrg	dst_img.height = img_h;
300e88f27b3Smrg	dst_img.stride = dst_img.width * 4;
301e88f27b3Smrg	dst_img.buf_type = G2D_IMGBUF_GEM;
302e88f27b3Smrg	dst_img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
303e88f27b3Smrg
304e88f27b3Smrg	ret = g2d_copy(ctx, &src_img, &dst_img, src_x, src_y, dst_x, dst_y,
305e88f27b3Smrg			img_w - 4, img_h - 4);
306e88f27b3Smrg	if (ret < 0)
307e88f27b3Smrg		goto err_free_userptr;
308e88f27b3Smrg
309e88f27b3Smrg	g2d_exec(ctx);
310e88f27b3Smrg
311e88f27b3Smrgerr_free_userptr:
312e88f27b3Smrg	if (type == G2D_IMGBUF_USERPTR)
313e88f27b3Smrg		if (userptr)
314e88f27b3Smrg			free((void *)userptr);
315e88f27b3Smrg
316e6188e58Smrgfail:
317e88f27b3Smrg	g2d_fini(ctx);
318e88f27b3Smrg
319e88f27b3Smrg	return ret;
320e88f27b3Smrg}
321e88f27b3Smrg
3223f012e29Smrgstatic int g2d_move_test(struct exynos_device *dev,
3233f012e29Smrg				struct exynos_bo *tmp,
3243f012e29Smrg				struct exynos_bo *buf,
3253f012e29Smrg				enum e_g2d_buf_type type)
3263f012e29Smrg{
3273f012e29Smrg	struct g2d_context *ctx;
3283f012e29Smrg	struct g2d_image img = {0}, tmp_img = {0};
3293f012e29Smrg	unsigned int img_w, img_h, count;
3303f012e29Smrg	int cur_x, cur_y;
3313f012e29Smrg	void *checkerboard;
3323f012e29Smrg	int ret;
3333f012e29Smrg
3343f012e29Smrg	static const struct g2d_step {
3353f012e29Smrg		int x, y;
3363f012e29Smrg	} steps[] = {
3373f012e29Smrg		{ 1,  0}, { 0,  1},
3383f012e29Smrg		{-1,  0}, { 0, -1},
3393f012e29Smrg		{ 1,  1}, {-1, -1},
3403f012e29Smrg		{ 1, -1}, {-1,  1},
3413f012e29Smrg		{ 2,  1}, { 1,  2},
3423f012e29Smrg		{-2, -1}, {-1, -2},
3433f012e29Smrg		{ 2, -1}, { 1, -2},
3443f012e29Smrg		{-2,  1}, {-1,  2}
3453f012e29Smrg	};
3463f012e29Smrg	static const unsigned int num_steps =
3473f012e29Smrg		sizeof(steps) / sizeof(struct g2d_step);
3483f012e29Smrg
3493f012e29Smrg	ctx = g2d_init(dev->fd);
3503f012e29Smrg	if (!ctx)
3513f012e29Smrg		return -EFAULT;
3523f012e29Smrg
3533f012e29Smrg	img.bo[0] = buf->handle;
3543f012e29Smrg
3553f012e29Smrg	/* create pattern of half the screen size */
3563f012e29Smrg	checkerboard = create_checkerboard_pattern(screen_width / 64, screen_height / 64, 32);
3573f012e29Smrg	if (!checkerboard) {
3583f012e29Smrg		ret = -EFAULT;
3593f012e29Smrg		goto fail;
3603f012e29Smrg	}
3613f012e29Smrg
3623f012e29Smrg	img_w = (screen_width / 64) * 32;
3633f012e29Smrg	img_h = (screen_height / 64) * 32;
3643f012e29Smrg
3653f012e29Smrg	switch (type) {
3663f012e29Smrg	case G2D_IMGBUF_GEM:
3673f012e29Smrg		memcpy(tmp->vaddr, checkerboard, img_w * img_h * 4);
3683f012e29Smrg		tmp_img.bo[0] = tmp->handle;
3693f012e29Smrg		break;
3703f012e29Smrg	case G2D_IMGBUF_USERPTR:
3713f012e29Smrg		tmp_img.user_ptr[0].userptr = (unsigned long)checkerboard;
3723f012e29Smrg		tmp_img.user_ptr[0].size = img_w * img_h * 4;
3733f012e29Smrg		break;
3743f012e29Smrg	case G2D_IMGBUF_COLOR:
3753f012e29Smrg	default:
3763f012e29Smrg		ret = -EFAULT;
3773f012e29Smrg		goto fail;
3783f012e29Smrg	}
3793f012e29Smrg
3803f012e29Smrg	/* solid fill framebuffer with white color */
3813f012e29Smrg	img.width = screen_width;
3823f012e29Smrg	img.height = screen_height;
3833f012e29Smrg	img.stride = screen_width * 4;
3843f012e29Smrg	img.buf_type = G2D_IMGBUF_GEM;
3853f012e29Smrg	img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
3863f012e29Smrg	img.color = 0xffffffff;
3873f012e29Smrg
3883f012e29Smrg	/* put checkerboard pattern in the center of the framebuffer */
3893f012e29Smrg	cur_x = (screen_width - img_w) / 2;
3903f012e29Smrg	cur_y = (screen_height - img_h) / 2;
3913f012e29Smrg	tmp_img.width = img_w;
3923f012e29Smrg	tmp_img.height = img_h;
3933f012e29Smrg	tmp_img.stride = img_w * 4;
3943f012e29Smrg	tmp_img.buf_type = type;
3953f012e29Smrg	tmp_img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
3963f012e29Smrg
3973f012e29Smrg	ret = g2d_solid_fill(ctx, &img, 0, 0, screen_width, screen_height) ||
3983f012e29Smrg		g2d_copy(ctx, &tmp_img, &img, 0, 0, cur_x, cur_y, img_w, img_h);
3993f012e29Smrg
4003f012e29Smrg	if (!ret)
4013f012e29Smrg		ret = g2d_exec(ctx);
4023f012e29Smrg	if (ret < 0)
4033f012e29Smrg			goto fail;
4043f012e29Smrg
4053f012e29Smrg	printf("move test with %s.\n",
4063f012e29Smrg			type == G2D_IMGBUF_GEM ? "gem" : "userptr");
4073f012e29Smrg
4083f012e29Smrg	srand(time(NULL));
4093f012e29Smrg	for (count = 0; count < 256; ++count) {
4103f012e29Smrg		const struct g2d_step *s;
4113f012e29Smrg
4123f012e29Smrg		/* select step and validate it */
4133f012e29Smrg		while (1) {
4143f012e29Smrg			s = &steps[random() % num_steps];
4153f012e29Smrg
4163f012e29Smrg			if (cur_x + s->x < 0 || cur_y + s->y < 0 ||
4173f012e29Smrg				cur_x + img_w + s->x >= screen_width ||
4183f012e29Smrg				cur_y + img_h + s->y >= screen_height)
4193f012e29Smrg				continue;
4203f012e29Smrg			else
4213f012e29Smrg				break;
4223f012e29Smrg		}
4233f012e29Smrg
4243f012e29Smrg		ret = g2d_move(ctx, &img, cur_x, cur_y, cur_x + s->x, cur_y + s->y,
4253f012e29Smrg			img_w, img_h);
4263f012e29Smrg		if (!ret)
4273f012e29Smrg			ret = g2d_exec(ctx);
4283f012e29Smrg
4293f012e29Smrg		if (ret < 0)
4303f012e29Smrg			goto fail;
4313f012e29Smrg
4323f012e29Smrg		cur_x += s->x;
4333f012e29Smrg		cur_y += s->y;
4343f012e29Smrg
4353f012e29Smrg		usleep(100000);
4363f012e29Smrg	}
4373f012e29Smrg
4383f012e29Smrgfail:
4393f012e29Smrg	g2d_fini(ctx);
4403f012e29Smrg
4413f012e29Smrg	free(checkerboard);
4423f012e29Smrg
4433f012e29Smrg	return ret;
4443f012e29Smrg}
4453f012e29Smrg
446e88f27b3Smrgstatic int g2d_copy_with_scale_test(struct exynos_device *dev,
447e88f27b3Smrg					struct exynos_bo *src,
448e88f27b3Smrg					struct exynos_bo *dst,
449e88f27b3Smrg					enum e_g2d_buf_type type)
450e88f27b3Smrg{
451e88f27b3Smrg	struct g2d_context *ctx;
452e6188e58Smrg	struct g2d_image src_img = {0}, dst_img = {0};
453e6188e58Smrg	unsigned int src_x, src_y, img_w, img_h;
454e88f27b3Smrg	unsigned long userptr, size;
455e88f27b3Smrg	int ret;
456e88f27b3Smrg
457e88f27b3Smrg	ctx = g2d_init(dev->fd);
458e88f27b3Smrg	if (!ctx)
459e88f27b3Smrg		return -EFAULT;
460e88f27b3Smrg
461e88f27b3Smrg	dst_img.bo[0] = dst->handle;
462e88f27b3Smrg
463e88f27b3Smrg	src_x = 0;
464e88f27b3Smrg	src_y = 0;
465e88f27b3Smrg	img_w = screen_width;
466e88f27b3Smrg	img_h = screen_height;
467e88f27b3Smrg
468e88f27b3Smrg	switch (type) {
469e88f27b3Smrg	case G2D_IMGBUF_GEM:
470e88f27b3Smrg		src_img.bo[0] = src->handle;
471e88f27b3Smrg		break;
472e88f27b3Smrg	case G2D_IMGBUF_USERPTR:
473e88f27b3Smrg		size = img_w * img_h * 4;
474e88f27b3Smrg
475e88f27b3Smrg		userptr = (unsigned long)malloc(size);
476e88f27b3Smrg		if (!userptr) {
477e88f27b3Smrg			fprintf(stderr, "failed to allocate userptr.\n");
478d8807b2fSmrg			ret = -EFAULT;
479d8807b2fSmrg			goto fail;
480e88f27b3Smrg		}
481e88f27b3Smrg
482e88f27b3Smrg		src_img.user_ptr[0].userptr = userptr;
483e88f27b3Smrg		src_img.user_ptr[0].size = size;
484e88f27b3Smrg		break;
485e6188e58Smrg	case G2D_IMGBUF_COLOR:
486e88f27b3Smrg	default:
487e6188e58Smrg		ret = -EFAULT;
488e6188e58Smrg		goto fail;
489e88f27b3Smrg	}
490e88f27b3Smrg
491e88f27b3Smrg	printf("copy and scale test with %s.\n",
492e88f27b3Smrg			type == G2D_IMGBUF_GEM ? "gem" : "userptr");
493e88f27b3Smrg
494e88f27b3Smrg	src_img.width = img_w;
495e88f27b3Smrg	src_img.height = img_h;
496e88f27b3Smrg	src_img.stride = src_img.width * 4;
497e88f27b3Smrg	src_img.buf_type = type;
498e88f27b3Smrg	src_img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
499e88f27b3Smrg	src_img.color = 0xffffffff;
500e88f27b3Smrg	ret = g2d_solid_fill(ctx, &src_img, src_x, src_y, img_w ,  img_h);
501e88f27b3Smrg	if (ret < 0)
502e88f27b3Smrg		goto err_free_userptr;
503e88f27b3Smrg
504e88f27b3Smrg	src_img.color = 0xff00ff00;
505e88f27b3Smrg	ret = g2d_solid_fill(ctx, &src_img, 5, 5, 100, 100);
506e88f27b3Smrg	if (ret < 0)
507e88f27b3Smrg		goto err_free_userptr;
508e88f27b3Smrg
509e88f27b3Smrg	dst_img.width = img_w;
510e88f27b3Smrg	dst_img.height = img_h;
511e88f27b3Smrg	dst_img.buf_type = G2D_IMGBUF_GEM;
512e88f27b3Smrg	dst_img.stride = dst_img.width * 4;
513e88f27b3Smrg	dst_img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
514e88f27b3Smrg
515e88f27b3Smrg	ret = g2d_copy_with_scale(ctx, &src_img, &dst_img, 5, 5, 100, 100,
516e88f27b3Smrg					100, 100, 200, 200, 0);
517e88f27b3Smrg	if (ret < 0)
518e88f27b3Smrg		goto err_free_userptr;
519e88f27b3Smrg
520e88f27b3Smrg	g2d_exec(ctx);
521e88f27b3Smrg
522e88f27b3Smrgerr_free_userptr:
523e88f27b3Smrg	if (type == G2D_IMGBUF_USERPTR)
524e88f27b3Smrg		if (userptr)
525e88f27b3Smrg			free((void *)userptr);
526e88f27b3Smrg
527e6188e58Smrgfail:
528e88f27b3Smrg	g2d_fini(ctx);
529e88f27b3Smrg
53000a23bdaSmrg	return ret;
531e88f27b3Smrg}
532e88f27b3Smrg
53300a23bdaSmrg#ifdef EXYNOS_G2D_USERPTR_TEST
534e88f27b3Smrgstatic int g2d_blend_test(struct exynos_device *dev,
535e88f27b3Smrg					struct exynos_bo *src,
536e88f27b3Smrg					struct exynos_bo *dst,
537e88f27b3Smrg					enum e_g2d_buf_type type)
538e88f27b3Smrg{
539e88f27b3Smrg	struct g2d_context *ctx;
540e6188e58Smrg	struct g2d_image src_img = {0}, dst_img = {0};
541e88f27b3Smrg	unsigned int src_x, src_y, dst_x, dst_y, img_w, img_h;
542e88f27b3Smrg	unsigned long userptr, size;
543e88f27b3Smrg	int ret;
544e88f27b3Smrg
545e88f27b3Smrg	ctx = g2d_init(dev->fd);
546e88f27b3Smrg	if (!ctx)
547e88f27b3Smrg		return -EFAULT;
548e88f27b3Smrg
549e88f27b3Smrg	dst_img.bo[0] = dst->handle;
550e88f27b3Smrg
551e88f27b3Smrg	src_x = 0;
552e88f27b3Smrg	src_y = 0;
553e88f27b3Smrg	dst_x = 0;
554e88f27b3Smrg	dst_y = 0;
555e88f27b3Smrg	img_w = screen_width;
556e88f27b3Smrg	img_h = screen_height;
557e88f27b3Smrg
558e88f27b3Smrg	switch (type) {
559e88f27b3Smrg	case G2D_IMGBUF_GEM:
560e88f27b3Smrg		src_img.bo[0] = src->handle;
561e88f27b3Smrg		break;
562e88f27b3Smrg	case G2D_IMGBUF_USERPTR:
563e88f27b3Smrg		size = img_w * img_h * 4;
564e88f27b3Smrg
565e88f27b3Smrg		userptr = (unsigned long)malloc(size);
566e88f27b3Smrg		if (!userptr) {
567e88f27b3Smrg			fprintf(stderr, "failed to allocate userptr.\n");
568d8807b2fSmrg			ret = -EFAULT;
569d8807b2fSmrg			goto fail;
570e88f27b3Smrg		}
571e88f27b3Smrg
572e88f27b3Smrg		src_img.user_ptr[0].userptr = userptr;
573e88f27b3Smrg		src_img.user_ptr[0].size = size;
574e88f27b3Smrg		break;
575e6188e58Smrg	case G2D_IMGBUF_COLOR:
576e88f27b3Smrg	default:
577e6188e58Smrg		ret = -EFAULT;
578e6188e58Smrg		goto fail;
579e88f27b3Smrg	}
580e88f27b3Smrg
581e88f27b3Smrg	printf("blend test with %s.\n",
582e88f27b3Smrg			type == G2D_IMGBUF_GEM ? "gem" : "userptr");
583e88f27b3Smrg
584e88f27b3Smrg	src_img.width = img_w;
585e88f27b3Smrg	src_img.height = img_h;
586e88f27b3Smrg	src_img.stride = src_img.width * 4;
587e88f27b3Smrg	src_img.buf_type = type;
588e88f27b3Smrg	src_img.select_mode = G2D_SELECT_MODE_NORMAL;
589e88f27b3Smrg	src_img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
590e88f27b3Smrg	src_img.color = 0xffffffff;
591e88f27b3Smrg	ret = g2d_solid_fill(ctx, &src_img, src_x, src_y, img_w, img_h);
592e88f27b3Smrg	if (ret < 0)
593e88f27b3Smrg		goto err_free_userptr;
594e88f27b3Smrg
595e88f27b3Smrg	src_img.color = 0x770000ff;
596e88f27b3Smrg	ret = g2d_solid_fill(ctx, &src_img, 5, 5, 200, 200);
597e88f27b3Smrg	if (ret < 0)
598e88f27b3Smrg		goto err_free_userptr;
599e88f27b3Smrg
600e88f27b3Smrg	dst_img.width = img_w;
601e88f27b3Smrg	dst_img.height = img_h;
602e88f27b3Smrg	dst_img.stride = dst_img.width * 4;
603e88f27b3Smrg	dst_img.buf_type = G2D_IMGBUF_GEM;
604e88f27b3Smrg	dst_img.select_mode = G2D_SELECT_MODE_NORMAL;
605e88f27b3Smrg	dst_img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
606e88f27b3Smrg	dst_img.color = 0xffffffff;
607e88f27b3Smrg	ret = g2d_solid_fill(ctx, &dst_img, dst_x, dst_y, img_w, img_h);
608e88f27b3Smrg	if (ret < 0)
609e88f27b3Smrg		goto err_free_userptr;
610e88f27b3Smrg
611e88f27b3Smrg	dst_img.color = 0x77ff0000;
612e88f27b3Smrg	ret = g2d_solid_fill(ctx, &dst_img, 105, 105, 200, 200);
613e88f27b3Smrg	if (ret < 0)
614e88f27b3Smrg		goto err_free_userptr;
615e88f27b3Smrg
616e88f27b3Smrg	ret = g2d_blend(ctx, &src_img, &dst_img, 5, 5, 105, 105, 200, 200,
617e88f27b3Smrg			G2D_OP_OVER);
618e88f27b3Smrg	if (ret < 0)
619e88f27b3Smrg		goto err_free_userptr;
620e88f27b3Smrg
621e88f27b3Smrg	g2d_exec(ctx);
622e88f27b3Smrg
623e88f27b3Smrgerr_free_userptr:
624e88f27b3Smrg	if (type == G2D_IMGBUF_USERPTR)
625e88f27b3Smrg		if (userptr)
626e88f27b3Smrg			free((void *)userptr);
627e88f27b3Smrg
628e6188e58Smrgfail:
629e88f27b3Smrg	g2d_fini(ctx);
630e88f27b3Smrg
631d8807b2fSmrg	return ret;
632e88f27b3Smrg}
633d8807b2fSmrg#endif
634e88f27b3Smrg
635e6188e58Smrgstatic int g2d_checkerboard_test(struct exynos_device *dev,
636e6188e58Smrg					struct exynos_bo *src,
637e6188e58Smrg					struct exynos_bo *dst,
638e6188e58Smrg					enum e_g2d_buf_type type)
639e6188e58Smrg{
640e6188e58Smrg	struct g2d_context *ctx;
641e6188e58Smrg	struct g2d_image src_img = {0}, dst_img = {0};
642e6188e58Smrg	unsigned int src_x, src_y, dst_x, dst_y, img_w, img_h;
643e6188e58Smrg	void *checkerboard = NULL;
644e6188e58Smrg	int ret;
645e6188e58Smrg
646e6188e58Smrg	ctx = g2d_init(dev->fd);
647e6188e58Smrg	if (!ctx)
648e6188e58Smrg		return -EFAULT;
649e6188e58Smrg
650e6188e58Smrg	dst_img.bo[0] = dst->handle;
651e6188e58Smrg
652e6188e58Smrg	src_x = 0;
653e6188e58Smrg	src_y = 0;
654e6188e58Smrg	dst_x = 0;
655e6188e58Smrg	dst_y = 0;
656e6188e58Smrg
657e6188e58Smrg	checkerboard = create_checkerboard_pattern(screen_width / 32, screen_height / 32, 32);
658d8807b2fSmrg	if (!checkerboard) {
659d8807b2fSmrg		ret = -EFAULT;
660e6188e58Smrg		goto fail;
661e6188e58Smrg	}
662e6188e58Smrg
663e6188e58Smrg	img_w = screen_width - (screen_width % 32);
664e6188e58Smrg	img_h = screen_height - (screen_height % 32);
665e6188e58Smrg
666e6188e58Smrg	switch (type) {
667e6188e58Smrg	case G2D_IMGBUF_GEM:
668e6188e58Smrg		memcpy(src->vaddr, checkerboard, img_w * img_h * 4);
669e6188e58Smrg		src_img.bo[0] = src->handle;
670e6188e58Smrg		break;
671e6188e58Smrg	case G2D_IMGBUF_USERPTR:
672e6188e58Smrg		src_img.user_ptr[0].userptr = (unsigned long)checkerboard;
673e6188e58Smrg		src_img.user_ptr[0].size = img_w * img_h * 4;
674e6188e58Smrg		break;
675e6188e58Smrg	case G2D_IMGBUF_COLOR:
676e6188e58Smrg	default:
677e6188e58Smrg		ret = -EFAULT;
678e6188e58Smrg		goto fail;
679e6188e58Smrg	}
680e6188e58Smrg
681e6188e58Smrg	printf("checkerboard test with %s.\n",
682e6188e58Smrg			type == G2D_IMGBUF_GEM ? "gem" : "userptr");
683e6188e58Smrg
684e6188e58Smrg	src_img.width = img_w;
685e6188e58Smrg	src_img.height = img_h;
686e6188e58Smrg	src_img.stride = src_img.width * 4;
687e6188e58Smrg	src_img.buf_type = type;
688e6188e58Smrg	src_img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
689e6188e58Smrg
690e6188e58Smrg	dst_img.width = screen_width;
691e6188e58Smrg	dst_img.height = screen_height;
692e6188e58Smrg	dst_img.stride = dst_img.width * 4;
693e6188e58Smrg	dst_img.buf_type = G2D_IMGBUF_GEM;
694e6188e58Smrg	dst_img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
695e6188e58Smrg	src_img.color = 0xff000000;
696e6188e58Smrg	ret = g2d_solid_fill(ctx, &dst_img, src_x, src_y, screen_width, screen_height);
697e6188e58Smrg	if (ret < 0)
698e6188e58Smrg		goto fail;
699e6188e58Smrg
700e6188e58Smrg	ret = g2d_copy(ctx, &src_img, &dst_img, src_x, src_y, dst_x, dst_y,
701e6188e58Smrg			img_w, img_h);
702e6188e58Smrg	if (ret < 0)
703e6188e58Smrg		goto fail;
704e6188e58Smrg
705e6188e58Smrg	g2d_exec(ctx);
706e6188e58Smrg
707e6188e58Smrgfail:
708e6188e58Smrg	free(checkerboard);
709e6188e58Smrg	g2d_fini(ctx);
710e6188e58Smrg
711e6188e58Smrg	return ret;
712e6188e58Smrg}
713e88f27b3Smrg
714e88f27b3Smrgstatic void usage(char *name)
715e88f27b3Smrg{
716e88f27b3Smrg	fprintf(stderr, "usage: %s [-s]\n", name);
717e88f27b3Smrg	fprintf(stderr, "-s <connector_id>@<crtc_id>:<mode>\n");
718e88f27b3Smrg	exit(0);
719e88f27b3Smrg}
720e88f27b3Smrg
721e88f27b3Smrgextern char *optarg;
722e88f27b3Smrgstatic const char optstr[] = "s:";
723e88f27b3Smrg
724e88f27b3Smrgint main(int argc, char **argv)
725e88f27b3Smrg{
726e88f27b3Smrg	struct exynos_device *dev;
727e88f27b3Smrg	struct exynos_bo *bo, *src;
728e88f27b3Smrg	struct connector con;
729e88f27b3Smrg	unsigned int fb_id;
730e88f27b3Smrg	uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
731e88f27b3Smrg	drmModeRes *resources;
732e88f27b3Smrg	int ret, fd, c;
733e88f27b3Smrg
734e88f27b3Smrg	memset(&con, 0, sizeof(struct connector));
735e88f27b3Smrg
736e88f27b3Smrg	if (argc != 3) {
737e88f27b3Smrg		usage(argv[0]);
738e88f27b3Smrg		return -EINVAL;
739e88f27b3Smrg	}
740e88f27b3Smrg
741e88f27b3Smrg	while ((c = getopt(argc, argv, optstr)) != -1) {
742e88f27b3Smrg		switch (c) {
743e88f27b3Smrg		case 's':
744e88f27b3Smrg			con.crtc = -1;
745e88f27b3Smrg			if (sscanf(optarg, "%d:0x%64s",
746e88f27b3Smrg						&con.id,
747e88f27b3Smrg						con.mode_str) != 2 &&
748e88f27b3Smrg					sscanf(optarg, "%d@%d:%64s",
749e88f27b3Smrg						&con.id,
750e88f27b3Smrg						&con.crtc,
751e88f27b3Smrg						con.mode_str) != 3)
752e88f27b3Smrg				usage(argv[0]);
753e88f27b3Smrg			break;
754e88f27b3Smrg		default:
755e88f27b3Smrg			usage(argv[0]);
756e6188e58Smrg			break;
757e88f27b3Smrg		}
758e88f27b3Smrg	}
759e88f27b3Smrg
760e88f27b3Smrg	fd = drmOpen(DRM_MODULE_NAME, NULL);
761e88f27b3Smrg	if (fd < 0) {
762e88f27b3Smrg		fprintf(stderr, "failed to open.\n");
763e88f27b3Smrg		return fd;
764e88f27b3Smrg	}
765e88f27b3Smrg
766e88f27b3Smrg	dev = exynos_device_create(fd);
767e88f27b3Smrg	if (!dev) {
768d8807b2fSmrg		ret = -EFAULT;
769d8807b2fSmrg		goto err_drm_close;
770e88f27b3Smrg	}
771e88f27b3Smrg
772e88f27b3Smrg	resources = drmModeGetResources(dev->fd);
773e88f27b3Smrg	if (!resources) {
774e88f27b3Smrg		fprintf(stderr, "drmModeGetResources failed: %s\n",
775e88f27b3Smrg				strerror(errno));
776e88f27b3Smrg		ret = -EFAULT;
777d8807b2fSmrg		goto err_dev_destory;
778e88f27b3Smrg	}
779e88f27b3Smrg
780e88f27b3Smrg	connector_find_mode(dev->fd, &con, resources);
781e88f27b3Smrg	drmModeFreeResources(resources);
782e88f27b3Smrg
783e6188e58Smrg	if (!con.mode) {
784e6188e58Smrg		fprintf(stderr, "failed to find usable connector\n");
785e6188e58Smrg		ret = -EFAULT;
786d8807b2fSmrg		goto err_dev_destory;
787e6188e58Smrg	}
788e6188e58Smrg
789e88f27b3Smrg	screen_width = con.mode->hdisplay;
790e88f27b3Smrg	screen_height = con.mode->vdisplay;
791e88f27b3Smrg
792e6188e58Smrg	if (screen_width == 0 || screen_height == 0) {
793e6188e58Smrg		fprintf(stderr, "failed to find sane resolution on connector\n");
794e6188e58Smrg		ret = -EFAULT;
795d8807b2fSmrg		goto err_dev_destory;
796e6188e58Smrg	}
797e6188e58Smrg
798e6188e58Smrg	printf("screen width = %d, screen height = %d\n", screen_width,
799e88f27b3Smrg			screen_height);
800e88f27b3Smrg
801e88f27b3Smrg	bo = exynos_create_buffer(dev, screen_width * screen_height * 4, 0);
802e88f27b3Smrg	if (!bo) {
803e88f27b3Smrg		ret = -EFAULT;
804d8807b2fSmrg		goto err_dev_destory;
805e88f27b3Smrg	}
806e88f27b3Smrg
807e88f27b3Smrg	handles[0] = bo->handle;
808e88f27b3Smrg	pitches[0] = screen_width * 4;
809e88f27b3Smrg	offsets[0] = 0;
810e88f27b3Smrg
811e88f27b3Smrg	ret = drmModeAddFB2(dev->fd, screen_width, screen_height,
8123f012e29Smrg				DRM_FORMAT_XRGB8888, handles,
813e88f27b3Smrg				pitches, offsets, &fb_id, 0);
814e88f27b3Smrg	if (ret < 0)
815e88f27b3Smrg		goto err_destroy_buffer;
816e88f27b3Smrg
817e88f27b3Smrg	memset(bo->vaddr, 0xff, screen_width * screen_height * 4);
818e88f27b3Smrg
819e88f27b3Smrg	ret = drm_set_crtc(dev, &con, fb_id);
820e88f27b3Smrg	if (ret < 0)
821e88f27b3Smrg		goto err_rm_fb;
822e88f27b3Smrg
823e6188e58Smrg	ret = g2d_solid_fill_test(dev, bo);
824e88f27b3Smrg	if (ret < 0) {
825e88f27b3Smrg		fprintf(stderr, "failed to solid fill operation.\n");
826e88f27b3Smrg		goto err_rm_fb;
827e88f27b3Smrg	}
828e88f27b3Smrg
829e6188e58Smrg	wait_for_user_input(0);
830e88f27b3Smrg
831e88f27b3Smrg	src = exynos_create_buffer(dev, screen_width * screen_height * 4, 0);
832e88f27b3Smrg	if (!src) {
833e88f27b3Smrg		ret = -EFAULT;
834e88f27b3Smrg		goto err_rm_fb;
835e88f27b3Smrg	}
836e88f27b3Smrg
837e6188e58Smrg	ret = g2d_copy_test(dev, src, bo, G2D_IMGBUF_GEM);
838e88f27b3Smrg	if (ret < 0) {
839e88f27b3Smrg		fprintf(stderr, "failed to test copy operation.\n");
840e88f27b3Smrg		goto err_free_src;
841e88f27b3Smrg	}
842e88f27b3Smrg
843e6188e58Smrg	wait_for_user_input(0);
844e88f27b3Smrg
8453f012e29Smrg	ret = g2d_move_test(dev, src, bo, G2D_IMGBUF_GEM);
8463f012e29Smrg	if (ret < 0) {
8473f012e29Smrg		fprintf(stderr, "failed to test move operation.\n");
8483f012e29Smrg		goto err_free_src;
8493f012e29Smrg	}
8503f012e29Smrg
8513f012e29Smrg	wait_for_user_input(0);
8523f012e29Smrg
853e6188e58Smrg	ret = g2d_copy_with_scale_test(dev, src, bo, G2D_IMGBUF_GEM);
854e88f27b3Smrg	if (ret < 0) {
855e88f27b3Smrg		fprintf(stderr, "failed to test copy and scale operation.\n");
856e88f27b3Smrg		goto err_free_src;
857e88f27b3Smrg	}
858e88f27b3Smrg
859e6188e58Smrg	wait_for_user_input(0);
860e88f27b3Smrg
861e6188e58Smrg	ret = g2d_checkerboard_test(dev, src, bo, G2D_IMGBUF_GEM);
862e6188e58Smrg	if (ret < 0) {
863e6188e58Smrg		fprintf(stderr, "failed to issue checkerboard test.\n");
864e6188e58Smrg		goto err_free_src;
865e6188e58Smrg	}
866e6188e58Smrg
867e6188e58Smrg	wait_for_user_input(1);
868e6188e58Smrg
869e6188e58Smrg	/*
870e6188e58Smrg	 * The blend test uses the userptr functionality of exynos-drm, which
871e6188e58Smrg	 * is currently not safe to use. If the kernel hasn't been build with
872e6188e58Smrg	 * exynos-iommu support, then the blend test is going to produce (kernel)
873e6188e58Smrg	 * memory corruption, eventually leading to a system crash.
874e6188e58Smrg	 *
875e6188e58Smrg	 * Disable the test for now, until the kernel code has been sanitized.
876e6188e58Smrg	 */
87700a23bdaSmrg#ifdef EXYNOS_G2D_USERPTR_TEST
878e6188e58Smrg	ret  = g2d_blend_test(dev, src, bo, G2D_IMGBUF_USERPTR);
879e88f27b3Smrg	if (ret < 0)
880e88f27b3Smrg		fprintf(stderr, "failed to test blend operation.\n");
881e88f27b3Smrg
882e88f27b3Smrg	getchar();
883e6188e58Smrg#endif
884e88f27b3Smrg
885e88f27b3Smrgerr_free_src:
886e88f27b3Smrg	if (src)
887e88f27b3Smrg		exynos_destroy_buffer(src);
888e88f27b3Smrg
889e88f27b3Smrgerr_rm_fb:
890baaff307Smrg	drmModeRmFB(dev->fd, fb_id);
891e88f27b3Smrg
892e88f27b3Smrgerr_destroy_buffer:
893e88f27b3Smrg	exynos_destroy_buffer(bo);
894e88f27b3Smrg
895d8807b2fSmrgerr_dev_destory:
896e88f27b3Smrg	exynos_device_destroy(dev);
897e88f27b3Smrg
898d8807b2fSmrgerr_drm_close:
899d8807b2fSmrg	drmClose(fd);
900d8807b2fSmrg
901d8807b2fSmrg	return ret;
902e88f27b3Smrg}
903