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