1d722e3fbSopenharmony_ci/*
2d722e3fbSopenharmony_ci * Copyright (C) 2015 - Tobias Jakobi
3d722e3fbSopenharmony_ci *
4d722e3fbSopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
5d722e3fbSopenharmony_ci * copy of this software and associated documentation files (the "Software"),
6d722e3fbSopenharmony_ci * to deal in the Software without restriction, including without limitation
7d722e3fbSopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8d722e3fbSopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
9d722e3fbSopenharmony_ci * Software is furnished to do so, subject to the following conditions:
10d722e3fbSopenharmony_ci *
11d722e3fbSopenharmony_ci * The above copyright notice and this permission notice (including the next
12d722e3fbSopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
13d722e3fbSopenharmony_ci * Software.
14d722e3fbSopenharmony_ci *
15d722e3fbSopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16d722e3fbSopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17d722e3fbSopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18d722e3fbSopenharmony_ci * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19d722e3fbSopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20d722e3fbSopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21d722e3fbSopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE.
22d722e3fbSopenharmony_ci */
23d722e3fbSopenharmony_ci
24d722e3fbSopenharmony_ci#include <unistd.h>
25d722e3fbSopenharmony_ci#include <poll.h>
26d722e3fbSopenharmony_ci
27d722e3fbSopenharmony_ci#include <stdlib.h>
28d722e3fbSopenharmony_ci#include <stdio.h>
29d722e3fbSopenharmony_ci#include <time.h>
30d722e3fbSopenharmony_ci#include <getopt.h>
31d722e3fbSopenharmony_ci
32d722e3fbSopenharmony_ci#include <pthread.h>
33d722e3fbSopenharmony_ci
34d722e3fbSopenharmony_ci#include <xf86drm.h>
35d722e3fbSopenharmony_ci
36d722e3fbSopenharmony_ci#include "exynos_drm.h"
37d722e3fbSopenharmony_ci#include "exynos_drmif.h"
38d722e3fbSopenharmony_ci#include "exynos_fimg2d.h"
39d722e3fbSopenharmony_ci
40d722e3fbSopenharmony_cistruct g2d_job {
41d722e3fbSopenharmony_ci	unsigned int id;
42d722e3fbSopenharmony_ci	unsigned int busy;
43d722e3fbSopenharmony_ci};
44d722e3fbSopenharmony_ci
45d722e3fbSopenharmony_cistruct exynos_evhandler {
46d722e3fbSopenharmony_ci	struct pollfd fds;
47d722e3fbSopenharmony_ci	struct exynos_event_context evctx;
48d722e3fbSopenharmony_ci};
49d722e3fbSopenharmony_ci
50d722e3fbSopenharmony_cistruct threaddata {
51d722e3fbSopenharmony_ci	unsigned int stop;
52d722e3fbSopenharmony_ci	struct exynos_device *dev;
53d722e3fbSopenharmony_ci	struct exynos_evhandler evhandler;
54d722e3fbSopenharmony_ci};
55d722e3fbSopenharmony_ci
56d722e3fbSopenharmony_cistatic void g2d_event_handler(int fd, unsigned int cmdlist_no, unsigned int tv_sec,
57d722e3fbSopenharmony_ci							unsigned int tv_usec, void *user_data)
58d722e3fbSopenharmony_ci{
59d722e3fbSopenharmony_ci	struct g2d_job *job = user_data;
60d722e3fbSopenharmony_ci
61d722e3fbSopenharmony_ci	fprintf(stderr, "info: g2d job (id = %u, cmdlist number = %u) finished!\n",
62d722e3fbSopenharmony_ci			job->id, cmdlist_no);
63d722e3fbSopenharmony_ci
64d722e3fbSopenharmony_ci	job->busy = 0;
65d722e3fbSopenharmony_ci}
66d722e3fbSopenharmony_ci
67d722e3fbSopenharmony_cistatic void setup_g2d_event_handler(struct exynos_evhandler *evhandler, int fd)
68d722e3fbSopenharmony_ci{
69d722e3fbSopenharmony_ci	evhandler->fds.fd = fd;
70d722e3fbSopenharmony_ci	evhandler->fds.events = POLLIN;
71d722e3fbSopenharmony_ci	evhandler->evctx.base.version = 2;
72d722e3fbSopenharmony_ci	evhandler->evctx.version = 1;
73d722e3fbSopenharmony_ci	evhandler->evctx.g2d_event_handler = g2d_event_handler;
74d722e3fbSopenharmony_ci}
75d722e3fbSopenharmony_ci
76d722e3fbSopenharmony_cistatic void* threadfunc(void *arg) {
77d722e3fbSopenharmony_ci	const int timeout = 0;
78d722e3fbSopenharmony_ci	struct threaddata *data;
79d722e3fbSopenharmony_ci
80d722e3fbSopenharmony_ci	data = arg;
81d722e3fbSopenharmony_ci
82d722e3fbSopenharmony_ci	while (1) {
83d722e3fbSopenharmony_ci		if (data->stop) break;
84d722e3fbSopenharmony_ci
85d722e3fbSopenharmony_ci		usleep(500);
86d722e3fbSopenharmony_ci
87d722e3fbSopenharmony_ci		data->evhandler.fds.revents = 0;
88d722e3fbSopenharmony_ci
89d722e3fbSopenharmony_ci		if (poll(&data->evhandler.fds, 1, timeout) < 0)
90d722e3fbSopenharmony_ci			continue;
91d722e3fbSopenharmony_ci
92d722e3fbSopenharmony_ci		if (data->evhandler.fds.revents & (POLLHUP | POLLERR))
93d722e3fbSopenharmony_ci			continue;
94d722e3fbSopenharmony_ci
95d722e3fbSopenharmony_ci		if (data->evhandler.fds.revents & POLLIN)
96d722e3fbSopenharmony_ci			exynos_handle_event(data->dev, &data->evhandler.evctx);
97d722e3fbSopenharmony_ci	}
98d722e3fbSopenharmony_ci
99d722e3fbSopenharmony_ci	pthread_exit(0);
100d722e3fbSopenharmony_ci}
101d722e3fbSopenharmony_ci
102d722e3fbSopenharmony_ci/*
103d722e3fbSopenharmony_ci * We need to wait until all G2D jobs are finished, otherwise we
104d722e3fbSopenharmony_ci * potentially remove a BO which the engine still operates on.
105d722e3fbSopenharmony_ci * This results in the following kernel message:
106d722e3fbSopenharmony_ci * [drm:exynos_drm_gem_put_dma_addr] *ERROR* failed to lookup gem object.
107d722e3fbSopenharmony_ci * Also any subsequent BO allocations fail then with:
108d722e3fbSopenharmony_ci * [drm:exynos_drm_alloc_buf] *ERROR* failed to allocate buffer.
109d722e3fbSopenharmony_ci */
110d722e3fbSopenharmony_cistatic void wait_all_jobs(struct g2d_job* jobs, unsigned num_jobs)
111d722e3fbSopenharmony_ci{
112d722e3fbSopenharmony_ci	unsigned i;
113d722e3fbSopenharmony_ci
114d722e3fbSopenharmony_ci	for (i = 0; i < num_jobs; ++i) {
115d722e3fbSopenharmony_ci		while (jobs[i].busy)
116d722e3fbSopenharmony_ci			usleep(500);
117d722e3fbSopenharmony_ci	}
118d722e3fbSopenharmony_ci
119d722e3fbSopenharmony_ci}
120d722e3fbSopenharmony_ci
121d722e3fbSopenharmony_cistatic struct g2d_job* free_job(struct g2d_job* jobs, unsigned num_jobs)
122d722e3fbSopenharmony_ci{
123d722e3fbSopenharmony_ci	unsigned i;
124d722e3fbSopenharmony_ci
125d722e3fbSopenharmony_ci	for (i = 0; i < num_jobs; ++i) {
126d722e3fbSopenharmony_ci		if (jobs[i].busy == 0)
127d722e3fbSopenharmony_ci			return &jobs[i];
128d722e3fbSopenharmony_ci	}
129d722e3fbSopenharmony_ci
130d722e3fbSopenharmony_ci	return NULL;
131d722e3fbSopenharmony_ci}
132d722e3fbSopenharmony_ci
133d722e3fbSopenharmony_cistatic int g2d_work(struct g2d_context *ctx, struct g2d_image *img,
134d722e3fbSopenharmony_ci					unsigned num_jobs, unsigned iterations)
135d722e3fbSopenharmony_ci{
136d722e3fbSopenharmony_ci	struct g2d_job *jobs = calloc(num_jobs, sizeof(struct g2d_job));
137d722e3fbSopenharmony_ci	int ret;
138d722e3fbSopenharmony_ci	unsigned i;
139d722e3fbSopenharmony_ci
140d722e3fbSopenharmony_ci	/* setup job ids */
141d722e3fbSopenharmony_ci	for (i = 0; i < num_jobs; ++i)
142d722e3fbSopenharmony_ci		jobs[i].id = i;
143d722e3fbSopenharmony_ci
144d722e3fbSopenharmony_ci	for (i = 0; i < iterations; ++i) {
145d722e3fbSopenharmony_ci		unsigned x, y, w, h;
146d722e3fbSopenharmony_ci
147d722e3fbSopenharmony_ci		struct g2d_job *j = NULL;
148d722e3fbSopenharmony_ci
149d722e3fbSopenharmony_ci		while (1) {
150d722e3fbSopenharmony_ci			j = free_job(jobs, num_jobs);
151d722e3fbSopenharmony_ci
152d722e3fbSopenharmony_ci			if (j)
153d722e3fbSopenharmony_ci				break;
154d722e3fbSopenharmony_ci			else
155d722e3fbSopenharmony_ci				usleep(500);
156d722e3fbSopenharmony_ci		}
157d722e3fbSopenharmony_ci
158d722e3fbSopenharmony_ci		x = rand() % img->width;
159d722e3fbSopenharmony_ci		y = rand() % img->height;
160d722e3fbSopenharmony_ci
161d722e3fbSopenharmony_ci		if (x == (img->width - 1))
162d722e3fbSopenharmony_ci			x -= 1;
163d722e3fbSopenharmony_ci		if (y == (img->height - 1))
164d722e3fbSopenharmony_ci			y -= 1;
165d722e3fbSopenharmony_ci
166d722e3fbSopenharmony_ci		w = rand() % (img->width - x);
167d722e3fbSopenharmony_ci		h = rand() % (img->height - y);
168d722e3fbSopenharmony_ci
169d722e3fbSopenharmony_ci		if (w == 0) w = 1;
170d722e3fbSopenharmony_ci		if (h == 0) h = 1;
171d722e3fbSopenharmony_ci
172d722e3fbSopenharmony_ci		img->color = rand();
173d722e3fbSopenharmony_ci
174d722e3fbSopenharmony_ci		j->busy = 1;
175d722e3fbSopenharmony_ci		g2d_config_event(ctx, j);
176d722e3fbSopenharmony_ci
177d722e3fbSopenharmony_ci		ret = g2d_solid_fill(ctx, img, x, y, w, h);
178d722e3fbSopenharmony_ci
179d722e3fbSopenharmony_ci		if (ret == 0)
180d722e3fbSopenharmony_ci			g2d_exec(ctx);
181d722e3fbSopenharmony_ci
182d722e3fbSopenharmony_ci		if (ret != 0) {
183d722e3fbSopenharmony_ci			fprintf(stderr, "error: iteration %u (x = %u, x = %u, x = %u, x = %u) failed\n",
184d722e3fbSopenharmony_ci					i, x, y, w, h);
185d722e3fbSopenharmony_ci			break;
186d722e3fbSopenharmony_ci		}
187d722e3fbSopenharmony_ci	}
188d722e3fbSopenharmony_ci
189d722e3fbSopenharmony_ci	wait_all_jobs(jobs, num_jobs);
190d722e3fbSopenharmony_ci	free(jobs);
191d722e3fbSopenharmony_ci
192d722e3fbSopenharmony_ci	return 0;
193d722e3fbSopenharmony_ci}
194d722e3fbSopenharmony_ci
195d722e3fbSopenharmony_cistatic void usage(const char *name)
196d722e3fbSopenharmony_ci{
197d722e3fbSopenharmony_ci	fprintf(stderr, "usage: %s [-ijwh]\n\n", name);
198d722e3fbSopenharmony_ci
199d722e3fbSopenharmony_ci	fprintf(stderr, "\t-i <number of iterations>\n");
200d722e3fbSopenharmony_ci	fprintf(stderr, "\t-j <number of G2D jobs> (default = 4)\n\n");
201d722e3fbSopenharmony_ci
202d722e3fbSopenharmony_ci	fprintf(stderr, "\t-w <buffer width> (default = 4096)\n");
203d722e3fbSopenharmony_ci	fprintf(stderr, "\t-h <buffer height> (default = 4096)\n");
204d722e3fbSopenharmony_ci
205d722e3fbSopenharmony_ci	exit(0);
206d722e3fbSopenharmony_ci}
207d722e3fbSopenharmony_ci
208d722e3fbSopenharmony_ciint main(int argc, char **argv)
209d722e3fbSopenharmony_ci{
210d722e3fbSopenharmony_ci	int fd, ret, c, parsefail;
211d722e3fbSopenharmony_ci
212d722e3fbSopenharmony_ci	pthread_t event_thread;
213d722e3fbSopenharmony_ci	struct threaddata event_data = {0};
214d722e3fbSopenharmony_ci
215d722e3fbSopenharmony_ci	struct exynos_device *dev;
216d722e3fbSopenharmony_ci	struct g2d_context *ctx;
217d722e3fbSopenharmony_ci	struct exynos_bo *bo;
218d722e3fbSopenharmony_ci
219d722e3fbSopenharmony_ci	struct g2d_image img = {0};
220d722e3fbSopenharmony_ci
221d722e3fbSopenharmony_ci	unsigned int iters = 0, njobs = 4;
222d722e3fbSopenharmony_ci	unsigned int bufw = 4096, bufh = 4096;
223d722e3fbSopenharmony_ci
224d722e3fbSopenharmony_ci	ret = 0;
225d722e3fbSopenharmony_ci	parsefail = 0;
226d722e3fbSopenharmony_ci
227d722e3fbSopenharmony_ci	while ((c = getopt(argc, argv, "i:j:w:h:")) != -1) {
228d722e3fbSopenharmony_ci		switch (c) {
229d722e3fbSopenharmony_ci		case 'i':
230d722e3fbSopenharmony_ci			if (sscanf(optarg, "%u", &iters) != 1)
231d722e3fbSopenharmony_ci				parsefail = 1;
232d722e3fbSopenharmony_ci			break;
233d722e3fbSopenharmony_ci		case 'j':
234d722e3fbSopenharmony_ci			if (sscanf(optarg, "%u", &njobs) != 1)
235d722e3fbSopenharmony_ci				parsefail = 1;
236d722e3fbSopenharmony_ci			break;
237d722e3fbSopenharmony_ci		case 'w':
238d722e3fbSopenharmony_ci			if (sscanf(optarg, "%u", &bufw) != 1)
239d722e3fbSopenharmony_ci				parsefail = 1;
240d722e3fbSopenharmony_ci			break;
241d722e3fbSopenharmony_ci		case 'h':
242d722e3fbSopenharmony_ci			if (sscanf(optarg, "%u", &bufh) != 1)
243d722e3fbSopenharmony_ci				parsefail = 1;
244d722e3fbSopenharmony_ci			break;
245d722e3fbSopenharmony_ci		default:
246d722e3fbSopenharmony_ci			parsefail = 1;
247d722e3fbSopenharmony_ci			break;
248d722e3fbSopenharmony_ci		}
249d722e3fbSopenharmony_ci	}
250d722e3fbSopenharmony_ci
251d722e3fbSopenharmony_ci	if (parsefail || (argc == 1) || (iters == 0))
252d722e3fbSopenharmony_ci		usage(argv[0]);
253d722e3fbSopenharmony_ci
254d722e3fbSopenharmony_ci	if (bufw > 4096 || bufh > 4096) {
255d722e3fbSopenharmony_ci		fprintf(stderr, "error: buffer width/height should be less than 4096.\n");
256d722e3fbSopenharmony_ci		ret = -1;
257d722e3fbSopenharmony_ci
258d722e3fbSopenharmony_ci		goto out;
259d722e3fbSopenharmony_ci	}
260d722e3fbSopenharmony_ci
261d722e3fbSopenharmony_ci	if (bufw == 0 || bufh == 0) {
262d722e3fbSopenharmony_ci		fprintf(stderr, "error: buffer width/height should be non-zero.\n");
263d722e3fbSopenharmony_ci		ret = -1;
264d722e3fbSopenharmony_ci
265d722e3fbSopenharmony_ci		goto out;
266d722e3fbSopenharmony_ci	}
267d722e3fbSopenharmony_ci
268d722e3fbSopenharmony_ci	fd = drmOpen("exynos", NULL);
269d722e3fbSopenharmony_ci	if (fd < 0) {
270d722e3fbSopenharmony_ci		fprintf(stderr, "error: failed to open drm\n");
271d722e3fbSopenharmony_ci		ret = -1;
272d722e3fbSopenharmony_ci
273d722e3fbSopenharmony_ci		goto out;
274d722e3fbSopenharmony_ci	}
275d722e3fbSopenharmony_ci
276d722e3fbSopenharmony_ci	dev = exynos_device_create(fd);
277d722e3fbSopenharmony_ci	if (dev == NULL) {
278d722e3fbSopenharmony_ci		fprintf(stderr, "error: failed to create device\n");
279d722e3fbSopenharmony_ci		ret = -2;
280d722e3fbSopenharmony_ci
281d722e3fbSopenharmony_ci		goto fail;
282d722e3fbSopenharmony_ci	}
283d722e3fbSopenharmony_ci
284d722e3fbSopenharmony_ci	ctx = g2d_init(fd);
285d722e3fbSopenharmony_ci	if (ctx == NULL) {
286d722e3fbSopenharmony_ci		fprintf(stderr, "error: failed to init G2D\n");
287d722e3fbSopenharmony_ci		ret = -3;
288d722e3fbSopenharmony_ci
289d722e3fbSopenharmony_ci		goto g2d_fail;
290d722e3fbSopenharmony_ci	}
291d722e3fbSopenharmony_ci
292d722e3fbSopenharmony_ci	bo = exynos_bo_create(dev, bufw * bufh * 4, 0);
293d722e3fbSopenharmony_ci	if (bo == NULL) {
294d722e3fbSopenharmony_ci		fprintf(stderr, "error: failed to create bo\n");
295d722e3fbSopenharmony_ci		ret = -4;
296d722e3fbSopenharmony_ci
297d722e3fbSopenharmony_ci		goto bo_fail;
298d722e3fbSopenharmony_ci	}
299d722e3fbSopenharmony_ci
300d722e3fbSopenharmony_ci	/* setup g2d image object */
301d722e3fbSopenharmony_ci	img.width = bufw;
302d722e3fbSopenharmony_ci	img.height = bufh;
303d722e3fbSopenharmony_ci	img.stride = bufw * 4;
304d722e3fbSopenharmony_ci	img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
305d722e3fbSopenharmony_ci	img.buf_type = G2D_IMGBUF_GEM;
306d722e3fbSopenharmony_ci	img.bo[0] = bo->handle;
307d722e3fbSopenharmony_ci
308d722e3fbSopenharmony_ci	event_data.dev = dev;
309d722e3fbSopenharmony_ci	setup_g2d_event_handler(&event_data.evhandler, fd);
310d722e3fbSopenharmony_ci
311d722e3fbSopenharmony_ci	pthread_create(&event_thread, NULL, threadfunc, &event_data);
312d722e3fbSopenharmony_ci
313d722e3fbSopenharmony_ci	ret = g2d_work(ctx, &img, njobs, iters);
314d722e3fbSopenharmony_ci	if (ret != 0)
315d722e3fbSopenharmony_ci		fprintf(stderr, "error: g2d_work failed\n");
316d722e3fbSopenharmony_ci
317d722e3fbSopenharmony_ci	event_data.stop = 1;
318d722e3fbSopenharmony_ci	pthread_join(event_thread, NULL);
319d722e3fbSopenharmony_ci
320d722e3fbSopenharmony_ci	exynos_bo_destroy(bo);
321d722e3fbSopenharmony_ci
322d722e3fbSopenharmony_cibo_fail:
323d722e3fbSopenharmony_ci	g2d_fini(ctx);
324d722e3fbSopenharmony_ci
325d722e3fbSopenharmony_cig2d_fail:
326d722e3fbSopenharmony_ci	exynos_device_destroy(dev);
327d722e3fbSopenharmony_ci
328d722e3fbSopenharmony_cifail:
329d722e3fbSopenharmony_ci	drmClose(fd);
330d722e3fbSopenharmony_ci
331d722e3fbSopenharmony_ciout:
332d722e3fbSopenharmony_ci	return ret;
333d722e3fbSopenharmony_ci}
334