1 /* 2 * Copyright (C) 2013 Samsung Electronics Co.Ltd 3 * Authors: 4 * Inki Dae <inki.dae (at) samsung.com> 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License as published by the 8 * Free Software Foundation; either version 2 of the License, or (at your 9 * option) any later version. 10 * 11 */ 12 13 #ifdef HAVE_CONFIG_H 14 #include "config.h" 15 #endif 16 17 #include <stdlib.h> 18 #include <stdio.h> 19 #include <string.h> 20 #include <errno.h> 21 #include <assert.h> 22 23 #include <sys/mman.h> 24 #include <linux/stddef.h> 25 26 #include <xf86drm.h> 27 28 #include "libdrm_macros.h" 29 #include "exynos_drm.h" 30 #include "fimg2d_reg.h" 31 #include "exynos_fimg2d.h" 32 33 #define SET_BF(val, sc, si, scsa, scda, dc, di, dcsa, dcda) \ 34 val.data.src_coeff = sc; \ 35 val.data.inv_src_color_coeff = si; \ 36 val.data.src_coeff_src_a = scsa; \ 37 val.data.src_coeff_dst_a = scda; \ 38 val.data.dst_coeff = dc; \ 39 val.data.inv_dst_color_coeff = di; \ 40 val.data.dst_coeff_src_a = dcsa; \ 41 val.data.dst_coeff_dst_a = dcda; 42 43 #define MIN(a, b) ((a) < (b) ? (a) : (b)) 44 45 #define MSG_PREFIX "exynos/fimg2d: " 46 47 #define G2D_MAX_CMD_NR 64 48 #define G2D_MAX_GEM_CMD_NR 64 49 #define G2D_MAX_CMD_LIST_NR 64 50 51 struct g2d_context { 52 int fd; 53 unsigned int major; 54 unsigned int minor; 55 struct drm_exynos_g2d_cmd cmd[G2D_MAX_CMD_NR]; 56 struct drm_exynos_g2d_cmd cmd_buf[G2D_MAX_GEM_CMD_NR]; 57 unsigned int cmd_nr; 58 unsigned int cmd_buf_nr; 59 unsigned int cmdlist_nr; 60 void *event_userdata; 61 }; 62 63 enum g2d_base_addr_reg { 64 g2d_dst = 0, 65 g2d_src 66 }; 67 68 enum e_g2d_dir_mode { 69 G2D_DIR_MODE_POSITIVE = 0, 70 G2D_DIR_MODE_NEGATIVE = 1 71 }; 72 73 union g2d_direction_val { 74 unsigned int val[2]; 75 struct { 76 /* SRC_MSK_DIRECT_REG [0:1] (source) */ 77 enum e_g2d_dir_mode src_x_direction:1; 78 enum e_g2d_dir_mode src_y_direction:1; 79 80 /* SRC_MSK_DIRECT_REG [2:3] */ 81 unsigned int reversed1:2; 82 83 /* SRC_MSK_DIRECT_REG [4:5] (mask) */ 84 enum e_g2d_dir_mode mask_x_direction:1; 85 enum e_g2d_dir_mode mask_y_direction:1; 86 87 /* SRC_MSK_DIRECT_REG [6:31] */ 88 unsigned int padding1:26; 89 90 /* DST_PAT_DIRECT_REG [0:1] (destination) */ 91 enum e_g2d_dir_mode dst_x_direction:1; 92 enum e_g2d_dir_mode dst_y_direction:1; 93 94 /* DST_PAT_DIRECT_REG [2:3] */ 95 unsigned int reversed2:2; 96 97 /* DST_PAT_DIRECT_REG [4:5] (pattern) */ 98 enum e_g2d_dir_mode pat_x_direction:1; 99 enum e_g2d_dir_mode pat_y_direction:1; 100 101 /* DST_PAT_DIRECT_REG [6:31] */ 102 unsigned int padding2:26; 103 } data; 104 }; 105 106 static unsigned int g2d_get_scaling(unsigned int src, unsigned int dst) 107 { 108 /* 109 * The G2D hw scaling factor is a normalized inverse of the scaling factor. 110 * For example: When source width is 100 and destination width is 200 111 * (scaling of 2x), then the hw factor is NC * 100 / 200. 112 * The normalization factor (NC) is 2^16 = 0x10000. 113 */ 114 115 return ((src << 16) / dst); 116 } 117 118 static unsigned int g2d_get_blend_op(enum e_g2d_op op) 119 { 120 union g2d_blend_func_val val; 121 122 val.val = 0; 123 124 /* 125 * The switch statement is missing the default branch since 126 * we assume that the caller checks the blending operation 127 * via g2d_validate_blending_op() first. 128 */ 129 switch (op) { 130 case G2D_OP_CLEAR: 131 case G2D_OP_DISJOINT_CLEAR: 132 case G2D_OP_CONJOINT_CLEAR: 133 SET_BF(val, G2D_COEFF_MODE_ZERO, 0, 0, 0, G2D_COEFF_MODE_ZERO, 134 0, 0, 0); 135 break; 136 case G2D_OP_SRC: 137 case G2D_OP_DISJOINT_SRC: 138 case G2D_OP_CONJOINT_SRC: 139 SET_BF(val, G2D_COEFF_MODE_ONE, 0, 0, 0, G2D_COEFF_MODE_ZERO, 140 0, 0, 0); 141 break; 142 case G2D_OP_DST: 143 case G2D_OP_DISJOINT_DST: 144 case G2D_OP_CONJOINT_DST: 145 SET_BF(val, G2D_COEFF_MODE_ZERO, 0, 0, 0, G2D_COEFF_MODE_ONE, 146 0, 0, 0); 147 break; 148 case G2D_OP_OVER: 149 SET_BF(val, G2D_COEFF_MODE_ONE, 0, 0, 0, 150 G2D_COEFF_MODE_SRC_ALPHA, 1, 0, 0); 151 break; 152 case G2D_OP_INTERPOLATE: 153 SET_BF(val, G2D_COEFF_MODE_SRC_ALPHA, 0, 0, 0, 154 G2D_COEFF_MODE_SRC_ALPHA, 1, 0, 0); 155 break; 156 } 157 158 return val.val; 159 } 160 161 /* 162 * g2d_check_space - check if command buffers have enough space left. 163 * 164 * @ctx: a pointer to g2d_context structure. 165 * @num_cmds: number of (regular) commands. 166 * @num_gem_cmds: number of GEM commands. 167 */ 168 static unsigned int g2d_check_space(const struct g2d_context *ctx, 169 unsigned int num_cmds, unsigned int num_gem_cmds) 170 { 171 if (ctx->cmd_nr + num_cmds >= G2D_MAX_CMD_NR || 172 ctx->cmd_buf_nr + num_gem_cmds >= G2D_MAX_GEM_CMD_NR) 173 return 1; 174 else 175 return 0; 176 } 177 178 /* 179 * g2d_validate_select_mode - validate select mode. 180 * 181 * @mode: the mode to validate 182 * 183 * Returns zero for an invalid mode and one otherwise. 184 */ 185 static int g2d_validate_select_mode( 186 enum e_g2d_select_mode mode) 187 { 188 switch (mode) { 189 case G2D_SELECT_MODE_NORMAL: 190 case G2D_SELECT_MODE_FGCOLOR: 191 case G2D_SELECT_MODE_BGCOLOR: 192 return 1; 193 } 194 195 return 0; 196 } 197 198 /* 199 * g2d_validate_blending_op - validate blending operation. 200 * 201 * @operation: the operation to validate 202 * 203 * Returns zero for an invalid mode and one otherwise. 204 */ 205 static int g2d_validate_blending_op( 206 enum e_g2d_op operation) 207 { 208 switch (operation) { 209 case G2D_OP_CLEAR: 210 case G2D_OP_SRC: 211 case G2D_OP_DST: 212 case G2D_OP_OVER: 213 case G2D_OP_INTERPOLATE: 214 case G2D_OP_DISJOINT_CLEAR: 215 case G2D_OP_DISJOINT_SRC: 216 case G2D_OP_DISJOINT_DST: 217 case G2D_OP_CONJOINT_CLEAR: 218 case G2D_OP_CONJOINT_SRC: 219 case G2D_OP_CONJOINT_DST: 220 return 1; 221 } 222 223 return 0; 224 } 225 226 /* 227 * g2d_add_cmd - set given command and value to user side command buffer. 228 * 229 * @ctx: a pointer to g2d_context structure. 230 * @cmd: command data. 231 * @value: value data. 232 * 233 * The caller has to make sure that the commands buffers have enough space 234 * left to hold the command. Use g2d_check_space() to ensure this. 235 */ 236 static void g2d_add_cmd(struct g2d_context *ctx, unsigned long cmd, 237 unsigned long value) 238 { 239 switch (cmd & ~(G2D_BUF_USERPTR)) { 240 case SRC_BASE_ADDR_REG: 241 case SRC_PLANE2_BASE_ADDR_REG: 242 case DST_BASE_ADDR_REG: 243 case DST_PLANE2_BASE_ADDR_REG: 244 case PAT_BASE_ADDR_REG: 245 case MASK_BASE_ADDR_REG: 246 assert(ctx->cmd_buf_nr < G2D_MAX_GEM_CMD_NR); 247 248 ctx->cmd_buf[ctx->cmd_buf_nr].offset = cmd; 249 ctx->cmd_buf[ctx->cmd_buf_nr].data = value; 250 ctx->cmd_buf_nr++; 251 break; 252 default: 253 assert(ctx->cmd_nr < G2D_MAX_CMD_NR); 254 255 ctx->cmd[ctx->cmd_nr].offset = cmd; 256 ctx->cmd[ctx->cmd_nr].data = value; 257 ctx->cmd_nr++; 258 break; 259 } 260 } 261 262 /* 263 * g2d_add_base_addr - helper function to set dst/src base address register. 264 * 265 * @ctx: a pointer to g2d_context structure. 266 * @img: a pointer to the dst/src g2d_image structure. 267 * @reg: the register that should be set. 268 */ 269 static void g2d_add_base_addr(struct g2d_context *ctx, struct g2d_image *img, 270 enum g2d_base_addr_reg reg) 271 { 272 const unsigned long cmd = (reg == g2d_dst) ? 273 DST_BASE_ADDR_REG : SRC_BASE_ADDR_REG; 274 275 if (img->buf_type == G2D_IMGBUF_USERPTR) 276 g2d_add_cmd(ctx, cmd | G2D_BUF_USERPTR, 277 (unsigned long)&img->user_ptr[0]); 278 else 279 g2d_add_cmd(ctx, cmd, img->bo[0]); 280 } 281 282 /* 283 * g2d_set_direction - setup direction register (useful for overlapping blits). 284 * 285 * @ctx: a pointer to g2d_context structure. 286 * @dir: a pointer to the g2d_direction_val structure. 287 */ 288 static void g2d_set_direction(struct g2d_context *ctx, 289 const union g2d_direction_val *dir) 290 { 291 g2d_add_cmd(ctx, SRC_MASK_DIRECT_REG, dir->val[0]); 292 g2d_add_cmd(ctx, DST_PAT_DIRECT_REG, dir->val[1]); 293 } 294 295 /* 296 * g2d_reset - reset fimg2d hardware. 297 * 298 * @ctx: a pointer to g2d_context structure. 299 * 300 */ 301 static void g2d_reset(struct g2d_context *ctx) 302 { 303 ctx->cmd_nr = 0; 304 ctx->cmd_buf_nr = 0; 305 306 g2d_add_cmd(ctx, SOFT_RESET_REG, 0x01); 307 } 308 309 /* 310 * g2d_flush - submit all commands and values in user side command buffer 311 * to command queue aware of fimg2d dma. 312 * 313 * @ctx: a pointer to g2d_context structure. 314 * 315 * This function should be called after all commands and values to user 316 * side command buffer are set. It submits that buffer to the kernel side driver. 317 */ 318 static int g2d_flush(struct g2d_context *ctx) 319 { 320 int ret; 321 struct drm_exynos_g2d_set_cmdlist cmdlist = {0}; 322 323 if (ctx->cmd_nr == 0 && ctx->cmd_buf_nr == 0) 324 return 0; 325 326 if (ctx->cmdlist_nr >= G2D_MAX_CMD_LIST_NR) { 327 fprintf(stderr, MSG_PREFIX "command list overflow.\n"); 328 return -EINVAL; 329 } 330 331 cmdlist.cmd = (uint64_t)(uintptr_t)&ctx->cmd[0]; 332 cmdlist.cmd_buf = (uint64_t)(uintptr_t)&ctx->cmd_buf[0]; 333 cmdlist.cmd_nr = ctx->cmd_nr; 334 cmdlist.cmd_buf_nr = ctx->cmd_buf_nr; 335 336 if (ctx->event_userdata) { 337 cmdlist.event_type = G2D_EVENT_NONSTOP; 338 cmdlist.user_data = (uint64_t)(uintptr_t)(ctx->event_userdata); 339 ctx->event_userdata = NULL; 340 } else { 341 cmdlist.event_type = G2D_EVENT_NOT; 342 cmdlist.user_data = 0; 343 } 344 345 ctx->cmd_nr = 0; 346 ctx->cmd_buf_nr = 0; 347 348 ret = drmIoctl(ctx->fd, DRM_IOCTL_EXYNOS_G2D_SET_CMDLIST, &cmdlist); 349 if (ret < 0) { 350 fprintf(stderr, MSG_PREFIX "failed to set cmdlist.\n"); 351 return ret; 352 } 353 354 ctx->cmdlist_nr++; 355 356 return ret; 357 } 358 359 /** 360 * g2d_init - create a new g2d context and get hardware version. 361 * 362 * fd: a file descriptor to an opened drm device. 363 */ 364 struct g2d_context *g2d_init(int fd) 365 { 366 struct drm_exynos_g2d_get_ver ver; 367 struct g2d_context *ctx; 368 int ret; 369 370 ctx = calloc(1, sizeof(*ctx)); 371 if (!ctx) { 372 fprintf(stderr, MSG_PREFIX "failed to allocate context.\n"); 373 return NULL; 374 } 375 376 ctx->fd = fd; 377 378 ret = drmIoctl(fd, DRM_IOCTL_EXYNOS_G2D_GET_VER, &ver); 379 if (ret < 0) { 380 fprintf(stderr, MSG_PREFIX "failed to get version.\n"); 381 free(ctx); 382 return NULL; 383 } 384 385 ctx->major = ver.major; 386 ctx->minor = ver.minor; 387 388 printf(MSG_PREFIX "G2D version (%d.%d).\n", ctx->major, ctx->minor); 389 return ctx; 390 } 391 392 void g2d_fini(struct g2d_context *ctx) 393 { 394 free(ctx); 395 } 396 397 /** 398 * g2d_config_event - setup userdata configuration for a g2d event. 399 * The next invocation of a g2d call (e.g. g2d_solid_fill) is 400 * then going to flag the command buffer as 'nonstop'. 401 * Completion of the command buffer execution can then be 402 * determined by using drmHandleEvent on the DRM fd. 403 * The userdata is 'consumed' in the process. 404 * 405 * @ctx: a pointer to g2d_context structure. 406 * @userdata: a pointer to the user data 407 */ 408 void g2d_config_event(struct g2d_context *ctx, void *userdata) 409 { 410 ctx->event_userdata = userdata; 411 } 412 413 /** 414 * g2d_exec - start the dma to process all commands summited by g2d_flush(). 415 * 416 * @ctx: a pointer to g2d_context structure. 417 */ 418 int g2d_exec(struct g2d_context *ctx) 419 { 420 struct drm_exynos_g2d_exec exec; 421 int ret; 422 423 if (ctx->cmdlist_nr == 0) 424 return -EINVAL; 425 426 exec.async = 0; 427 428 ret = drmIoctl(ctx->fd, DRM_IOCTL_EXYNOS_G2D_EXEC, &exec); 429 if (ret < 0) { 430 fprintf(stderr, MSG_PREFIX "failed to execute.\n"); 431 return ret; 432 } 433 434 ctx->cmdlist_nr = 0; 435 436 return ret; 437 } 438 439 /** 440 * g2d_solid_fill - fill given buffer with given color data. 441 * 442 * @ctx: a pointer to g2d_context structure. 443 * @img: a pointer to g2d_image structure including image and buffer 444 * information. 445 * @x: x start position to buffer filled with given color data. 446 * @y: y start position to buffer filled with given color data. 447 * @w: width value to buffer filled with given color data. 448 * @h: height value to buffer filled with given color data. 449 */ 450 int 451 g2d_solid_fill(struct g2d_context *ctx, struct g2d_image *img, 452 unsigned int x, unsigned int y, unsigned int w, 453 unsigned int h) 454 { 455 union g2d_bitblt_cmd_val bitblt; 456 union g2d_point_val pt; 457 458 if (g2d_check_space(ctx, 7, 1)) 459 return -ENOSPC; 460 461 g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL); 462 g2d_add_cmd(ctx, DST_COLOR_MODE_REG, img->color_mode); 463 g2d_add_base_addr(ctx, img, g2d_dst); 464 g2d_add_cmd(ctx, DST_STRIDE_REG, img->stride); 465 466 if (x + w > img->width) 467 w = img->width - x; 468 if (y + h > img->height) 469 h = img->height - y; 470 471 pt.data.x = x; 472 pt.data.y = y; 473 g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val); 474 475 pt.data.x = x + w; 476 pt.data.y = y + h; 477 g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val); 478 479 g2d_add_cmd(ctx, SF_COLOR_REG, img->color); 480 481 bitblt.val = 0; 482 bitblt.data.fast_solid_color_fill_en = 1; 483 g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val); 484 485 return g2d_flush(ctx); 486 } 487 488 /** 489 * g2d_copy - copy contents in source buffer to destination buffer. 490 * 491 * @ctx: a pointer to g2d_context structure. 492 * @src: a pointer to g2d_image structure including image and buffer 493 * information to source. 494 * @dst: a pointer to g2d_image structure including image and buffer 495 * information to destination. 496 * @src_x: x start position to source buffer. 497 * @src_y: y start position to source buffer. 498 * @dst_x: x start position to destination buffer. 499 * @dst_y: y start position to destination buffer. 500 * @w: width value to source and destination buffers. 501 * @h: height value to source and destination buffers. 502 */ 503 int 504 g2d_copy(struct g2d_context *ctx, struct g2d_image *src, 505 struct g2d_image *dst, unsigned int src_x, unsigned int src_y, 506 unsigned int dst_x, unsigned dst_y, unsigned int w, 507 unsigned int h) 508 { 509 union g2d_rop4_val rop4; 510 union g2d_point_val pt; 511 unsigned int src_w, src_h, dst_w, dst_h; 512 513 src_w = w; 514 src_h = h; 515 if (src_x + src->width > w) 516 src_w = src->width - src_x; 517 if (src_y + src->height > h) 518 src_h = src->height - src_y; 519 520 dst_w = w; 521 dst_h = w; 522 if (dst_x + dst->width > w) 523 dst_w = dst->width - dst_x; 524 if (dst_y + dst->height > h) 525 dst_h = dst->height - dst_y; 526 527 w = MIN(src_w, dst_w); 528 h = MIN(src_h, dst_h); 529 530 if (w <= 0 || h <= 0) { 531 fprintf(stderr, MSG_PREFIX "invalid width or height.\n"); 532 return -EINVAL; 533 } 534 535 if (g2d_check_space(ctx, 11, 2)) 536 return -ENOSPC; 537 538 g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR); 539 g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode); 540 g2d_add_base_addr(ctx, dst, g2d_dst); 541 g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride); 542 543 g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL); 544 g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode); 545 g2d_add_base_addr(ctx, src, g2d_src); 546 g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride); 547 548 pt.data.x = src_x; 549 pt.data.y = src_y; 550 g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val); 551 pt.data.x = src_x + w; 552 pt.data.y = src_y + h; 553 g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val); 554 555 pt.data.x = dst_x; 556 pt.data.y = dst_y; 557 g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val); 558 pt.data.x = dst_x + w; 559 pt.data.y = dst_y + h; 560 g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val); 561 562 rop4.val = 0; 563 rop4.data.unmasked_rop3 = G2D_ROP3_SRC; 564 g2d_add_cmd(ctx, ROP4_REG, rop4.val); 565 566 return g2d_flush(ctx); 567 } 568 569 /** 570 * g2d_move - copy content inside single buffer. 571 * Similar to libc's memmove() this copies a rectangular 572 * region of the provided buffer to another location, while 573 * properly handling the situation where source and 574 * destination rectangle overlap. 575 * 576 * @ctx: a pointer to g2d_context structure. 577 * @img: a pointer to g2d_image structure providing 578 * buffer information. 579 * @src_x: x position of source rectangle. 580 * @src_y: y position of source rectangle. 581 * @dst_x: x position of destination rectangle. 582 * @dst_y: y position of destination rectangle. 583 * @w: width of rectangle to move. 584 * @h: height of rectangle to move. 585 */ 586 int 587 g2d_move(struct g2d_context *ctx, struct g2d_image *img, 588 unsigned int src_x, unsigned int src_y, 589 unsigned int dst_x, unsigned dst_y, unsigned int w, 590 unsigned int h) 591 { 592 union g2d_rop4_val rop4; 593 union g2d_point_val pt; 594 union g2d_direction_val dir; 595 unsigned int src_w, src_h, dst_w, dst_h; 596 597 src_w = w; 598 src_h = h; 599 if (src_x + img->width > w) 600 src_w = img->width - src_x; 601 if (src_y + img->height > h) 602 src_h = img->height - src_y; 603 604 dst_w = w; 605 dst_h = w; 606 if (dst_x + img->width > w) 607 dst_w = img->width - dst_x; 608 if (dst_y + img->height > h) 609 dst_h = img->height - dst_y; 610 611 w = MIN(src_w, dst_w); 612 h = MIN(src_h, dst_h); 613 614 if (w == 0 || h == 0) { 615 fprintf(stderr, MSG_PREFIX "invalid width or height.\n"); 616 return -EINVAL; 617 } 618 619 if (g2d_check_space(ctx, 13, 2)) 620 return -ENOSPC; 621 622 g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR); 623 g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL); 624 625 g2d_add_cmd(ctx, DST_COLOR_MODE_REG, img->color_mode); 626 g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, img->color_mode); 627 628 g2d_add_base_addr(ctx, img, g2d_dst); 629 g2d_add_base_addr(ctx, img, g2d_src); 630 631 g2d_add_cmd(ctx, DST_STRIDE_REG, img->stride); 632 g2d_add_cmd(ctx, SRC_STRIDE_REG, img->stride); 633 634 dir.val[0] = dir.val[1] = 0; 635 636 if (dst_x >= src_x) 637 dir.data.src_x_direction = dir.data.dst_x_direction = 1; 638 if (dst_y >= src_y) 639 dir.data.src_y_direction = dir.data.dst_y_direction = 1; 640 641 g2d_set_direction(ctx, &dir); 642 643 pt.data.x = src_x; 644 pt.data.y = src_y; 645 g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val); 646 pt.data.x = src_x + w; 647 pt.data.y = src_y + h; 648 g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val); 649 650 pt.data.x = dst_x; 651 pt.data.y = dst_y; 652 g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val); 653 pt.data.x = dst_x + w; 654 pt.data.y = dst_y + h; 655 g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val); 656 657 rop4.val = 0; 658 rop4.data.unmasked_rop3 = G2D_ROP3_SRC; 659 g2d_add_cmd(ctx, ROP4_REG, rop4.val); 660 661 return g2d_flush(ctx); 662 } 663 664 /** 665 * g2d_copy_with_scale - copy contents in source buffer to destination buffer 666 * scaling up or down properly. 667 * 668 * @ctx: a pointer to g2d_context structure. 669 * @src: a pointer to g2d_image structure including image and buffer 670 * information to source. 671 * @dst: a pointer to g2d_image structure including image and buffer 672 * information to destination. 673 * @src_x: x start position to source buffer. 674 * @src_y: y start position to source buffer. 675 * @src_w: width value to source buffer. 676 * @src_h: height value to source buffer. 677 * @dst_x: x start position to destination buffer. 678 * @dst_y: y start position to destination buffer. 679 * @dst_w: width value to destination buffer. 680 * @dst_h: height value to destination buffer. 681 * @negative: indicate that it uses color negative to source and 682 * destination buffers. 683 */ 684 int 685 g2d_copy_with_scale(struct g2d_context *ctx, struct g2d_image *src, 686 struct g2d_image *dst, unsigned int src_x, 687 unsigned int src_y, unsigned int src_w, 688 unsigned int src_h, unsigned int dst_x, 689 unsigned int dst_y, unsigned int dst_w, 690 unsigned int dst_h, unsigned int negative) 691 { 692 union g2d_rop4_val rop4; 693 union g2d_point_val pt; 694 unsigned int scale, repeat_pad; 695 unsigned int scale_x, scale_y; 696 697 /* Sanitize this parameter to facilitate space computation below. */ 698 if (negative) 699 negative = 1; 700 701 if (src_w == dst_w && src_h == dst_h) 702 scale = 0; 703 else { 704 scale = 1; 705 scale_x = g2d_get_scaling(src_w, dst_w); 706 scale_y = g2d_get_scaling(src_h, dst_h); 707 } 708 709 repeat_pad = src->repeat_mode == G2D_REPEAT_MODE_PAD ? 1 : 0; 710 711 if (src_x + src_w > src->width) 712 src_w = src->width - src_x; 713 if (src_y + src_h > src->height) 714 src_h = src->height - src_y; 715 716 if (dst_x + dst_w > dst->width) 717 dst_w = dst->width - dst_x; 718 if (dst_y + dst_h > dst->height) 719 dst_h = dst->height - dst_y; 720 721 if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) { 722 fprintf(stderr, MSG_PREFIX "invalid width or height.\n"); 723 return -EINVAL; 724 } 725 726 if (g2d_check_space(ctx, 12 + scale * 3 + negative + repeat_pad, 2)) 727 return -ENOSPC; 728 729 g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR); 730 g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode); 731 g2d_add_base_addr(ctx, dst, g2d_dst); 732 g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride); 733 734 g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL); 735 g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode); 736 737 g2d_add_cmd(ctx, SRC_REPEAT_MODE_REG, src->repeat_mode); 738 if (repeat_pad) 739 g2d_add_cmd(ctx, SRC_PAD_VALUE_REG, dst->color); 740 741 g2d_add_base_addr(ctx, src, g2d_src); 742 g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride); 743 744 rop4.val = 0; 745 rop4.data.unmasked_rop3 = G2D_ROP3_SRC; 746 747 if (negative) { 748 g2d_add_cmd(ctx, BG_COLOR_REG, 0x00FFFFFF); 749 rop4.data.unmasked_rop3 ^= G2D_ROP3_DST; 750 } 751 752 g2d_add_cmd(ctx, ROP4_REG, rop4.val); 753 754 if (scale) { 755 g2d_add_cmd(ctx, SRC_SCALE_CTRL_REG, G2D_SCALE_MODE_BILINEAR); 756 g2d_add_cmd(ctx, SRC_XSCALE_REG, scale_x); 757 g2d_add_cmd(ctx, SRC_YSCALE_REG, scale_y); 758 } 759 760 pt.data.x = src_x; 761 pt.data.y = src_y; 762 g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val); 763 pt.data.x = src_x + src_w; 764 pt.data.y = src_y + src_h; 765 g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val); 766 767 pt.data.x = dst_x; 768 pt.data.y = dst_y; 769 g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val); 770 pt.data.x = dst_x + dst_w; 771 pt.data.y = dst_y + dst_h; 772 g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val); 773 774 return g2d_flush(ctx); 775 } 776 777 /** 778 * g2d_blend - blend image data in source and destination buffers. 779 * 780 * @ctx: a pointer to g2d_context structure. 781 * @src: a pointer to g2d_image structure including image and buffer 782 * information to source. 783 * @dst: a pointer to g2d_image structure including image and buffer 784 * information to destination. 785 * @src_x: x start position to source buffer. 786 * @src_y: y start position to source buffer. 787 * @dst_x: x start position to destination buffer. 788 * @dst_y: y start position to destination buffer. 789 * @w: width value to source and destination buffer. 790 * @h: height value to source and destination buffer. 791 * @op: blend operation type. 792 */ 793 int 794 g2d_blend(struct g2d_context *ctx, struct g2d_image *src, 795 struct g2d_image *dst, unsigned int src_x, 796 unsigned int src_y, unsigned int dst_x, unsigned int dst_y, 797 unsigned int w, unsigned int h, enum e_g2d_op op) 798 { 799 union g2d_point_val pt; 800 union g2d_bitblt_cmd_val bitblt; 801 union g2d_blend_func_val blend; 802 unsigned int gem_space; 803 unsigned int src_w, src_h, dst_w, dst_h; 804 805 src_w = w; 806 src_h = h; 807 if (src_x + w > src->width) 808 src_w = src->width - src_x; 809 if (src_y + h > src->height) 810 src_h = src->height - src_y; 811 812 dst_w = w; 813 dst_h = h; 814 if (dst_x + w > dst->width) 815 dst_w = dst->width - dst_x; 816 if (dst_y + h > dst->height) 817 dst_h = dst->height - dst_y; 818 819 w = MIN(src_w, dst_w); 820 h = MIN(src_h, dst_h); 821 822 if (w <= 0 || h <= 0) { 823 fprintf(stderr, MSG_PREFIX "invalid width or height.\n"); 824 return -EINVAL; 825 } 826 827 if (!g2d_validate_select_mode(src->select_mode)) { 828 fprintf(stderr , MSG_PREFIX "invalid select mode for source.\n"); 829 return -EINVAL; 830 } 831 832 if (!g2d_validate_blending_op(op)) { 833 fprintf(stderr , MSG_PREFIX "unsupported blending operation.\n"); 834 return -EINVAL; 835 } 836 837 gem_space = src->select_mode == G2D_SELECT_MODE_NORMAL ? 2 : 1; 838 839 if (g2d_check_space(ctx, 12, gem_space)) 840 return -ENOSPC; 841 842 bitblt.val = 0; 843 blend.val = 0; 844 845 if (op == G2D_OP_SRC || op == G2D_OP_CLEAR) 846 g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR); 847 else 848 g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL); 849 850 g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode); 851 g2d_add_base_addr(ctx, dst, g2d_dst); 852 g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride); 853 854 g2d_add_cmd(ctx, SRC_SELECT_REG, src->select_mode); 855 g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode); 856 857 switch (src->select_mode) { 858 case G2D_SELECT_MODE_NORMAL: 859 g2d_add_base_addr(ctx, src, g2d_src); 860 g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride); 861 break; 862 case G2D_SELECT_MODE_FGCOLOR: 863 g2d_add_cmd(ctx, FG_COLOR_REG, src->color); 864 break; 865 case G2D_SELECT_MODE_BGCOLOR: 866 g2d_add_cmd(ctx, BG_COLOR_REG, src->color); 867 break; 868 } 869 870 bitblt.data.alpha_blend_mode = G2D_ALPHA_BLEND_MODE_ENABLE; 871 blend.val = g2d_get_blend_op(op); 872 g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val); 873 g2d_add_cmd(ctx, BLEND_FUNCTION_REG, blend.val); 874 875 pt.data.x = src_x; 876 pt.data.y = src_y; 877 g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val); 878 pt.data.x = src_x + w; 879 pt.data.y = src_y + h; 880 g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val); 881 882 pt.data.x = dst_x; 883 pt.data.y = dst_y; 884 g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val); 885 pt.data.x = dst_x + w; 886 pt.data.y = dst_y + h; 887 g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val); 888 889 return g2d_flush(ctx); 890 } 891 892 /** 893 * g2d_scale_and_blend - apply scaling to source buffer and then blend to destination buffer 894 * 895 * @ctx: a pointer to g2d_context structure. 896 * @src: a pointer to g2d_image structure including image and buffer 897 * information to source. 898 * @dst: a pointer to g2d_image structure including image and buffer 899 * information to destination. 900 * @src_x: x start position to source buffer. 901 * @src_y: y start position to source buffer. 902 * @src_w: width value to source buffer. 903 * @src_h: height value to source buffer. 904 * @dst_x: x start position to destination buffer. 905 * @dst_y: y start position to destination buffer. 906 * @dst_w: width value to destination buffer. 907 * @dst_h: height value to destination buffer. 908 * @op: blend operation type. 909 */ 910 int 911 g2d_scale_and_blend(struct g2d_context *ctx, struct g2d_image *src, 912 struct g2d_image *dst, unsigned int src_x, unsigned int src_y, 913 unsigned int src_w, unsigned int src_h, unsigned int dst_x, 914 unsigned int dst_y, unsigned int dst_w, unsigned int dst_h, 915 enum e_g2d_op op) 916 { 917 union g2d_point_val pt; 918 union g2d_bitblt_cmd_val bitblt; 919 union g2d_blend_func_val blend; 920 unsigned int scale, gem_space; 921 unsigned int scale_x, scale_y; 922 923 if (src_w == dst_w && src_h == dst_h) 924 scale = 0; 925 else { 926 scale = 1; 927 scale_x = g2d_get_scaling(src_w, dst_w); 928 scale_y = g2d_get_scaling(src_h, dst_h); 929 } 930 931 if (src_x + src_w > src->width) 932 src_w = src->width - src_x; 933 if (src_y + src_h > src->height) 934 src_h = src->height - src_y; 935 936 if (dst_x + dst_w > dst->width) 937 dst_w = dst->width - dst_x; 938 if (dst_y + dst_h > dst->height) 939 dst_h = dst->height - dst_y; 940 941 if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) { 942 fprintf(stderr, MSG_PREFIX "invalid width or height.\n"); 943 return -EINVAL; 944 } 945 946 if (!g2d_validate_select_mode(src->select_mode)) { 947 fprintf(stderr , MSG_PREFIX "invalid select mode for source.\n"); 948 return -EINVAL; 949 } 950 951 if (!g2d_validate_blending_op(op)) { 952 fprintf(stderr , MSG_PREFIX "unsupported blending operation.\n"); 953 return -EINVAL; 954 } 955 956 gem_space = src->select_mode == G2D_SELECT_MODE_NORMAL ? 2 : 1; 957 958 if (g2d_check_space(ctx, 12 + scale * 3, gem_space)) 959 return -ENOSPC; 960 961 bitblt.val = 0; 962 blend.val = 0; 963 964 if (op == G2D_OP_SRC || op == G2D_OP_CLEAR) 965 g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR); 966 else 967 g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL); 968 969 g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode); 970 g2d_add_base_addr(ctx, dst, g2d_dst); 971 g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride); 972 973 g2d_add_cmd(ctx, SRC_SELECT_REG, src->select_mode); 974 g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode); 975 976 switch (src->select_mode) { 977 case G2D_SELECT_MODE_NORMAL: 978 g2d_add_base_addr(ctx, src, g2d_src); 979 g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride); 980 break; 981 case G2D_SELECT_MODE_FGCOLOR: 982 g2d_add_cmd(ctx, FG_COLOR_REG, src->color); 983 break; 984 case G2D_SELECT_MODE_BGCOLOR: 985 g2d_add_cmd(ctx, BG_COLOR_REG, src->color); 986 break; 987 } 988 989 if (scale) { 990 g2d_add_cmd(ctx, SRC_SCALE_CTRL_REG, G2D_SCALE_MODE_BILINEAR); 991 g2d_add_cmd(ctx, SRC_XSCALE_REG, scale_x); 992 g2d_add_cmd(ctx, SRC_YSCALE_REG, scale_y); 993 } 994 995 bitblt.data.alpha_blend_mode = G2D_ALPHA_BLEND_MODE_ENABLE; 996 blend.val = g2d_get_blend_op(op); 997 g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val); 998 g2d_add_cmd(ctx, BLEND_FUNCTION_REG, blend.val); 999 1000 pt.data.x = src_x; 1001 pt.data.y = src_y; 1002 g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val); 1003 pt.data.x = src_x + src_w; 1004 pt.data.y = src_y + src_h; 1005 g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val); 1006 1007 pt.data.x = dst_x; 1008 pt.data.y = dst_y; 1009 g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val); 1010 pt.data.x = dst_x + dst_w; 1011 pt.data.y = dst_y + dst_h; 1012 g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val); 1013 1014 return g2d_flush(ctx); 1015 } 1016