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
g2d_event_handler(int fd,unsigned int cmdlist_no,unsigned int tv_sec,unsigned int tv_usec,void * user_data)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
setup_g2d_event_handler(struct exynos_evhandler * evhandler,int fd)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
threadfunc(void * arg)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 */
wait_all_jobs(struct g2d_job * jobs,unsigned num_jobs)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
free_job(struct g2d_job * jobs,unsigned num_jobs)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
g2d_work(struct g2d_context * ctx,struct g2d_image * img,unsigned num_jobs,unsigned iterations)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
usage(const char * name)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
main(int argc,char ** argv)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