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