exynos_fimg2d.c revision 00a23bda
1e88f27b3Smrg/* 2e88f27b3Smrg * Copyright (C) 2013 Samsung Electronics Co.Ltd 3e88f27b3Smrg * Authors: 4e88f27b3Smrg * Inki Dae <inki.dae@samsung.com> 5e88f27b3Smrg * 600a23bdaSmrg * Permission is hereby granted, free of charge, to any person obtaining a 700a23bdaSmrg * copy of this software and associated documentation files (the "Software"), 800a23bdaSmrg * to deal in the Software without restriction, including without limitation 900a23bdaSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 1000a23bdaSmrg * and/or sell copies of the Software, and to permit persons to whom the 1100a23bdaSmrg * Software is furnished to do so, subject to the following conditions: 12e88f27b3Smrg * 1300a23bdaSmrg * The above copyright notice and this permission notice (including the next 1400a23bdaSmrg * paragraph) shall be included in all copies or substantial portions of the 1500a23bdaSmrg * Software. 1600a23bdaSmrg * 1700a23bdaSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1800a23bdaSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1900a23bdaSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 2000a23bdaSmrg * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 2100a23bdaSmrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 2200a23bdaSmrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 2300a23bdaSmrg * OTHER DEALINGS IN THE SOFTWARE. 24e88f27b3Smrg */ 25e88f27b3Smrg 26e88f27b3Smrg#ifdef HAVE_CONFIG_H 27e88f27b3Smrg#include "config.h" 28e88f27b3Smrg#endif 29e88f27b3Smrg 30e88f27b3Smrg#include <stdlib.h> 31e88f27b3Smrg#include <stdio.h> 32e88f27b3Smrg#include <string.h> 33e88f27b3Smrg#include <errno.h> 343f012e29Smrg#include <assert.h> 35e88f27b3Smrg 36e88f27b3Smrg#include <sys/mman.h> 37e88f27b3Smrg#include <linux/stddef.h> 38e88f27b3Smrg 39e88f27b3Smrg#include <xf86drm.h> 40e88f27b3Smrg 41e6188e58Smrg#include "libdrm_macros.h" 42e88f27b3Smrg#include "exynos_drm.h" 43e88f27b3Smrg#include "fimg2d_reg.h" 44e6188e58Smrg#include "exynos_fimg2d.h" 45e88f27b3Smrg 46e88f27b3Smrg#define SET_BF(val, sc, si, scsa, scda, dc, di, dcsa, dcda) \ 47e88f27b3Smrg val.data.src_coeff = sc; \ 48e88f27b3Smrg val.data.inv_src_color_coeff = si; \ 49e88f27b3Smrg val.data.src_coeff_src_a = scsa; \ 50e88f27b3Smrg val.data.src_coeff_dst_a = scda; \ 51e88f27b3Smrg val.data.dst_coeff = dc; \ 52e88f27b3Smrg val.data.inv_dst_color_coeff = di; \ 53e88f27b3Smrg val.data.dst_coeff_src_a = dcsa; \ 54e88f27b3Smrg val.data.dst_coeff_dst_a = dcda; 55e88f27b3Smrg 56e88f27b3Smrg#define MIN(a, b) ((a) < (b) ? (a) : (b)) 57e88f27b3Smrg 583f012e29Smrg#define MSG_PREFIX "exynos/fimg2d: " 593f012e29Smrg 603f012e29Smrg#define G2D_MAX_CMD_NR 64 613f012e29Smrg#define G2D_MAX_GEM_CMD_NR 64 623f012e29Smrg#define G2D_MAX_CMD_LIST_NR 64 633f012e29Smrg 643f012e29Smrgstruct g2d_context { 653f012e29Smrg int fd; 663f012e29Smrg unsigned int major; 673f012e29Smrg unsigned int minor; 683f012e29Smrg struct drm_exynos_g2d_cmd cmd[G2D_MAX_CMD_NR]; 693f012e29Smrg struct drm_exynos_g2d_cmd cmd_buf[G2D_MAX_GEM_CMD_NR]; 703f012e29Smrg unsigned int cmd_nr; 713f012e29Smrg unsigned int cmd_buf_nr; 723f012e29Smrg unsigned int cmdlist_nr; 733f012e29Smrg void *event_userdata; 743f012e29Smrg}; 753f012e29Smrg 76e6188e58Smrgenum g2d_base_addr_reg { 77e6188e58Smrg g2d_dst = 0, 78e6188e58Smrg g2d_src 79e6188e58Smrg}; 80e6188e58Smrg 813f012e29Smrgenum e_g2d_dir_mode { 823f012e29Smrg G2D_DIR_MODE_POSITIVE = 0, 833f012e29Smrg G2D_DIR_MODE_NEGATIVE = 1 843f012e29Smrg}; 853f012e29Smrg 863f012e29Smrgunion g2d_direction_val { 873f012e29Smrg unsigned int val[2]; 883f012e29Smrg struct { 893f012e29Smrg /* SRC_MSK_DIRECT_REG [0:1] (source) */ 903f012e29Smrg enum e_g2d_dir_mode src_x_direction:1; 913f012e29Smrg enum e_g2d_dir_mode src_y_direction:1; 923f012e29Smrg 933f012e29Smrg /* SRC_MSK_DIRECT_REG [2:3] */ 943f012e29Smrg unsigned int reversed1:2; 953f012e29Smrg 963f012e29Smrg /* SRC_MSK_DIRECT_REG [4:5] (mask) */ 973f012e29Smrg enum e_g2d_dir_mode mask_x_direction:1; 983f012e29Smrg enum e_g2d_dir_mode mask_y_direction:1; 993f012e29Smrg 1003f012e29Smrg /* SRC_MSK_DIRECT_REG [6:31] */ 1013f012e29Smrg unsigned int padding1:26; 1023f012e29Smrg 1033f012e29Smrg /* DST_PAT_DIRECT_REG [0:1] (destination) */ 1043f012e29Smrg enum e_g2d_dir_mode dst_x_direction:1; 1053f012e29Smrg enum e_g2d_dir_mode dst_y_direction:1; 1063f012e29Smrg 1073f012e29Smrg /* DST_PAT_DIRECT_REG [2:3] */ 1083f012e29Smrg unsigned int reversed2:2; 1093f012e29Smrg 1103f012e29Smrg /* DST_PAT_DIRECT_REG [4:5] (pattern) */ 1113f012e29Smrg enum e_g2d_dir_mode pat_x_direction:1; 1123f012e29Smrg enum e_g2d_dir_mode pat_y_direction:1; 1133f012e29Smrg 1143f012e29Smrg /* DST_PAT_DIRECT_REG [6:31] */ 1153f012e29Smrg unsigned int padding2:26; 1163f012e29Smrg } data; 1173f012e29Smrg}; 1183f012e29Smrg 119e6188e58Smrgstatic unsigned int g2d_get_scaling(unsigned int src, unsigned int dst) 120e6188e58Smrg{ 121e6188e58Smrg /* 122e6188e58Smrg * The G2D hw scaling factor is a normalized inverse of the scaling factor. 123e6188e58Smrg * For example: When source width is 100 and destination width is 200 124e6188e58Smrg * (scaling of 2x), then the hw factor is NC * 100 / 200. 125e6188e58Smrg * The normalization factor (NC) is 2^16 = 0x10000. 126e6188e58Smrg */ 127e6188e58Smrg 128e6188e58Smrg return ((src << 16) / dst); 129e6188e58Smrg} 130e6188e58Smrg 131e88f27b3Smrgstatic unsigned int g2d_get_blend_op(enum e_g2d_op op) 132e88f27b3Smrg{ 133e88f27b3Smrg union g2d_blend_func_val val; 134e88f27b3Smrg 135e88f27b3Smrg val.val = 0; 136e88f27b3Smrg 1373f012e29Smrg /* 1383f012e29Smrg * The switch statement is missing the default branch since 1393f012e29Smrg * we assume that the caller checks the blending operation 1403f012e29Smrg * via g2d_validate_blending_op() first. 1413f012e29Smrg */ 142e88f27b3Smrg switch (op) { 143e88f27b3Smrg case G2D_OP_CLEAR: 144e88f27b3Smrg case G2D_OP_DISJOINT_CLEAR: 145e88f27b3Smrg case G2D_OP_CONJOINT_CLEAR: 146e88f27b3Smrg SET_BF(val, G2D_COEFF_MODE_ZERO, 0, 0, 0, G2D_COEFF_MODE_ZERO, 147e88f27b3Smrg 0, 0, 0); 148e88f27b3Smrg break; 149e88f27b3Smrg case G2D_OP_SRC: 150e88f27b3Smrg case G2D_OP_DISJOINT_SRC: 151e88f27b3Smrg case G2D_OP_CONJOINT_SRC: 152e88f27b3Smrg SET_BF(val, G2D_COEFF_MODE_ONE, 0, 0, 0, G2D_COEFF_MODE_ZERO, 153e88f27b3Smrg 0, 0, 0); 154e88f27b3Smrg break; 155e88f27b3Smrg case G2D_OP_DST: 156e88f27b3Smrg case G2D_OP_DISJOINT_DST: 157e88f27b3Smrg case G2D_OP_CONJOINT_DST: 158e88f27b3Smrg SET_BF(val, G2D_COEFF_MODE_ZERO, 0, 0, 0, G2D_COEFF_MODE_ONE, 159e88f27b3Smrg 0, 0, 0); 160e88f27b3Smrg break; 161e88f27b3Smrg case G2D_OP_OVER: 162e88f27b3Smrg SET_BF(val, G2D_COEFF_MODE_ONE, 0, 0, 0, 163e88f27b3Smrg G2D_COEFF_MODE_SRC_ALPHA, 1, 0, 0); 164e88f27b3Smrg break; 165e6188e58Smrg case G2D_OP_INTERPOLATE: 166e6188e58Smrg SET_BF(val, G2D_COEFF_MODE_SRC_ALPHA, 0, 0, 0, 167e6188e58Smrg G2D_COEFF_MODE_SRC_ALPHA, 1, 0, 0); 168e6188e58Smrg break; 169e88f27b3Smrg } 170e88f27b3Smrg 171e88f27b3Smrg return val.val; 172e88f27b3Smrg} 173e88f27b3Smrg 1743f012e29Smrg/* 1753f012e29Smrg * g2d_check_space - check if command buffers have enough space left. 1763f012e29Smrg * 1773f012e29Smrg * @ctx: a pointer to g2d_context structure. 1783f012e29Smrg * @num_cmds: number of (regular) commands. 1793f012e29Smrg * @num_gem_cmds: number of GEM commands. 1803f012e29Smrg */ 1813f012e29Smrgstatic unsigned int g2d_check_space(const struct g2d_context *ctx, 1823f012e29Smrg unsigned int num_cmds, unsigned int num_gem_cmds) 1833f012e29Smrg{ 1843f012e29Smrg if (ctx->cmd_nr + num_cmds >= G2D_MAX_CMD_NR || 1853f012e29Smrg ctx->cmd_buf_nr + num_gem_cmds >= G2D_MAX_GEM_CMD_NR) 1863f012e29Smrg return 1; 1873f012e29Smrg else 1883f012e29Smrg return 0; 1893f012e29Smrg} 1903f012e29Smrg 1913f012e29Smrg/* 1923f012e29Smrg * g2d_validate_select_mode - validate select mode. 1933f012e29Smrg * 1943f012e29Smrg * @mode: the mode to validate 1953f012e29Smrg * 1963f012e29Smrg * Returns zero for an invalid mode and one otherwise. 1973f012e29Smrg */ 1983f012e29Smrgstatic int g2d_validate_select_mode( 1993f012e29Smrg enum e_g2d_select_mode mode) 2003f012e29Smrg{ 2013f012e29Smrg switch (mode) { 2023f012e29Smrg case G2D_SELECT_MODE_NORMAL: 2033f012e29Smrg case G2D_SELECT_MODE_FGCOLOR: 2043f012e29Smrg case G2D_SELECT_MODE_BGCOLOR: 2053f012e29Smrg return 1; 2063f012e29Smrg } 2073f012e29Smrg 2083f012e29Smrg return 0; 2093f012e29Smrg} 2103f012e29Smrg 2113f012e29Smrg/* 2123f012e29Smrg * g2d_validate_blending_op - validate blending operation. 2133f012e29Smrg * 2143f012e29Smrg * @operation: the operation to validate 2153f012e29Smrg * 2163f012e29Smrg * Returns zero for an invalid mode and one otherwise. 2173f012e29Smrg */ 2183f012e29Smrgstatic int g2d_validate_blending_op( 2193f012e29Smrg enum e_g2d_op operation) 2203f012e29Smrg{ 2213f012e29Smrg switch (operation) { 2223f012e29Smrg case G2D_OP_CLEAR: 2233f012e29Smrg case G2D_OP_SRC: 2243f012e29Smrg case G2D_OP_DST: 2253f012e29Smrg case G2D_OP_OVER: 2263f012e29Smrg case G2D_OP_INTERPOLATE: 2273f012e29Smrg case G2D_OP_DISJOINT_CLEAR: 2283f012e29Smrg case G2D_OP_DISJOINT_SRC: 2293f012e29Smrg case G2D_OP_DISJOINT_DST: 2303f012e29Smrg case G2D_OP_CONJOINT_CLEAR: 2313f012e29Smrg case G2D_OP_CONJOINT_SRC: 2323f012e29Smrg case G2D_OP_CONJOINT_DST: 2333f012e29Smrg return 1; 2343f012e29Smrg } 2353f012e29Smrg 2363f012e29Smrg return 0; 2373f012e29Smrg} 2383f012e29Smrg 239e88f27b3Smrg/* 240e88f27b3Smrg * g2d_add_cmd - set given command and value to user side command buffer. 241e88f27b3Smrg * 242e88f27b3Smrg * @ctx: a pointer to g2d_context structure. 243e88f27b3Smrg * @cmd: command data. 244e88f27b3Smrg * @value: value data. 2453f012e29Smrg * 2463f012e29Smrg * The caller has to make sure that the commands buffers have enough space 2473f012e29Smrg * left to hold the command. Use g2d_check_space() to ensure this. 248e88f27b3Smrg */ 2493f012e29Smrgstatic void g2d_add_cmd(struct g2d_context *ctx, unsigned long cmd, 250e88f27b3Smrg unsigned long value) 251e88f27b3Smrg{ 252e88f27b3Smrg switch (cmd & ~(G2D_BUF_USERPTR)) { 253e88f27b3Smrg case SRC_BASE_ADDR_REG: 254e88f27b3Smrg case SRC_PLANE2_BASE_ADDR_REG: 255e88f27b3Smrg case DST_BASE_ADDR_REG: 256e88f27b3Smrg case DST_PLANE2_BASE_ADDR_REG: 257e88f27b3Smrg case PAT_BASE_ADDR_REG: 258e88f27b3Smrg case MASK_BASE_ADDR_REG: 2593f012e29Smrg assert(ctx->cmd_buf_nr < G2D_MAX_GEM_CMD_NR); 260e88f27b3Smrg 261e88f27b3Smrg ctx->cmd_buf[ctx->cmd_buf_nr].offset = cmd; 262e88f27b3Smrg ctx->cmd_buf[ctx->cmd_buf_nr].data = value; 263e88f27b3Smrg ctx->cmd_buf_nr++; 264e88f27b3Smrg break; 265e88f27b3Smrg default: 2663f012e29Smrg assert(ctx->cmd_nr < G2D_MAX_CMD_NR); 267e88f27b3Smrg 268e88f27b3Smrg ctx->cmd[ctx->cmd_nr].offset = cmd; 269e88f27b3Smrg ctx->cmd[ctx->cmd_nr].data = value; 270e88f27b3Smrg ctx->cmd_nr++; 271e88f27b3Smrg break; 272e88f27b3Smrg } 273e6188e58Smrg} 274e6188e58Smrg 275e6188e58Smrg/* 276e6188e58Smrg * g2d_add_base_addr - helper function to set dst/src base address register. 277e6188e58Smrg * 278e6188e58Smrg * @ctx: a pointer to g2d_context structure. 279e6188e58Smrg * @img: a pointer to the dst/src g2d_image structure. 280e6188e58Smrg * @reg: the register that should be set. 281e6188e58Smrg */ 282e6188e58Smrgstatic void g2d_add_base_addr(struct g2d_context *ctx, struct g2d_image *img, 283e6188e58Smrg enum g2d_base_addr_reg reg) 284e6188e58Smrg{ 285e6188e58Smrg const unsigned long cmd = (reg == g2d_dst) ? 286e6188e58Smrg DST_BASE_ADDR_REG : SRC_BASE_ADDR_REG; 287e6188e58Smrg 288e6188e58Smrg if (img->buf_type == G2D_IMGBUF_USERPTR) 289e6188e58Smrg g2d_add_cmd(ctx, cmd | G2D_BUF_USERPTR, 290e6188e58Smrg (unsigned long)&img->user_ptr[0]); 291e6188e58Smrg else 292e6188e58Smrg g2d_add_cmd(ctx, cmd, img->bo[0]); 293e88f27b3Smrg} 294e88f27b3Smrg 2953f012e29Smrg/* 2963f012e29Smrg * g2d_set_direction - setup direction register (useful for overlapping blits). 2973f012e29Smrg * 2983f012e29Smrg * @ctx: a pointer to g2d_context structure. 2993f012e29Smrg * @dir: a pointer to the g2d_direction_val structure. 3003f012e29Smrg */ 3013f012e29Smrgstatic void g2d_set_direction(struct g2d_context *ctx, 3023f012e29Smrg const union g2d_direction_val *dir) 3033f012e29Smrg{ 3043f012e29Smrg g2d_add_cmd(ctx, SRC_MASK_DIRECT_REG, dir->val[0]); 3053f012e29Smrg g2d_add_cmd(ctx, DST_PAT_DIRECT_REG, dir->val[1]); 3063f012e29Smrg} 3073f012e29Smrg 308e88f27b3Smrg/* 309e6188e58Smrg * g2d_flush - submit all commands and values in user side command buffer 310e88f27b3Smrg * to command queue aware of fimg2d dma. 311e88f27b3Smrg * 312e88f27b3Smrg * @ctx: a pointer to g2d_context structure. 313e88f27b3Smrg * 314e88f27b3Smrg * This function should be called after all commands and values to user 315e6188e58Smrg * side command buffer are set. It submits that buffer to the kernel side driver. 316e88f27b3Smrg */ 317e88f27b3Smrgstatic int g2d_flush(struct g2d_context *ctx) 318e88f27b3Smrg{ 319e88f27b3Smrg int ret; 320e6188e58Smrg struct drm_exynos_g2d_set_cmdlist cmdlist = {0}; 321e88f27b3Smrg 322e6188e58Smrg if (ctx->cmd_nr == 0 && ctx->cmd_buf_nr == 0) 3233f012e29Smrg return 0; 324e88f27b3Smrg 325e88f27b3Smrg if (ctx->cmdlist_nr >= G2D_MAX_CMD_LIST_NR) { 3263f012e29Smrg fprintf(stderr, MSG_PREFIX "command list overflow.\n"); 327e88f27b3Smrg return -EINVAL; 328e88f27b3Smrg } 329e88f27b3Smrg 330baaff307Smrg cmdlist.cmd = (uint64_t)(uintptr_t)&ctx->cmd[0]; 331baaff307Smrg cmdlist.cmd_buf = (uint64_t)(uintptr_t)&ctx->cmd_buf[0]; 332e88f27b3Smrg cmdlist.cmd_nr = ctx->cmd_nr; 333e88f27b3Smrg cmdlist.cmd_buf_nr = ctx->cmd_buf_nr; 3343f012e29Smrg 3353f012e29Smrg if (ctx->event_userdata) { 3363f012e29Smrg cmdlist.event_type = G2D_EVENT_NONSTOP; 3373f012e29Smrg cmdlist.user_data = (uint64_t)(uintptr_t)(ctx->event_userdata); 3383f012e29Smrg ctx->event_userdata = NULL; 3393f012e29Smrg } else { 3403f012e29Smrg cmdlist.event_type = G2D_EVENT_NOT; 3413f012e29Smrg cmdlist.user_data = 0; 3423f012e29Smrg } 343e88f27b3Smrg 344e88f27b3Smrg ctx->cmd_nr = 0; 345e88f27b3Smrg ctx->cmd_buf_nr = 0; 346e88f27b3Smrg 347e88f27b3Smrg ret = drmIoctl(ctx->fd, DRM_IOCTL_EXYNOS_G2D_SET_CMDLIST, &cmdlist); 348e88f27b3Smrg if (ret < 0) { 3493f012e29Smrg fprintf(stderr, MSG_PREFIX "failed to set cmdlist.\n"); 350e88f27b3Smrg return ret; 351e88f27b3Smrg } 352e88f27b3Smrg 353e88f27b3Smrg ctx->cmdlist_nr++; 354e88f27b3Smrg 355e88f27b3Smrg return ret; 356e88f27b3Smrg} 357e88f27b3Smrg 358e88f27b3Smrg/** 359e88f27b3Smrg * g2d_init - create a new g2d context and get hardware version. 360e88f27b3Smrg * 361e6188e58Smrg * fd: a file descriptor to an opened drm device. 362e88f27b3Smrg */ 363e6188e58Smrgstruct g2d_context *g2d_init(int fd) 364e88f27b3Smrg{ 365e88f27b3Smrg struct drm_exynos_g2d_get_ver ver; 366e88f27b3Smrg struct g2d_context *ctx; 367e88f27b3Smrg int ret; 368e88f27b3Smrg 369e88f27b3Smrg ctx = calloc(1, sizeof(*ctx)); 370e88f27b3Smrg if (!ctx) { 3713f012e29Smrg fprintf(stderr, MSG_PREFIX "failed to allocate context.\n"); 372e88f27b3Smrg return NULL; 373e88f27b3Smrg } 374e88f27b3Smrg 375e88f27b3Smrg ctx->fd = fd; 376e88f27b3Smrg 377e88f27b3Smrg ret = drmIoctl(fd, DRM_IOCTL_EXYNOS_G2D_GET_VER, &ver); 378e88f27b3Smrg if (ret < 0) { 3793f012e29Smrg fprintf(stderr, MSG_PREFIX "failed to get version.\n"); 380e88f27b3Smrg free(ctx); 381e88f27b3Smrg return NULL; 382e88f27b3Smrg } 383e88f27b3Smrg 384e88f27b3Smrg ctx->major = ver.major; 385e88f27b3Smrg ctx->minor = ver.minor; 386e88f27b3Smrg 3873f012e29Smrg printf(MSG_PREFIX "G2D version (%d.%d).\n", ctx->major, ctx->minor); 388e88f27b3Smrg return ctx; 389e88f27b3Smrg} 390e88f27b3Smrg 391e6188e58Smrgvoid g2d_fini(struct g2d_context *ctx) 392e88f27b3Smrg{ 393e6188e58Smrg free(ctx); 394e88f27b3Smrg} 395e88f27b3Smrg 3963f012e29Smrg/** 3973f012e29Smrg * g2d_config_event - setup userdata configuration for a g2d event. 3983f012e29Smrg * The next invocation of a g2d call (e.g. g2d_solid_fill) is 3993f012e29Smrg * then going to flag the command buffer as 'nonstop'. 4003f012e29Smrg * Completion of the command buffer execution can then be 4013f012e29Smrg * determined by using drmHandleEvent on the DRM fd. 4023f012e29Smrg * The userdata is 'consumed' in the process. 4033f012e29Smrg * 4043f012e29Smrg * @ctx: a pointer to g2d_context structure. 4053f012e29Smrg * @userdata: a pointer to the user data 4063f012e29Smrg */ 4073f012e29Smrgvoid g2d_config_event(struct g2d_context *ctx, void *userdata) 4083f012e29Smrg{ 4093f012e29Smrg ctx->event_userdata = userdata; 4103f012e29Smrg} 4113f012e29Smrg 412e88f27b3Smrg/** 413e88f27b3Smrg * g2d_exec - start the dma to process all commands summited by g2d_flush(). 414e88f27b3Smrg * 415e88f27b3Smrg * @ctx: a pointer to g2d_context structure. 416e88f27b3Smrg */ 417e6188e58Smrgint g2d_exec(struct g2d_context *ctx) 418e88f27b3Smrg{ 419e88f27b3Smrg struct drm_exynos_g2d_exec exec; 420e88f27b3Smrg int ret; 421e88f27b3Smrg 422e88f27b3Smrg if (ctx->cmdlist_nr == 0) 423e88f27b3Smrg return -EINVAL; 424e88f27b3Smrg 425e88f27b3Smrg exec.async = 0; 426e88f27b3Smrg 427e88f27b3Smrg ret = drmIoctl(ctx->fd, DRM_IOCTL_EXYNOS_G2D_EXEC, &exec); 428e88f27b3Smrg if (ret < 0) { 4293f012e29Smrg fprintf(stderr, MSG_PREFIX "failed to execute.\n"); 430e88f27b3Smrg return ret; 431e88f27b3Smrg } 432e88f27b3Smrg 433e88f27b3Smrg ctx->cmdlist_nr = 0; 434e88f27b3Smrg 435e88f27b3Smrg return ret; 436e88f27b3Smrg} 437e88f27b3Smrg 438e88f27b3Smrg/** 439e88f27b3Smrg * g2d_solid_fill - fill given buffer with given color data. 440e88f27b3Smrg * 441e88f27b3Smrg * @ctx: a pointer to g2d_context structure. 442e88f27b3Smrg * @img: a pointer to g2d_image structure including image and buffer 443e88f27b3Smrg * information. 444e88f27b3Smrg * @x: x start position to buffer filled with given color data. 445e88f27b3Smrg * @y: y start position to buffer filled with given color data. 446e88f27b3Smrg * @w: width value to buffer filled with given color data. 447e88f27b3Smrg * @h: height value to buffer filled with given color data. 448e88f27b3Smrg */ 449e6188e58Smrgint 450baaff307Smrgg2d_solid_fill(struct g2d_context *ctx, struct g2d_image *img, 451e88f27b3Smrg unsigned int x, unsigned int y, unsigned int w, 452e88f27b3Smrg unsigned int h) 453e88f27b3Smrg{ 454e88f27b3Smrg union g2d_bitblt_cmd_val bitblt; 455e88f27b3Smrg union g2d_point_val pt; 456e88f27b3Smrg 4573f012e29Smrg if (g2d_check_space(ctx, 7, 1)) 4583f012e29Smrg return -ENOSPC; 4593f012e29Smrg 460e88f27b3Smrg g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL); 461e88f27b3Smrg g2d_add_cmd(ctx, DST_COLOR_MODE_REG, img->color_mode); 462e6188e58Smrg g2d_add_base_addr(ctx, img, g2d_dst); 463e88f27b3Smrg g2d_add_cmd(ctx, DST_STRIDE_REG, img->stride); 464e88f27b3Smrg 465e88f27b3Smrg if (x + w > img->width) 466e88f27b3Smrg w = img->width - x; 467e88f27b3Smrg if (y + h > img->height) 468e88f27b3Smrg h = img->height - y; 469e88f27b3Smrg 470e88f27b3Smrg pt.data.x = x; 471e88f27b3Smrg pt.data.y = y; 472e88f27b3Smrg g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val); 473e88f27b3Smrg 474e88f27b3Smrg pt.data.x = x + w; 475e88f27b3Smrg pt.data.y = y + h; 476e88f27b3Smrg g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val); 477e88f27b3Smrg 478e88f27b3Smrg g2d_add_cmd(ctx, SF_COLOR_REG, img->color); 479e88f27b3Smrg 480e88f27b3Smrg bitblt.val = 0; 481e88f27b3Smrg bitblt.data.fast_solid_color_fill_en = 1; 482e88f27b3Smrg g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val); 483e88f27b3Smrg 484e6188e58Smrg return g2d_flush(ctx); 485e88f27b3Smrg} 486e88f27b3Smrg 487e88f27b3Smrg/** 488e88f27b3Smrg * g2d_copy - copy contents in source buffer to destination buffer. 489e88f27b3Smrg * 490e88f27b3Smrg * @ctx: a pointer to g2d_context structure. 491e88f27b3Smrg * @src: a pointer to g2d_image structure including image and buffer 492e88f27b3Smrg * information to source. 493e88f27b3Smrg * @dst: a pointer to g2d_image structure including image and buffer 494e88f27b3Smrg * information to destination. 495e88f27b3Smrg * @src_x: x start position to source buffer. 496e88f27b3Smrg * @src_y: y start position to source buffer. 497e88f27b3Smrg * @dst_x: x start position to destination buffer. 498e88f27b3Smrg * @dst_y: y start position to destination buffer. 499e88f27b3Smrg * @w: width value to source and destination buffers. 500e88f27b3Smrg * @h: height value to source and destination buffers. 501e88f27b3Smrg */ 502e6188e58Smrgint 503baaff307Smrgg2d_copy(struct g2d_context *ctx, struct g2d_image *src, 504e88f27b3Smrg struct g2d_image *dst, unsigned int src_x, unsigned int src_y, 505e88f27b3Smrg unsigned int dst_x, unsigned dst_y, unsigned int w, 506e88f27b3Smrg unsigned int h) 507e88f27b3Smrg{ 508e88f27b3Smrg union g2d_rop4_val rop4; 509e88f27b3Smrg union g2d_point_val pt; 5103f012e29Smrg unsigned int src_w, src_h, dst_w, dst_h; 5113f012e29Smrg 5123f012e29Smrg src_w = w; 5133f012e29Smrg src_h = h; 5143f012e29Smrg if (src_x + src->width > w) 5153f012e29Smrg src_w = src->width - src_x; 5163f012e29Smrg if (src_y + src->height > h) 5173f012e29Smrg src_h = src->height - src_y; 5183f012e29Smrg 5193f012e29Smrg dst_w = w; 5203f012e29Smrg dst_h = w; 5213f012e29Smrg if (dst_x + dst->width > w) 5223f012e29Smrg dst_w = dst->width - dst_x; 5233f012e29Smrg if (dst_y + dst->height > h) 5243f012e29Smrg dst_h = dst->height - dst_y; 5253f012e29Smrg 5263f012e29Smrg w = MIN(src_w, dst_w); 5273f012e29Smrg h = MIN(src_h, dst_h); 5283f012e29Smrg 5293f012e29Smrg if (w <= 0 || h <= 0) { 5303f012e29Smrg fprintf(stderr, MSG_PREFIX "invalid width or height.\n"); 5313f012e29Smrg return -EINVAL; 5323f012e29Smrg } 5333f012e29Smrg 5343f012e29Smrg if (g2d_check_space(ctx, 11, 2)) 5353f012e29Smrg return -ENOSPC; 536e88f27b3Smrg 537e88f27b3Smrg g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR); 538e88f27b3Smrg g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode); 539e6188e58Smrg g2d_add_base_addr(ctx, dst, g2d_dst); 540e88f27b3Smrg g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride); 541e88f27b3Smrg 542e88f27b3Smrg g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL); 543e88f27b3Smrg g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode); 544e6188e58Smrg g2d_add_base_addr(ctx, src, g2d_src); 545e88f27b3Smrg g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride); 546e88f27b3Smrg 5473f012e29Smrg pt.data.x = src_x; 5483f012e29Smrg pt.data.y = src_y; 5493f012e29Smrg g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val); 5503f012e29Smrg pt.data.x = src_x + w; 5513f012e29Smrg pt.data.y = src_y + h; 5523f012e29Smrg g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val); 5533f012e29Smrg 5543f012e29Smrg pt.data.x = dst_x; 5553f012e29Smrg pt.data.y = dst_y; 5563f012e29Smrg g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val); 5573f012e29Smrg pt.data.x = dst_x + w; 5583f012e29Smrg pt.data.y = dst_y + h; 5593f012e29Smrg g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val); 5603f012e29Smrg 5613f012e29Smrg rop4.val = 0; 5623f012e29Smrg rop4.data.unmasked_rop3 = G2D_ROP3_SRC; 5633f012e29Smrg g2d_add_cmd(ctx, ROP4_REG, rop4.val); 5643f012e29Smrg 5653f012e29Smrg return g2d_flush(ctx); 5663f012e29Smrg} 5673f012e29Smrg 5683f012e29Smrg/** 5693f012e29Smrg * g2d_move - copy content inside single buffer. 5703f012e29Smrg * Similar to libc's memmove() this copies a rectangular 5713f012e29Smrg * region of the provided buffer to another location, while 5723f012e29Smrg * properly handling the situation where source and 5733f012e29Smrg * destination rectangle overlap. 5743f012e29Smrg * 5753f012e29Smrg * @ctx: a pointer to g2d_context structure. 5763f012e29Smrg * @img: a pointer to g2d_image structure providing 5773f012e29Smrg * buffer information. 5783f012e29Smrg * @src_x: x position of source rectangle. 5793f012e29Smrg * @src_y: y position of source rectangle. 5803f012e29Smrg * @dst_x: x position of destination rectangle. 5813f012e29Smrg * @dst_y: y position of destination rectangle. 5823f012e29Smrg * @w: width of rectangle to move. 5833f012e29Smrg * @h: height of rectangle to move. 5843f012e29Smrg */ 5853f012e29Smrgint 5863f012e29Smrgg2d_move(struct g2d_context *ctx, struct g2d_image *img, 5873f012e29Smrg unsigned int src_x, unsigned int src_y, 5883f012e29Smrg unsigned int dst_x, unsigned dst_y, unsigned int w, 5893f012e29Smrg unsigned int h) 5903f012e29Smrg{ 5913f012e29Smrg union g2d_rop4_val rop4; 5923f012e29Smrg union g2d_point_val pt; 5933f012e29Smrg union g2d_direction_val dir; 5943f012e29Smrg unsigned int src_w, src_h, dst_w, dst_h; 5953f012e29Smrg 596e88f27b3Smrg src_w = w; 597e88f27b3Smrg src_h = h; 5983f012e29Smrg if (src_x + img->width > w) 5993f012e29Smrg src_w = img->width - src_x; 6003f012e29Smrg if (src_y + img->height > h) 6013f012e29Smrg src_h = img->height - src_y; 602e88f27b3Smrg 603e88f27b3Smrg dst_w = w; 604e88f27b3Smrg dst_h = w; 6053f012e29Smrg if (dst_x + img->width > w) 6063f012e29Smrg dst_w = img->width - dst_x; 6073f012e29Smrg if (dst_y + img->height > h) 6083f012e29Smrg dst_h = img->height - dst_y; 609e88f27b3Smrg 610e88f27b3Smrg w = MIN(src_w, dst_w); 611e88f27b3Smrg h = MIN(src_h, dst_h); 612e88f27b3Smrg 6133f012e29Smrg if (w == 0 || h == 0) { 6143f012e29Smrg fprintf(stderr, MSG_PREFIX "invalid width or height.\n"); 615e88f27b3Smrg return -EINVAL; 616e88f27b3Smrg } 617e88f27b3Smrg 6183f012e29Smrg if (g2d_check_space(ctx, 13, 2)) 6193f012e29Smrg return -ENOSPC; 6203f012e29Smrg 6213f012e29Smrg g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR); 6223f012e29Smrg g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL); 6233f012e29Smrg 6243f012e29Smrg g2d_add_cmd(ctx, DST_COLOR_MODE_REG, img->color_mode); 6253f012e29Smrg g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, img->color_mode); 6263f012e29Smrg 6273f012e29Smrg g2d_add_base_addr(ctx, img, g2d_dst); 6283f012e29Smrg g2d_add_base_addr(ctx, img, g2d_src); 6293f012e29Smrg 6303f012e29Smrg g2d_add_cmd(ctx, DST_STRIDE_REG, img->stride); 6313f012e29Smrg g2d_add_cmd(ctx, SRC_STRIDE_REG, img->stride); 6323f012e29Smrg 6333f012e29Smrg dir.val[0] = dir.val[1] = 0; 6343f012e29Smrg 6353f012e29Smrg if (dst_x >= src_x) 6363f012e29Smrg dir.data.src_x_direction = dir.data.dst_x_direction = 1; 6373f012e29Smrg if (dst_y >= src_y) 6383f012e29Smrg dir.data.src_y_direction = dir.data.dst_y_direction = 1; 6393f012e29Smrg 6403f012e29Smrg g2d_set_direction(ctx, &dir); 6413f012e29Smrg 642e88f27b3Smrg pt.data.x = src_x; 643e88f27b3Smrg pt.data.y = src_y; 644e88f27b3Smrg g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val); 645e88f27b3Smrg pt.data.x = src_x + w; 646e88f27b3Smrg pt.data.y = src_y + h; 647e88f27b3Smrg g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val); 648e88f27b3Smrg 649e88f27b3Smrg pt.data.x = dst_x; 650e88f27b3Smrg pt.data.y = dst_y; 651e88f27b3Smrg g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val); 652e88f27b3Smrg pt.data.x = dst_x + w; 653baaff307Smrg pt.data.y = dst_y + h; 654e88f27b3Smrg g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val); 655e88f27b3Smrg 656e88f27b3Smrg rop4.val = 0; 657e88f27b3Smrg rop4.data.unmasked_rop3 = G2D_ROP3_SRC; 658e88f27b3Smrg g2d_add_cmd(ctx, ROP4_REG, rop4.val); 659e88f27b3Smrg 660e6188e58Smrg return g2d_flush(ctx); 661e88f27b3Smrg} 662e88f27b3Smrg 663e88f27b3Smrg/** 664e88f27b3Smrg * g2d_copy_with_scale - copy contents in source buffer to destination buffer 665e88f27b3Smrg * scaling up or down properly. 666e88f27b3Smrg * 667e88f27b3Smrg * @ctx: a pointer to g2d_context structure. 668e88f27b3Smrg * @src: a pointer to g2d_image structure including image and buffer 669e88f27b3Smrg * information to source. 670e88f27b3Smrg * @dst: a pointer to g2d_image structure including image and buffer 671e88f27b3Smrg * information to destination. 672e88f27b3Smrg * @src_x: x start position to source buffer. 673e88f27b3Smrg * @src_y: y start position to source buffer. 674e88f27b3Smrg * @src_w: width value to source buffer. 675e88f27b3Smrg * @src_h: height value to source buffer. 676e88f27b3Smrg * @dst_x: x start position to destination buffer. 677e88f27b3Smrg * @dst_y: y start position to destination buffer. 678e88f27b3Smrg * @dst_w: width value to destination buffer. 679e88f27b3Smrg * @dst_h: height value to destination buffer. 680e88f27b3Smrg * @negative: indicate that it uses color negative to source and 681e88f27b3Smrg * destination buffers. 682e88f27b3Smrg */ 683e6188e58Smrgint 684baaff307Smrgg2d_copy_with_scale(struct g2d_context *ctx, struct g2d_image *src, 685e88f27b3Smrg struct g2d_image *dst, unsigned int src_x, 686e88f27b3Smrg unsigned int src_y, unsigned int src_w, 687e88f27b3Smrg unsigned int src_h, unsigned int dst_x, 688e88f27b3Smrg unsigned int dst_y, unsigned int dst_w, 689e88f27b3Smrg unsigned int dst_h, unsigned int negative) 690e88f27b3Smrg{ 691e88f27b3Smrg union g2d_rop4_val rop4; 692e88f27b3Smrg union g2d_point_val pt; 6933f012e29Smrg unsigned int scale, repeat_pad; 694e6188e58Smrg unsigned int scale_x, scale_y; 695e88f27b3Smrg 6963f012e29Smrg /* Sanitize this parameter to facilitate space computation below. */ 6973f012e29Smrg if (negative) 6983f012e29Smrg negative = 1; 699e88f27b3Smrg 700e88f27b3Smrg if (src_w == dst_w && src_h == dst_h) 701e88f27b3Smrg scale = 0; 702e88f27b3Smrg else { 703e88f27b3Smrg scale = 1; 704e6188e58Smrg scale_x = g2d_get_scaling(src_w, dst_w); 705e6188e58Smrg scale_y = g2d_get_scaling(src_h, dst_h); 706e88f27b3Smrg } 707e88f27b3Smrg 7083f012e29Smrg repeat_pad = src->repeat_mode == G2D_REPEAT_MODE_PAD ? 1 : 0; 7093f012e29Smrg 710e88f27b3Smrg if (src_x + src_w > src->width) 711e88f27b3Smrg src_w = src->width - src_x; 712e88f27b3Smrg if (src_y + src_h > src->height) 713e88f27b3Smrg src_h = src->height - src_y; 714e88f27b3Smrg 715e88f27b3Smrg if (dst_x + dst_w > dst->width) 716e88f27b3Smrg dst_w = dst->width - dst_x; 717e88f27b3Smrg if (dst_y + dst_h > dst->height) 718e88f27b3Smrg dst_h = dst->height - dst_y; 719e88f27b3Smrg 720e88f27b3Smrg if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) { 7213f012e29Smrg fprintf(stderr, MSG_PREFIX "invalid width or height.\n"); 722e88f27b3Smrg return -EINVAL; 723e88f27b3Smrg } 724e88f27b3Smrg 7253f012e29Smrg if (g2d_check_space(ctx, 12 + scale * 3 + negative + repeat_pad, 2)) 7263f012e29Smrg return -ENOSPC; 7273f012e29Smrg 7283f012e29Smrg g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR); 7293f012e29Smrg g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode); 7303f012e29Smrg g2d_add_base_addr(ctx, dst, g2d_dst); 7313f012e29Smrg g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride); 7323f012e29Smrg 7333f012e29Smrg g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL); 7343f012e29Smrg g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode); 7353f012e29Smrg 7363f012e29Smrg g2d_add_cmd(ctx, SRC_REPEAT_MODE_REG, src->repeat_mode); 7373f012e29Smrg if (repeat_pad) 7383f012e29Smrg g2d_add_cmd(ctx, SRC_PAD_VALUE_REG, dst->color); 7393f012e29Smrg 7403f012e29Smrg g2d_add_base_addr(ctx, src, g2d_src); 7413f012e29Smrg g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride); 7423f012e29Smrg 7433f012e29Smrg rop4.val = 0; 7443f012e29Smrg rop4.data.unmasked_rop3 = G2D_ROP3_SRC; 7453f012e29Smrg 746e88f27b3Smrg if (negative) { 747e88f27b3Smrg g2d_add_cmd(ctx, BG_COLOR_REG, 0x00FFFFFF); 7483f012e29Smrg rop4.data.unmasked_rop3 ^= G2D_ROP3_DST; 749e88f27b3Smrg } 750e88f27b3Smrg 7513f012e29Smrg g2d_add_cmd(ctx, ROP4_REG, rop4.val); 7523f012e29Smrg 753e88f27b3Smrg if (scale) { 754e88f27b3Smrg g2d_add_cmd(ctx, SRC_SCALE_CTRL_REG, G2D_SCALE_MODE_BILINEAR); 755e6188e58Smrg g2d_add_cmd(ctx, SRC_XSCALE_REG, scale_x); 756e6188e58Smrg g2d_add_cmd(ctx, SRC_YSCALE_REG, scale_y); 757e88f27b3Smrg } 758e88f27b3Smrg 759e88f27b3Smrg pt.data.x = src_x; 760e88f27b3Smrg pt.data.y = src_y; 761e88f27b3Smrg g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val); 762e88f27b3Smrg pt.data.x = src_x + src_w; 763e88f27b3Smrg pt.data.y = src_y + src_h; 764e88f27b3Smrg g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val); 765e88f27b3Smrg 766e88f27b3Smrg pt.data.x = dst_x; 767e88f27b3Smrg pt.data.y = dst_y; 768e88f27b3Smrg g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val); 769e88f27b3Smrg pt.data.x = dst_x + dst_w; 770e88f27b3Smrg pt.data.y = dst_y + dst_h; 771e88f27b3Smrg g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val); 772e88f27b3Smrg 773e6188e58Smrg return g2d_flush(ctx); 774e88f27b3Smrg} 775e88f27b3Smrg 776e88f27b3Smrg/** 777e6188e58Smrg * g2d_blend - blend image data in source and destination buffers. 778e88f27b3Smrg * 779e88f27b3Smrg * @ctx: a pointer to g2d_context structure. 780e88f27b3Smrg * @src: a pointer to g2d_image structure including image and buffer 781e88f27b3Smrg * information to source. 782e88f27b3Smrg * @dst: a pointer to g2d_image structure including image and buffer 783e88f27b3Smrg * information to destination. 784e88f27b3Smrg * @src_x: x start position to source buffer. 785e88f27b3Smrg * @src_y: y start position to source buffer. 786e88f27b3Smrg * @dst_x: x start position to destination buffer. 787e88f27b3Smrg * @dst_y: y start position to destination buffer. 788e88f27b3Smrg * @w: width value to source and destination buffer. 789e88f27b3Smrg * @h: height value to source and destination buffer. 790e88f27b3Smrg * @op: blend operation type. 791e88f27b3Smrg */ 792e6188e58Smrgint 793baaff307Smrgg2d_blend(struct g2d_context *ctx, struct g2d_image *src, 794e88f27b3Smrg struct g2d_image *dst, unsigned int src_x, 795e88f27b3Smrg unsigned int src_y, unsigned int dst_x, unsigned int dst_y, 796e88f27b3Smrg unsigned int w, unsigned int h, enum e_g2d_op op) 797e88f27b3Smrg{ 798e88f27b3Smrg union g2d_point_val pt; 799e88f27b3Smrg union g2d_bitblt_cmd_val bitblt; 800e88f27b3Smrg union g2d_blend_func_val blend; 8013f012e29Smrg unsigned int gem_space; 8023f012e29Smrg unsigned int src_w, src_h, dst_w, dst_h; 8033f012e29Smrg 8043f012e29Smrg src_w = w; 8053f012e29Smrg src_h = h; 8063f012e29Smrg if (src_x + w > src->width) 8073f012e29Smrg src_w = src->width - src_x; 8083f012e29Smrg if (src_y + h > src->height) 8093f012e29Smrg src_h = src->height - src_y; 8103f012e29Smrg 8113f012e29Smrg dst_w = w; 8123f012e29Smrg dst_h = h; 8133f012e29Smrg if (dst_x + w > dst->width) 8143f012e29Smrg dst_w = dst->width - dst_x; 8153f012e29Smrg if (dst_y + h > dst->height) 8163f012e29Smrg dst_h = dst->height - dst_y; 8173f012e29Smrg 8183f012e29Smrg w = MIN(src_w, dst_w); 8193f012e29Smrg h = MIN(src_h, dst_h); 8203f012e29Smrg 8213f012e29Smrg if (w <= 0 || h <= 0) { 8223f012e29Smrg fprintf(stderr, MSG_PREFIX "invalid width or height.\n"); 8233f012e29Smrg return -EINVAL; 8243f012e29Smrg } 8253f012e29Smrg 8263f012e29Smrg if (!g2d_validate_select_mode(src->select_mode)) { 8273f012e29Smrg fprintf(stderr , MSG_PREFIX "invalid select mode for source.\n"); 8283f012e29Smrg return -EINVAL; 8293f012e29Smrg } 8303f012e29Smrg 8313f012e29Smrg if (!g2d_validate_blending_op(op)) { 8323f012e29Smrg fprintf(stderr , MSG_PREFIX "unsupported blending operation.\n"); 8333f012e29Smrg return -EINVAL; 8343f012e29Smrg } 8353f012e29Smrg 8363f012e29Smrg gem_space = src->select_mode == G2D_SELECT_MODE_NORMAL ? 2 : 1; 8373f012e29Smrg 8383f012e29Smrg if (g2d_check_space(ctx, 12, gem_space)) 8393f012e29Smrg return -ENOSPC; 840e88f27b3Smrg 841e88f27b3Smrg bitblt.val = 0; 842e88f27b3Smrg blend.val = 0; 843e88f27b3Smrg 844e88f27b3Smrg if (op == G2D_OP_SRC || op == G2D_OP_CLEAR) 845e88f27b3Smrg g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR); 846e88f27b3Smrg else 847e88f27b3Smrg g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL); 848e88f27b3Smrg 849e88f27b3Smrg g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode); 850e6188e58Smrg g2d_add_base_addr(ctx, dst, g2d_dst); 851e88f27b3Smrg g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride); 852e88f27b3Smrg 853e88f27b3Smrg g2d_add_cmd(ctx, SRC_SELECT_REG, src->select_mode); 854e88f27b3Smrg g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode); 855e88f27b3Smrg 856e88f27b3Smrg switch (src->select_mode) { 857e88f27b3Smrg case G2D_SELECT_MODE_NORMAL: 858e6188e58Smrg g2d_add_base_addr(ctx, src, g2d_src); 859e88f27b3Smrg g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride); 860e88f27b3Smrg break; 861e88f27b3Smrg case G2D_SELECT_MODE_FGCOLOR: 862e88f27b3Smrg g2d_add_cmd(ctx, FG_COLOR_REG, src->color); 863e88f27b3Smrg break; 864e88f27b3Smrg case G2D_SELECT_MODE_BGCOLOR: 865e88f27b3Smrg g2d_add_cmd(ctx, BG_COLOR_REG, src->color); 866e88f27b3Smrg break; 867e88f27b3Smrg } 868e88f27b3Smrg 869e88f27b3Smrg bitblt.data.alpha_blend_mode = G2D_ALPHA_BLEND_MODE_ENABLE; 870e88f27b3Smrg blend.val = g2d_get_blend_op(op); 871e88f27b3Smrg g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val); 872e88f27b3Smrg g2d_add_cmd(ctx, BLEND_FUNCTION_REG, blend.val); 873e88f27b3Smrg 874e88f27b3Smrg pt.data.x = src_x; 875e88f27b3Smrg pt.data.y = src_y; 876e88f27b3Smrg g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val); 877e88f27b3Smrg pt.data.x = src_x + w; 878e88f27b3Smrg pt.data.y = src_y + h; 879e88f27b3Smrg g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val); 880e88f27b3Smrg 881e88f27b3Smrg pt.data.x = dst_x; 882e88f27b3Smrg pt.data.y = dst_y; 883e88f27b3Smrg g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val); 884e88f27b3Smrg pt.data.x = dst_x + w; 885e88f27b3Smrg pt.data.y = dst_y + h; 886e88f27b3Smrg g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val); 887e88f27b3Smrg 888e6188e58Smrg return g2d_flush(ctx); 889e88f27b3Smrg} 890e88f27b3Smrg 891e6188e58Smrg/** 892e6188e58Smrg * g2d_scale_and_blend - apply scaling to source buffer and then blend to destination buffer 893e6188e58Smrg * 894e6188e58Smrg * @ctx: a pointer to g2d_context structure. 895e6188e58Smrg * @src: a pointer to g2d_image structure including image and buffer 896e6188e58Smrg * information to source. 897e6188e58Smrg * @dst: a pointer to g2d_image structure including image and buffer 898e6188e58Smrg * information to destination. 899e6188e58Smrg * @src_x: x start position to source buffer. 900e6188e58Smrg * @src_y: y start position to source buffer. 901e6188e58Smrg * @src_w: width value to source buffer. 902e6188e58Smrg * @src_h: height value to source buffer. 903e6188e58Smrg * @dst_x: x start position to destination buffer. 904e6188e58Smrg * @dst_y: y start position to destination buffer. 905e6188e58Smrg * @dst_w: width value to destination buffer. 906e6188e58Smrg * @dst_h: height value to destination buffer. 907e6188e58Smrg * @op: blend operation type. 908e6188e58Smrg */ 909e6188e58Smrgint 910e6188e58Smrgg2d_scale_and_blend(struct g2d_context *ctx, struct g2d_image *src, 911e6188e58Smrg struct g2d_image *dst, unsigned int src_x, unsigned int src_y, 912e6188e58Smrg unsigned int src_w, unsigned int src_h, unsigned int dst_x, 913e6188e58Smrg unsigned int dst_y, unsigned int dst_w, unsigned int dst_h, 914e6188e58Smrg enum e_g2d_op op) 915e6188e58Smrg{ 916e6188e58Smrg union g2d_point_val pt; 917e6188e58Smrg union g2d_bitblt_cmd_val bitblt; 918e6188e58Smrg union g2d_blend_func_val blend; 9193f012e29Smrg unsigned int scale, gem_space; 920e6188e58Smrg unsigned int scale_x, scale_y; 921e6188e58Smrg 9223f012e29Smrg if (src_w == dst_w && src_h == dst_h) 9233f012e29Smrg scale = 0; 9243f012e29Smrg else { 9253f012e29Smrg scale = 1; 9263f012e29Smrg scale_x = g2d_get_scaling(src_w, dst_w); 9273f012e29Smrg scale_y = g2d_get_scaling(src_h, dst_h); 9283f012e29Smrg } 9293f012e29Smrg 9303f012e29Smrg if (src_x + src_w > src->width) 9313f012e29Smrg src_w = src->width - src_x; 9323f012e29Smrg if (src_y + src_h > src->height) 9333f012e29Smrg src_h = src->height - src_y; 9343f012e29Smrg 9353f012e29Smrg if (dst_x + dst_w > dst->width) 9363f012e29Smrg dst_w = dst->width - dst_x; 9373f012e29Smrg if (dst_y + dst_h > dst->height) 9383f012e29Smrg dst_h = dst->height - dst_y; 9393f012e29Smrg 9403f012e29Smrg if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) { 9413f012e29Smrg fprintf(stderr, MSG_PREFIX "invalid width or height.\n"); 9423f012e29Smrg return -EINVAL; 9433f012e29Smrg } 9443f012e29Smrg 9453f012e29Smrg if (!g2d_validate_select_mode(src->select_mode)) { 9463f012e29Smrg fprintf(stderr , MSG_PREFIX "invalid select mode for source.\n"); 9473f012e29Smrg return -EINVAL; 9483f012e29Smrg } 9493f012e29Smrg 9503f012e29Smrg if (!g2d_validate_blending_op(op)) { 9513f012e29Smrg fprintf(stderr , MSG_PREFIX "unsupported blending operation.\n"); 9523f012e29Smrg return -EINVAL; 9533f012e29Smrg } 9543f012e29Smrg 9553f012e29Smrg gem_space = src->select_mode == G2D_SELECT_MODE_NORMAL ? 2 : 1; 9563f012e29Smrg 9573f012e29Smrg if (g2d_check_space(ctx, 12 + scale * 3, gem_space)) 9583f012e29Smrg return -ENOSPC; 9593f012e29Smrg 960e6188e58Smrg bitblt.val = 0; 961e6188e58Smrg blend.val = 0; 962e6188e58Smrg 963e6188e58Smrg if (op == G2D_OP_SRC || op == G2D_OP_CLEAR) 964e6188e58Smrg g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR); 965e6188e58Smrg else 966e6188e58Smrg g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL); 967e6188e58Smrg 968e6188e58Smrg g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode); 9693f012e29Smrg g2d_add_base_addr(ctx, dst, g2d_dst); 970e6188e58Smrg g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride); 971e6188e58Smrg 972e6188e58Smrg g2d_add_cmd(ctx, SRC_SELECT_REG, src->select_mode); 973e6188e58Smrg g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode); 974e6188e58Smrg 975e6188e58Smrg switch (src->select_mode) { 976e6188e58Smrg case G2D_SELECT_MODE_NORMAL: 9773f012e29Smrg g2d_add_base_addr(ctx, src, g2d_src); 978e6188e58Smrg g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride); 979e6188e58Smrg break; 980e6188e58Smrg case G2D_SELECT_MODE_FGCOLOR: 981e6188e58Smrg g2d_add_cmd(ctx, FG_COLOR_REG, src->color); 982e6188e58Smrg break; 983e6188e58Smrg case G2D_SELECT_MODE_BGCOLOR: 984e6188e58Smrg g2d_add_cmd(ctx, BG_COLOR_REG, src->color); 985e6188e58Smrg break; 986e6188e58Smrg } 987e6188e58Smrg 988e6188e58Smrg if (scale) { 989e6188e58Smrg g2d_add_cmd(ctx, SRC_SCALE_CTRL_REG, G2D_SCALE_MODE_BILINEAR); 990e6188e58Smrg g2d_add_cmd(ctx, SRC_XSCALE_REG, scale_x); 991e6188e58Smrg g2d_add_cmd(ctx, SRC_YSCALE_REG, scale_y); 992e6188e58Smrg } 993e6188e58Smrg 994e6188e58Smrg bitblt.data.alpha_blend_mode = G2D_ALPHA_BLEND_MODE_ENABLE; 995e6188e58Smrg blend.val = g2d_get_blend_op(op); 996e6188e58Smrg g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val); 997e6188e58Smrg g2d_add_cmd(ctx, BLEND_FUNCTION_REG, blend.val); 998e6188e58Smrg 999e6188e58Smrg pt.data.x = src_x; 1000e6188e58Smrg pt.data.y = src_y; 1001e6188e58Smrg g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val); 1002e6188e58Smrg pt.data.x = src_x + src_w; 1003e6188e58Smrg pt.data.y = src_y + src_h; 1004e6188e58Smrg g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val); 1005e6188e58Smrg 1006e6188e58Smrg pt.data.x = dst_x; 1007e6188e58Smrg pt.data.y = dst_y; 1008e6188e58Smrg g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val); 1009e6188e58Smrg pt.data.x = dst_x + dst_w; 1010e6188e58Smrg pt.data.y = dst_y + dst_h; 1011e6188e58Smrg g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val); 1012e6188e58Smrg 1013e6188e58Smrg return g2d_flush(ctx); 1014e6188e58Smrg} 1015