exynos_fimg2d.c revision d8807b2f
1/* 2 * Copyright (C) 2013 Samsung Electronics Co.Ltd 3 * Authors: 4 * Inki Dae <inki.dae@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 51struct 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 63enum g2d_base_addr_reg { 64 g2d_dst = 0, 65 g2d_src 66}; 67 68enum e_g2d_dir_mode { 69 G2D_DIR_MODE_POSITIVE = 0, 70 G2D_DIR_MODE_NEGATIVE = 1 71}; 72 73union 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 106static 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 118static 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 */ 168static 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 */ 185static 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 */ 205static 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 */ 236static 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 */ 269static 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 */ 288static 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_flush - submit all commands and values in user side command buffer 297 * to command queue aware of fimg2d dma. 298 * 299 * @ctx: a pointer to g2d_context structure. 300 * 301 * This function should be called after all commands and values to user 302 * side command buffer are set. It submits that buffer to the kernel side driver. 303 */ 304static int g2d_flush(struct g2d_context *ctx) 305{ 306 int ret; 307 struct drm_exynos_g2d_set_cmdlist cmdlist = {0}; 308 309 if (ctx->cmd_nr == 0 && ctx->cmd_buf_nr == 0) 310 return 0; 311 312 if (ctx->cmdlist_nr >= G2D_MAX_CMD_LIST_NR) { 313 fprintf(stderr, MSG_PREFIX "command list overflow.\n"); 314 return -EINVAL; 315 } 316 317 cmdlist.cmd = (uint64_t)(uintptr_t)&ctx->cmd[0]; 318 cmdlist.cmd_buf = (uint64_t)(uintptr_t)&ctx->cmd_buf[0]; 319 cmdlist.cmd_nr = ctx->cmd_nr; 320 cmdlist.cmd_buf_nr = ctx->cmd_buf_nr; 321 322 if (ctx->event_userdata) { 323 cmdlist.event_type = G2D_EVENT_NONSTOP; 324 cmdlist.user_data = (uint64_t)(uintptr_t)(ctx->event_userdata); 325 ctx->event_userdata = NULL; 326 } else { 327 cmdlist.event_type = G2D_EVENT_NOT; 328 cmdlist.user_data = 0; 329 } 330 331 ctx->cmd_nr = 0; 332 ctx->cmd_buf_nr = 0; 333 334 ret = drmIoctl(ctx->fd, DRM_IOCTL_EXYNOS_G2D_SET_CMDLIST, &cmdlist); 335 if (ret < 0) { 336 fprintf(stderr, MSG_PREFIX "failed to set cmdlist.\n"); 337 return ret; 338 } 339 340 ctx->cmdlist_nr++; 341 342 return ret; 343} 344 345/** 346 * g2d_init - create a new g2d context and get hardware version. 347 * 348 * fd: a file descriptor to an opened drm device. 349 */ 350struct g2d_context *g2d_init(int fd) 351{ 352 struct drm_exynos_g2d_get_ver ver; 353 struct g2d_context *ctx; 354 int ret; 355 356 ctx = calloc(1, sizeof(*ctx)); 357 if (!ctx) { 358 fprintf(stderr, MSG_PREFIX "failed to allocate context.\n"); 359 return NULL; 360 } 361 362 ctx->fd = fd; 363 364 ret = drmIoctl(fd, DRM_IOCTL_EXYNOS_G2D_GET_VER, &ver); 365 if (ret < 0) { 366 fprintf(stderr, MSG_PREFIX "failed to get version.\n"); 367 free(ctx); 368 return NULL; 369 } 370 371 ctx->major = ver.major; 372 ctx->minor = ver.minor; 373 374 printf(MSG_PREFIX "G2D version (%d.%d).\n", ctx->major, ctx->minor); 375 return ctx; 376} 377 378void g2d_fini(struct g2d_context *ctx) 379{ 380 free(ctx); 381} 382 383/** 384 * g2d_config_event - setup userdata configuration for a g2d event. 385 * The next invocation of a g2d call (e.g. g2d_solid_fill) is 386 * then going to flag the command buffer as 'nonstop'. 387 * Completion of the command buffer execution can then be 388 * determined by using drmHandleEvent on the DRM fd. 389 * The userdata is 'consumed' in the process. 390 * 391 * @ctx: a pointer to g2d_context structure. 392 * @userdata: a pointer to the user data 393 */ 394void g2d_config_event(struct g2d_context *ctx, void *userdata) 395{ 396 ctx->event_userdata = userdata; 397} 398 399/** 400 * g2d_exec - start the dma to process all commands summited by g2d_flush(). 401 * 402 * @ctx: a pointer to g2d_context structure. 403 */ 404int g2d_exec(struct g2d_context *ctx) 405{ 406 struct drm_exynos_g2d_exec exec; 407 int ret; 408 409 if (ctx->cmdlist_nr == 0) 410 return -EINVAL; 411 412 exec.async = 0; 413 414 ret = drmIoctl(ctx->fd, DRM_IOCTL_EXYNOS_G2D_EXEC, &exec); 415 if (ret < 0) { 416 fprintf(stderr, MSG_PREFIX "failed to execute.\n"); 417 return ret; 418 } 419 420 ctx->cmdlist_nr = 0; 421 422 return ret; 423} 424 425/** 426 * g2d_solid_fill - fill given buffer with given color data. 427 * 428 * @ctx: a pointer to g2d_context structure. 429 * @img: a pointer to g2d_image structure including image and buffer 430 * information. 431 * @x: x start position to buffer filled with given color data. 432 * @y: y start position to buffer filled with given color data. 433 * @w: width value to buffer filled with given color data. 434 * @h: height value to buffer filled with given color data. 435 */ 436int 437g2d_solid_fill(struct g2d_context *ctx, struct g2d_image *img, 438 unsigned int x, unsigned int y, unsigned int w, 439 unsigned int h) 440{ 441 union g2d_bitblt_cmd_val bitblt; 442 union g2d_point_val pt; 443 444 if (g2d_check_space(ctx, 7, 1)) 445 return -ENOSPC; 446 447 g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL); 448 g2d_add_cmd(ctx, DST_COLOR_MODE_REG, img->color_mode); 449 g2d_add_base_addr(ctx, img, g2d_dst); 450 g2d_add_cmd(ctx, DST_STRIDE_REG, img->stride); 451 452 if (x + w > img->width) 453 w = img->width - x; 454 if (y + h > img->height) 455 h = img->height - y; 456 457 pt.data.x = x; 458 pt.data.y = y; 459 g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val); 460 461 pt.data.x = x + w; 462 pt.data.y = y + h; 463 g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val); 464 465 g2d_add_cmd(ctx, SF_COLOR_REG, img->color); 466 467 bitblt.val = 0; 468 bitblt.data.fast_solid_color_fill_en = 1; 469 g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val); 470 471 return g2d_flush(ctx); 472} 473 474/** 475 * g2d_copy - copy contents in source buffer to destination buffer. 476 * 477 * @ctx: a pointer to g2d_context structure. 478 * @src: a pointer to g2d_image structure including image and buffer 479 * information to source. 480 * @dst: a pointer to g2d_image structure including image and buffer 481 * information to destination. 482 * @src_x: x start position to source buffer. 483 * @src_y: y start position to source buffer. 484 * @dst_x: x start position to destination buffer. 485 * @dst_y: y start position to destination buffer. 486 * @w: width value to source and destination buffers. 487 * @h: height value to source and destination buffers. 488 */ 489int 490g2d_copy(struct g2d_context *ctx, struct g2d_image *src, 491 struct g2d_image *dst, unsigned int src_x, unsigned int src_y, 492 unsigned int dst_x, unsigned dst_y, unsigned int w, 493 unsigned int h) 494{ 495 union g2d_rop4_val rop4; 496 union g2d_point_val pt; 497 unsigned int src_w, src_h, dst_w, dst_h; 498 499 src_w = w; 500 src_h = h; 501 if (src_x + src->width > w) 502 src_w = src->width - src_x; 503 if (src_y + src->height > h) 504 src_h = src->height - src_y; 505 506 dst_w = w; 507 dst_h = w; 508 if (dst_x + dst->width > w) 509 dst_w = dst->width - dst_x; 510 if (dst_y + dst->height > h) 511 dst_h = dst->height - dst_y; 512 513 w = MIN(src_w, dst_w); 514 h = MIN(src_h, dst_h); 515 516 if (w <= 0 || h <= 0) { 517 fprintf(stderr, MSG_PREFIX "invalid width or height.\n"); 518 return -EINVAL; 519 } 520 521 if (g2d_check_space(ctx, 11, 2)) 522 return -ENOSPC; 523 524 g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR); 525 g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode); 526 g2d_add_base_addr(ctx, dst, g2d_dst); 527 g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride); 528 529 g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL); 530 g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode); 531 g2d_add_base_addr(ctx, src, g2d_src); 532 g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride); 533 534 pt.data.x = src_x; 535 pt.data.y = src_y; 536 g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val); 537 pt.data.x = src_x + w; 538 pt.data.y = src_y + h; 539 g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val); 540 541 pt.data.x = dst_x; 542 pt.data.y = dst_y; 543 g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val); 544 pt.data.x = dst_x + w; 545 pt.data.y = dst_y + h; 546 g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val); 547 548 rop4.val = 0; 549 rop4.data.unmasked_rop3 = G2D_ROP3_SRC; 550 g2d_add_cmd(ctx, ROP4_REG, rop4.val); 551 552 return g2d_flush(ctx); 553} 554 555/** 556 * g2d_move - copy content inside single buffer. 557 * Similar to libc's memmove() this copies a rectangular 558 * region of the provided buffer to another location, while 559 * properly handling the situation where source and 560 * destination rectangle overlap. 561 * 562 * @ctx: a pointer to g2d_context structure. 563 * @img: a pointer to g2d_image structure providing 564 * buffer information. 565 * @src_x: x position of source rectangle. 566 * @src_y: y position of source rectangle. 567 * @dst_x: x position of destination rectangle. 568 * @dst_y: y position of destination rectangle. 569 * @w: width of rectangle to move. 570 * @h: height of rectangle to move. 571 */ 572int 573g2d_move(struct g2d_context *ctx, struct g2d_image *img, 574 unsigned int src_x, unsigned int src_y, 575 unsigned int dst_x, unsigned dst_y, unsigned int w, 576 unsigned int h) 577{ 578 union g2d_rop4_val rop4; 579 union g2d_point_val pt; 580 union g2d_direction_val dir; 581 unsigned int src_w, src_h, dst_w, dst_h; 582 583 src_w = w; 584 src_h = h; 585 if (src_x + img->width > w) 586 src_w = img->width - src_x; 587 if (src_y + img->height > h) 588 src_h = img->height - src_y; 589 590 dst_w = w; 591 dst_h = w; 592 if (dst_x + img->width > w) 593 dst_w = img->width - dst_x; 594 if (dst_y + img->height > h) 595 dst_h = img->height - dst_y; 596 597 w = MIN(src_w, dst_w); 598 h = MIN(src_h, dst_h); 599 600 if (w == 0 || h == 0) { 601 fprintf(stderr, MSG_PREFIX "invalid width or height.\n"); 602 return -EINVAL; 603 } 604 605 if (g2d_check_space(ctx, 13, 2)) 606 return -ENOSPC; 607 608 g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR); 609 g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL); 610 611 g2d_add_cmd(ctx, DST_COLOR_MODE_REG, img->color_mode); 612 g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, img->color_mode); 613 614 g2d_add_base_addr(ctx, img, g2d_dst); 615 g2d_add_base_addr(ctx, img, g2d_src); 616 617 g2d_add_cmd(ctx, DST_STRIDE_REG, img->stride); 618 g2d_add_cmd(ctx, SRC_STRIDE_REG, img->stride); 619 620 dir.val[0] = dir.val[1] = 0; 621 622 if (dst_x >= src_x) 623 dir.data.src_x_direction = dir.data.dst_x_direction = 1; 624 if (dst_y >= src_y) 625 dir.data.src_y_direction = dir.data.dst_y_direction = 1; 626 627 g2d_set_direction(ctx, &dir); 628 629 pt.data.x = src_x; 630 pt.data.y = src_y; 631 g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val); 632 pt.data.x = src_x + w; 633 pt.data.y = src_y + h; 634 g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val); 635 636 pt.data.x = dst_x; 637 pt.data.y = dst_y; 638 g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val); 639 pt.data.x = dst_x + w; 640 pt.data.y = dst_y + h; 641 g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val); 642 643 rop4.val = 0; 644 rop4.data.unmasked_rop3 = G2D_ROP3_SRC; 645 g2d_add_cmd(ctx, ROP4_REG, rop4.val); 646 647 return g2d_flush(ctx); 648} 649 650/** 651 * g2d_copy_with_scale - copy contents in source buffer to destination buffer 652 * scaling up or down properly. 653 * 654 * @ctx: a pointer to g2d_context structure. 655 * @src: a pointer to g2d_image structure including image and buffer 656 * information to source. 657 * @dst: a pointer to g2d_image structure including image and buffer 658 * information to destination. 659 * @src_x: x start position to source buffer. 660 * @src_y: y start position to source buffer. 661 * @src_w: width value to source buffer. 662 * @src_h: height value to source buffer. 663 * @dst_x: x start position to destination buffer. 664 * @dst_y: y start position to destination buffer. 665 * @dst_w: width value to destination buffer. 666 * @dst_h: height value to destination buffer. 667 * @negative: indicate that it uses color negative to source and 668 * destination buffers. 669 */ 670int 671g2d_copy_with_scale(struct g2d_context *ctx, struct g2d_image *src, 672 struct g2d_image *dst, unsigned int src_x, 673 unsigned int src_y, unsigned int src_w, 674 unsigned int src_h, unsigned int dst_x, 675 unsigned int dst_y, unsigned int dst_w, 676 unsigned int dst_h, unsigned int negative) 677{ 678 union g2d_rop4_val rop4; 679 union g2d_point_val pt; 680 unsigned int scale, repeat_pad; 681 unsigned int scale_x, scale_y; 682 683 /* Sanitize this parameter to facilitate space computation below. */ 684 if (negative) 685 negative = 1; 686 687 if (src_w == dst_w && src_h == dst_h) 688 scale = 0; 689 else { 690 scale = 1; 691 scale_x = g2d_get_scaling(src_w, dst_w); 692 scale_y = g2d_get_scaling(src_h, dst_h); 693 } 694 695 repeat_pad = src->repeat_mode == G2D_REPEAT_MODE_PAD ? 1 : 0; 696 697 if (src_x + src_w > src->width) 698 src_w = src->width - src_x; 699 if (src_y + src_h > src->height) 700 src_h = src->height - src_y; 701 702 if (dst_x + dst_w > dst->width) 703 dst_w = dst->width - dst_x; 704 if (dst_y + dst_h > dst->height) 705 dst_h = dst->height - dst_y; 706 707 if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) { 708 fprintf(stderr, MSG_PREFIX "invalid width or height.\n"); 709 return -EINVAL; 710 } 711 712 if (g2d_check_space(ctx, 12 + scale * 3 + negative + repeat_pad, 2)) 713 return -ENOSPC; 714 715 g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR); 716 g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode); 717 g2d_add_base_addr(ctx, dst, g2d_dst); 718 g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride); 719 720 g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL); 721 g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode); 722 723 g2d_add_cmd(ctx, SRC_REPEAT_MODE_REG, src->repeat_mode); 724 if (repeat_pad) 725 g2d_add_cmd(ctx, SRC_PAD_VALUE_REG, dst->color); 726 727 g2d_add_base_addr(ctx, src, g2d_src); 728 g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride); 729 730 rop4.val = 0; 731 rop4.data.unmasked_rop3 = G2D_ROP3_SRC; 732 733 if (negative) { 734 g2d_add_cmd(ctx, BG_COLOR_REG, 0x00FFFFFF); 735 rop4.data.unmasked_rop3 ^= G2D_ROP3_DST; 736 } 737 738 g2d_add_cmd(ctx, ROP4_REG, rop4.val); 739 740 if (scale) { 741 g2d_add_cmd(ctx, SRC_SCALE_CTRL_REG, G2D_SCALE_MODE_BILINEAR); 742 g2d_add_cmd(ctx, SRC_XSCALE_REG, scale_x); 743 g2d_add_cmd(ctx, SRC_YSCALE_REG, scale_y); 744 } 745 746 pt.data.x = src_x; 747 pt.data.y = src_y; 748 g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val); 749 pt.data.x = src_x + src_w; 750 pt.data.y = src_y + src_h; 751 g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val); 752 753 pt.data.x = dst_x; 754 pt.data.y = dst_y; 755 g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val); 756 pt.data.x = dst_x + dst_w; 757 pt.data.y = dst_y + dst_h; 758 g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val); 759 760 return g2d_flush(ctx); 761} 762 763/** 764 * g2d_blend - blend image data in source and destination buffers. 765 * 766 * @ctx: a pointer to g2d_context structure. 767 * @src: a pointer to g2d_image structure including image and buffer 768 * information to source. 769 * @dst: a pointer to g2d_image structure including image and buffer 770 * information to destination. 771 * @src_x: x start position to source buffer. 772 * @src_y: y start position to source buffer. 773 * @dst_x: x start position to destination buffer. 774 * @dst_y: y start position to destination buffer. 775 * @w: width value to source and destination buffer. 776 * @h: height value to source and destination buffer. 777 * @op: blend operation type. 778 */ 779int 780g2d_blend(struct g2d_context *ctx, struct g2d_image *src, 781 struct g2d_image *dst, unsigned int src_x, 782 unsigned int src_y, unsigned int dst_x, unsigned int dst_y, 783 unsigned int w, unsigned int h, enum e_g2d_op op) 784{ 785 union g2d_point_val pt; 786 union g2d_bitblt_cmd_val bitblt; 787 union g2d_blend_func_val blend; 788 unsigned int gem_space; 789 unsigned int src_w, src_h, dst_w, dst_h; 790 791 src_w = w; 792 src_h = h; 793 if (src_x + w > src->width) 794 src_w = src->width - src_x; 795 if (src_y + h > src->height) 796 src_h = src->height - src_y; 797 798 dst_w = w; 799 dst_h = h; 800 if (dst_x + w > dst->width) 801 dst_w = dst->width - dst_x; 802 if (dst_y + h > dst->height) 803 dst_h = dst->height - dst_y; 804 805 w = MIN(src_w, dst_w); 806 h = MIN(src_h, dst_h); 807 808 if (w <= 0 || h <= 0) { 809 fprintf(stderr, MSG_PREFIX "invalid width or height.\n"); 810 return -EINVAL; 811 } 812 813 if (!g2d_validate_select_mode(src->select_mode)) { 814 fprintf(stderr , MSG_PREFIX "invalid select mode for source.\n"); 815 return -EINVAL; 816 } 817 818 if (!g2d_validate_blending_op(op)) { 819 fprintf(stderr , MSG_PREFIX "unsupported blending operation.\n"); 820 return -EINVAL; 821 } 822 823 gem_space = src->select_mode == G2D_SELECT_MODE_NORMAL ? 2 : 1; 824 825 if (g2d_check_space(ctx, 12, gem_space)) 826 return -ENOSPC; 827 828 bitblt.val = 0; 829 blend.val = 0; 830 831 if (op == G2D_OP_SRC || op == G2D_OP_CLEAR) 832 g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR); 833 else 834 g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL); 835 836 g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode); 837 g2d_add_base_addr(ctx, dst, g2d_dst); 838 g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride); 839 840 g2d_add_cmd(ctx, SRC_SELECT_REG, src->select_mode); 841 g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode); 842 843 switch (src->select_mode) { 844 case G2D_SELECT_MODE_NORMAL: 845 g2d_add_base_addr(ctx, src, g2d_src); 846 g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride); 847 break; 848 case G2D_SELECT_MODE_FGCOLOR: 849 g2d_add_cmd(ctx, FG_COLOR_REG, src->color); 850 break; 851 case G2D_SELECT_MODE_BGCOLOR: 852 g2d_add_cmd(ctx, BG_COLOR_REG, src->color); 853 break; 854 } 855 856 bitblt.data.alpha_blend_mode = G2D_ALPHA_BLEND_MODE_ENABLE; 857 blend.val = g2d_get_blend_op(op); 858 g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val); 859 g2d_add_cmd(ctx, BLEND_FUNCTION_REG, blend.val); 860 861 pt.data.x = src_x; 862 pt.data.y = src_y; 863 g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val); 864 pt.data.x = src_x + w; 865 pt.data.y = src_y + h; 866 g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val); 867 868 pt.data.x = dst_x; 869 pt.data.y = dst_y; 870 g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val); 871 pt.data.x = dst_x + w; 872 pt.data.y = dst_y + h; 873 g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val); 874 875 return g2d_flush(ctx); 876} 877 878/** 879 * g2d_scale_and_blend - apply scaling to source buffer and then blend to destination buffer 880 * 881 * @ctx: a pointer to g2d_context structure. 882 * @src: a pointer to g2d_image structure including image and buffer 883 * information to source. 884 * @dst: a pointer to g2d_image structure including image and buffer 885 * information to destination. 886 * @src_x: x start position to source buffer. 887 * @src_y: y start position to source buffer. 888 * @src_w: width value to source buffer. 889 * @src_h: height value to source buffer. 890 * @dst_x: x start position to destination buffer. 891 * @dst_y: y start position to destination buffer. 892 * @dst_w: width value to destination buffer. 893 * @dst_h: height value to destination buffer. 894 * @op: blend operation type. 895 */ 896int 897g2d_scale_and_blend(struct g2d_context *ctx, struct g2d_image *src, 898 struct g2d_image *dst, unsigned int src_x, unsigned int src_y, 899 unsigned int src_w, unsigned int src_h, unsigned int dst_x, 900 unsigned int dst_y, unsigned int dst_w, unsigned int dst_h, 901 enum e_g2d_op op) 902{ 903 union g2d_point_val pt; 904 union g2d_bitblt_cmd_val bitblt; 905 union g2d_blend_func_val blend; 906 unsigned int scale, gem_space; 907 unsigned int scale_x, scale_y; 908 909 if (src_w == dst_w && src_h == dst_h) 910 scale = 0; 911 else { 912 scale = 1; 913 scale_x = g2d_get_scaling(src_w, dst_w); 914 scale_y = g2d_get_scaling(src_h, dst_h); 915 } 916 917 if (src_x + src_w > src->width) 918 src_w = src->width - src_x; 919 if (src_y + src_h > src->height) 920 src_h = src->height - src_y; 921 922 if (dst_x + dst_w > dst->width) 923 dst_w = dst->width - dst_x; 924 if (dst_y + dst_h > dst->height) 925 dst_h = dst->height - dst_y; 926 927 if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) { 928 fprintf(stderr, MSG_PREFIX "invalid width or height.\n"); 929 return -EINVAL; 930 } 931 932 if (!g2d_validate_select_mode(src->select_mode)) { 933 fprintf(stderr , MSG_PREFIX "invalid select mode for source.\n"); 934 return -EINVAL; 935 } 936 937 if (!g2d_validate_blending_op(op)) { 938 fprintf(stderr , MSG_PREFIX "unsupported blending operation.\n"); 939 return -EINVAL; 940 } 941 942 gem_space = src->select_mode == G2D_SELECT_MODE_NORMAL ? 2 : 1; 943 944 if (g2d_check_space(ctx, 12 + scale * 3, gem_space)) 945 return -ENOSPC; 946 947 bitblt.val = 0; 948 blend.val = 0; 949 950 if (op == G2D_OP_SRC || op == G2D_OP_CLEAR) 951 g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR); 952 else 953 g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL); 954 955 g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode); 956 g2d_add_base_addr(ctx, dst, g2d_dst); 957 g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride); 958 959 g2d_add_cmd(ctx, SRC_SELECT_REG, src->select_mode); 960 g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode); 961 962 switch (src->select_mode) { 963 case G2D_SELECT_MODE_NORMAL: 964 g2d_add_base_addr(ctx, src, g2d_src); 965 g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride); 966 break; 967 case G2D_SELECT_MODE_FGCOLOR: 968 g2d_add_cmd(ctx, FG_COLOR_REG, src->color); 969 break; 970 case G2D_SELECT_MODE_BGCOLOR: 971 g2d_add_cmd(ctx, BG_COLOR_REG, src->color); 972 break; 973 } 974 975 if (scale) { 976 g2d_add_cmd(ctx, SRC_SCALE_CTRL_REG, G2D_SCALE_MODE_BILINEAR); 977 g2d_add_cmd(ctx, SRC_XSCALE_REG, scale_x); 978 g2d_add_cmd(ctx, SRC_YSCALE_REG, scale_y); 979 } 980 981 bitblt.data.alpha_blend_mode = G2D_ALPHA_BLEND_MODE_ENABLE; 982 blend.val = g2d_get_blend_op(op); 983 g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val); 984 g2d_add_cmd(ctx, BLEND_FUNCTION_REG, blend.val); 985 986 pt.data.x = src_x; 987 pt.data.y = src_y; 988 g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val); 989 pt.data.x = src_x + src_w; 990 pt.data.y = src_y + src_h; 991 g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val); 992 993 pt.data.x = dst_x; 994 pt.data.y = dst_y; 995 g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val); 996 pt.data.x = dst_x + dst_w; 997 pt.data.y = dst_y + dst_h; 998 g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val); 999 1000 return g2d_flush(ctx); 1001} 1002