Home | History | Annotate | Download | only in exynos
      1 /*
      2  * Copyright (C) 2015 - Tobias Jakobi
      3  *
      4  * This is free software: you can redistribute it and/or modify
      5  * it under the terms of the GNU General Public License as published
      6  * by the Free Software Foundation, either version 2 of the License,
      7  * or (at your option) any later version.
      8  *
      9  * It is distributed in the hope that it will be useful, but
     10  * WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     12  * GNU General Public License for more details.
     13  * You should have received a copy of the GNU General Public License
     14  * along with it. If not, see <http://www.gnu.org/licenses/>.
     15  */
     16 
     17 #include <unistd.h>
     18 #include <poll.h>
     19 
     20 #include <stdlib.h>
     21 #include <stdio.h>
     22 #include <time.h>
     23 #include <getopt.h>
     24 
     25 #include <pthread.h>
     26 
     27 #include <xf86drm.h>
     28 
     29 #include "exynos_drm.h"
     30 #include "exynos_drmif.h"
     31 #include "exynos_fimg2d.h"
     32 
     33 struct g2d_job {
     34 	unsigned int id;
     35 	unsigned int busy;
     36 };
     37 
     38 struct exynos_evhandler {
     39 	struct pollfd fds;
     40 	struct exynos_event_context evctx;
     41 };
     42 
     43 struct threaddata {
     44 	unsigned int stop;
     45 	struct exynos_device *dev;
     46 	struct exynos_evhandler evhandler;
     47 };
     48 
     49 static void g2d_event_handler(int fd, unsigned int cmdlist_no, unsigned int tv_sec,
     50 							unsigned int tv_usec, void *user_data)
     51 {
     52 	struct g2d_job *job = user_data;
     53 
     54 	fprintf(stderr, "info: g2d job (id = %u, cmdlist number = %u) finished!\n",
     55 			job->id, cmdlist_no);
     56 
     57 	job->busy = 0;
     58 }
     59 
     60 static void setup_g2d_event_handler(struct exynos_evhandler *evhandler, int fd)
     61 {
     62 	evhandler->fds.fd = fd;
     63 	evhandler->fds.events = POLLIN;
     64 	evhandler->evctx.base.version = 2;
     65 	evhandler->evctx.version = 1;
     66 	evhandler->evctx.g2d_event_handler = g2d_event_handler;
     67 }
     68 
     69 static void* threadfunc(void *arg) {
     70 	const int timeout = 0;
     71 	struct threaddata *data;
     72 
     73 	data = arg;
     74 
     75 	while (1) {
     76 		if (data->stop) break;
     77 
     78 		usleep(500);
     79 
     80 		data->evhandler.fds.revents = 0;
     81 
     82 		if (poll(&data->evhandler.fds, 1, timeout) < 0)
     83 			continue;
     84 
     85 		if (data->evhandler.fds.revents & (POLLHUP | POLLERR))
     86 			continue;
     87 
     88 		if (data->evhandler.fds.revents & POLLIN)
     89 			exynos_handle_event(data->dev, &data->evhandler.evctx);
     90 	}
     91 
     92 	pthread_exit(0);
     93 }
     94 
     95 /*
     96  * We need to wait until all G2D jobs are finished, otherwise we
     97  * potentially remove a BO which the engine still operates on.
     98  * This results in the following kernel message:
     99  * [drm:exynos_drm_gem_put_dma_addr] *ERROR* failed to lookup gem object.
    100  * Also any subsequent BO allocations fail then with:
    101  * [drm:exynos_drm_alloc_buf] *ERROR* failed to allocate buffer.
    102  */
    103 static void wait_all_jobs(struct g2d_job* jobs, unsigned num_jobs)
    104 {
    105 	unsigned i;
    106 
    107 	for (i = 0; i < num_jobs; ++i) {
    108 		while (jobs[i].busy)
    109 			usleep(500);
    110 	}
    111 
    112 }
    113 
    114 static struct g2d_job* free_job(struct g2d_job* jobs, unsigned num_jobs)
    115 {
    116 	unsigned i;
    117 
    118 	for (i = 0; i < num_jobs; ++i) {
    119 		if (jobs[i].busy == 0)
    120 			return &jobs[i];
    121 	}
    122 
    123 	return NULL;
    124 }
    125 
    126 static int g2d_work(struct g2d_context *ctx, struct g2d_image *img,
    127 					unsigned num_jobs, unsigned iterations)
    128 {
    129 	struct g2d_job *jobs = calloc(num_jobs, sizeof(struct g2d_job));
    130 	int ret;
    131 	unsigned i;
    132 
    133 	/* setup job ids */
    134 	for (i = 0; i < num_jobs; ++i)
    135 		jobs[i].id = i;
    136 
    137 	for (i = 0; i < iterations; ++i) {
    138 		unsigned x, y, w, h;
    139 
    140 		struct g2d_job *j = NULL;
    141 
    142 		while (1) {
    143 			j = free_job(jobs, num_jobs);
    144 
    145 			if (j)
    146 				break;
    147 			else
    148 				usleep(500);
    149 		}
    150 
    151 		x = rand() % img->width;
    152 		y = rand() % img->height;
    153 
    154 		if (x == (img->width - 1))
    155 			x -= 1;
    156 		if (y == (img->height - 1))
    157 			y -= 1;
    158 
    159 		w = rand() % (img->width - x);
    160 		h = rand() % (img->height - y);
    161 
    162 		if (w == 0) w = 1;
    163 		if (h == 0) h = 1;
    164 
    165 		img->color = rand();
    166 
    167 		j->busy = 1;
    168 		g2d_config_event(ctx, j);
    169 
    170 		ret = g2d_solid_fill(ctx, img, x, y, w, h);
    171 
    172 		if (ret == 0)
    173 			g2d_exec(ctx);
    174 
    175 		if (ret != 0) {
    176 			fprintf(stderr, "error: iteration %u (x = %u, x = %u, x = %u, x = %u) failed\n",
    177 					i, x, y, w, h);
    178 			break;
    179 		}
    180 	}
    181 
    182 	wait_all_jobs(jobs, num_jobs);
    183 	free(jobs);
    184 
    185 	return 0;
    186 }
    187 
    188 static void usage(const char *name)
    189 {
    190 	fprintf(stderr, "usage: %s [-ijwh]\n\n", name);
    191 
    192 	fprintf(stderr, "\t-i <number of iterations>\n");
    193 	fprintf(stderr, "\t-j <number of G2D jobs> (default = 4)\n\n");
    194 
    195 	fprintf(stderr, "\t-w <buffer width> (default = 4096)\n");
    196 	fprintf(stderr, "\t-h <buffer height> (default = 4096)\n");
    197 
    198 	exit(0);
    199 }
    200 
    201 int main(int argc, char **argv)
    202 {
    203 	int fd, ret, c, parsefail;
    204 
    205 	pthread_t event_thread;
    206 	struct threaddata event_data = {0};
    207 
    208 	struct exynos_device *dev;
    209 	struct g2d_context *ctx;
    210 	struct exynos_bo *bo;
    211 
    212 	struct g2d_image img = {0};
    213 
    214 	unsigned int iters = 0, njobs = 4;
    215 	unsigned int bufw = 4096, bufh = 4096;
    216 
    217 	ret = 0;
    218 	parsefail = 0;
    219 
    220 	while ((c = getopt(argc, argv, "i:j:w:h:")) != -1) {
    221 		switch (c) {
    222 		case 'i':
    223 			if (sscanf(optarg, "%u", &iters) != 1)
    224 				parsefail = 1;
    225 			break;
    226 		case 'j':
    227 			if (sscanf(optarg, "%u", &njobs) != 1)
    228 				parsefail = 1;
    229 			break;
    230 		case 'w':
    231 			if (sscanf(optarg, "%u", &bufw) != 1)
    232 				parsefail = 1;
    233 			break;
    234 		case 'h':
    235 			if (sscanf(optarg, "%u", &bufh) != 1)
    236 				parsefail = 1;
    237 			break;
    238 		default:
    239 			parsefail = 1;
    240 			break;
    241 		}
    242 	}
    243 
    244 	if (parsefail || (argc == 1) || (iters == 0))
    245 		usage(argv[0]);
    246 
    247 	if (bufw > 4096 || bufh > 4096) {
    248 		fprintf(stderr, "error: buffer width/height should be less than 4096.\n");
    249 		ret = -1;
    250 
    251 		goto out;
    252 	}
    253 
    254 	if (bufw == 0 || bufh == 0) {
    255 		fprintf(stderr, "error: buffer width/height should be non-zero.\n");
    256 		ret = -1;
    257 
    258 		goto out;
    259 	}
    260 
    261 	fd = drmOpen("exynos", NULL);
    262 	if (fd < 0) {
    263 		fprintf(stderr, "error: failed to open drm\n");
    264 		ret = -1;
    265 
    266 		goto out;
    267 	}
    268 
    269 	dev = exynos_device_create(fd);
    270 	if (dev == NULL) {
    271 		fprintf(stderr, "error: failed to create device\n");
    272 		ret = -2;
    273 
    274 		goto fail;
    275 	}
    276 
    277 	ctx = g2d_init(fd);
    278 	if (ctx == NULL) {
    279 		fprintf(stderr, "error: failed to init G2D\n");
    280 		ret = -3;
    281 
    282 		goto g2d_fail;
    283 	}
    284 
    285 	bo = exynos_bo_create(dev, bufw * bufh * 4, 0);
    286 	if (bo == NULL) {
    287 		fprintf(stderr, "error: failed to create bo\n");
    288 		ret = -4;
    289 
    290 		goto bo_fail;
    291 	}
    292 
    293 	/* setup g2d image object */
    294 	img.width = bufw;
    295 	img.height = bufh;
    296 	img.stride = bufw * 4;
    297 	img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
    298 	img.buf_type = G2D_IMGBUF_GEM;
    299 	img.bo[0] = bo->handle;
    300 
    301 	event_data.dev = dev;
    302 	setup_g2d_event_handler(&event_data.evhandler, fd);
    303 
    304 	pthread_create(&event_thread, NULL, threadfunc, &event_data);
    305 
    306 	ret = g2d_work(ctx, &img, njobs, iters);
    307 	if (ret != 0)
    308 		fprintf(stderr, "error: g2d_work failed\n");
    309 
    310 	event_data.stop = 1;
    311 	pthread_join(event_thread, NULL);
    312 
    313 	exynos_bo_destroy(bo);
    314 
    315 bo_fail:
    316 	g2d_fini(ctx);
    317 
    318 g2d_fail:
    319 	exynos_device_destroy(dev);
    320 
    321 fail:
    322 	drmClose(fd);
    323 
    324 out:
    325 	return ret;
    326 }
    327