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