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#include <stdlib.h> 27e88f27b3Smrg#include <stdio.h> 28e88f27b3Smrg#include <string.h> 29e88f27b3Smrg#include <errno.h> 303f012e29Smrg#include <assert.h> 31e88f27b3Smrg 32e88f27b3Smrg#include <sys/mman.h> 33e88f27b3Smrg 34e88f27b3Smrg#include <xf86drm.h> 35e88f27b3Smrg 36e6188e58Smrg#include "libdrm_macros.h" 37e88f27b3Smrg#include "exynos_drm.h" 38e88f27b3Smrg#include "fimg2d_reg.h" 39e6188e58Smrg#include "exynos_fimg2d.h" 40e88f27b3Smrg 41e88f27b3Smrg#define SET_BF(val, sc, si, scsa, scda, dc, di, dcsa, dcda) \ 42e88f27b3Smrg val.data.src_coeff = sc; \ 43e88f27b3Smrg val.data.inv_src_color_coeff = si; \ 44e88f27b3Smrg val.data.src_coeff_src_a = scsa; \ 45e88f27b3Smrg val.data.src_coeff_dst_a = scda; \ 46e88f27b3Smrg val.data.dst_coeff = dc; \ 47e88f27b3Smrg val.data.inv_dst_color_coeff = di; \ 48e88f27b3Smrg val.data.dst_coeff_src_a = dcsa; \ 49e88f27b3Smrg val.data.dst_coeff_dst_a = dcda; 50e88f27b3Smrg 51e88f27b3Smrg#define MIN(a, b) ((a) < (b) ? (a) : (b)) 52e88f27b3Smrg 533f012e29Smrg#define MSG_PREFIX "exynos/fimg2d: " 543f012e29Smrg 553f012e29Smrg#define G2D_MAX_CMD_NR 64 563f012e29Smrg#define G2D_MAX_GEM_CMD_NR 64 573f012e29Smrg#define G2D_MAX_CMD_LIST_NR 64 583f012e29Smrg 593f012e29Smrgstruct g2d_context { 603f012e29Smrg int fd; 613f012e29Smrg unsigned int major; 623f012e29Smrg unsigned int minor; 633f012e29Smrg struct drm_exynos_g2d_cmd cmd[G2D_MAX_CMD_NR]; 643f012e29Smrg struct drm_exynos_g2d_cmd cmd_buf[G2D_MAX_GEM_CMD_NR]; 653f012e29Smrg unsigned int cmd_nr; 663f012e29Smrg unsigned int cmd_buf_nr; 673f012e29Smrg unsigned int cmdlist_nr; 683f012e29Smrg void *event_userdata; 693f012e29Smrg}; 703f012e29Smrg 71e6188e58Smrgenum g2d_base_addr_reg { 72e6188e58Smrg g2d_dst = 0, 73e6188e58Smrg g2d_src 74e6188e58Smrg}; 75e6188e58Smrg 763f012e29Smrgenum e_g2d_dir_mode { 773f012e29Smrg G2D_DIR_MODE_POSITIVE = 0, 783f012e29Smrg G2D_DIR_MODE_NEGATIVE = 1 793f012e29Smrg}; 803f012e29Smrg 813f012e29Smrgunion g2d_direction_val { 823f012e29Smrg unsigned int val[2]; 833f012e29Smrg struct { 843f012e29Smrg /* SRC_MSK_DIRECT_REG [0:1] (source) */ 853f012e29Smrg enum e_g2d_dir_mode src_x_direction:1; 863f012e29Smrg enum e_g2d_dir_mode src_y_direction:1; 873f012e29Smrg 883f012e29Smrg /* SRC_MSK_DIRECT_REG [2:3] */ 893f012e29Smrg unsigned int reversed1:2; 903f012e29Smrg 913f012e29Smrg /* SRC_MSK_DIRECT_REG [4:5] (mask) */ 923f012e29Smrg enum e_g2d_dir_mode mask_x_direction:1; 933f012e29Smrg enum e_g2d_dir_mode mask_y_direction:1; 943f012e29Smrg 953f012e29Smrg /* SRC_MSK_DIRECT_REG [6:31] */ 963f012e29Smrg unsigned int padding1:26; 973f012e29Smrg 983f012e29Smrg /* DST_PAT_DIRECT_REG [0:1] (destination) */ 993f012e29Smrg enum e_g2d_dir_mode dst_x_direction:1; 1003f012e29Smrg enum e_g2d_dir_mode dst_y_direction:1; 1013f012e29Smrg 1023f012e29Smrg /* DST_PAT_DIRECT_REG [2:3] */ 1033f012e29Smrg unsigned int reversed2:2; 1043f012e29Smrg 1053f012e29Smrg /* DST_PAT_DIRECT_REG [4:5] (pattern) */ 1063f012e29Smrg enum e_g2d_dir_mode pat_x_direction:1; 1073f012e29Smrg enum e_g2d_dir_mode pat_y_direction:1; 1083f012e29Smrg 1093f012e29Smrg /* DST_PAT_DIRECT_REG [6:31] */ 1103f012e29Smrg unsigned int padding2:26; 1113f012e29Smrg } data; 1123f012e29Smrg}; 1133f012e29Smrg 114e6188e58Smrgstatic unsigned int g2d_get_scaling(unsigned int src, unsigned int dst) 115e6188e58Smrg{ 116e6188e58Smrg /* 117e6188e58Smrg * The G2D hw scaling factor is a normalized inverse of the scaling factor. 118e6188e58Smrg * For example: When source width is 100 and destination width is 200 119e6188e58Smrg * (scaling of 2x), then the hw factor is NC * 100 / 200. 120e6188e58Smrg * The normalization factor (NC) is 2^16 = 0x10000. 121e6188e58Smrg */ 122e6188e58Smrg 123e6188e58Smrg return ((src << 16) / dst); 124e6188e58Smrg} 125e6188e58Smrg 126e88f27b3Smrgstatic unsigned int g2d_get_blend_op(enum e_g2d_op op) 127e88f27b3Smrg{ 128e88f27b3Smrg union g2d_blend_func_val val; 129e88f27b3Smrg 130e88f27b3Smrg val.val = 0; 131e88f27b3Smrg 1323f012e29Smrg /* 1333f012e29Smrg * The switch statement is missing the default branch since 1343f012e29Smrg * we assume that the caller checks the blending operation 1353f012e29Smrg * via g2d_validate_blending_op() first. 1363f012e29Smrg */ 137e88f27b3Smrg switch (op) { 138e88f27b3Smrg case G2D_OP_CLEAR: 139e88f27b3Smrg case G2D_OP_DISJOINT_CLEAR: 140e88f27b3Smrg case G2D_OP_CONJOINT_CLEAR: 141e88f27b3Smrg SET_BF(val, G2D_COEFF_MODE_ZERO, 0, 0, 0, G2D_COEFF_MODE_ZERO, 142e88f27b3Smrg 0, 0, 0); 143e88f27b3Smrg break; 144e88f27b3Smrg case G2D_OP_SRC: 145e88f27b3Smrg case G2D_OP_DISJOINT_SRC: 146e88f27b3Smrg case G2D_OP_CONJOINT_SRC: 147e88f27b3Smrg SET_BF(val, G2D_COEFF_MODE_ONE, 0, 0, 0, G2D_COEFF_MODE_ZERO, 148e88f27b3Smrg 0, 0, 0); 149e88f27b3Smrg break; 150e88f27b3Smrg case G2D_OP_DST: 151e88f27b3Smrg case G2D_OP_DISJOINT_DST: 152e88f27b3Smrg case G2D_OP_CONJOINT_DST: 153e88f27b3Smrg SET_BF(val, G2D_COEFF_MODE_ZERO, 0, 0, 0, G2D_COEFF_MODE_ONE, 154e88f27b3Smrg 0, 0, 0); 155e88f27b3Smrg break; 156e88f27b3Smrg case G2D_OP_OVER: 157e88f27b3Smrg SET_BF(val, G2D_COEFF_MODE_ONE, 0, 0, 0, 158e88f27b3Smrg G2D_COEFF_MODE_SRC_ALPHA, 1, 0, 0); 159e88f27b3Smrg break; 160e6188e58Smrg case G2D_OP_INTERPOLATE: 161e6188e58Smrg SET_BF(val, G2D_COEFF_MODE_SRC_ALPHA, 0, 0, 0, 162e6188e58Smrg G2D_COEFF_MODE_SRC_ALPHA, 1, 0, 0); 163e6188e58Smrg break; 164e88f27b3Smrg } 165e88f27b3Smrg 166e88f27b3Smrg return val.val; 167e88f27b3Smrg} 168e88f27b3Smrg 1693f012e29Smrg/* 1703f012e29Smrg * g2d_check_space - check if command buffers have enough space left. 1713f012e29Smrg * 1723f012e29Smrg * @ctx: a pointer to g2d_context structure. 1733f012e29Smrg * @num_cmds: number of (regular) commands. 1743f012e29Smrg * @num_gem_cmds: number of GEM commands. 1753f012e29Smrg */ 1763f012e29Smrgstatic unsigned int g2d_check_space(const struct g2d_context *ctx, 1773f012e29Smrg unsigned int num_cmds, unsigned int num_gem_cmds) 1783f012e29Smrg{ 1793f012e29Smrg if (ctx->cmd_nr + num_cmds >= G2D_MAX_CMD_NR || 1803f012e29Smrg ctx->cmd_buf_nr + num_gem_cmds >= G2D_MAX_GEM_CMD_NR) 1813f012e29Smrg return 1; 1823f012e29Smrg else 1833f012e29Smrg return 0; 1843f012e29Smrg} 1853f012e29Smrg 1863f012e29Smrg/* 1873f012e29Smrg * g2d_validate_select_mode - validate select mode. 1883f012e29Smrg * 1893f012e29Smrg * @mode: the mode to validate 1903f012e29Smrg * 1913f012e29Smrg * Returns zero for an invalid mode and one otherwise. 1923f012e29Smrg */ 1933f012e29Smrgstatic int g2d_validate_select_mode( 1943f012e29Smrg enum e_g2d_select_mode mode) 1953f012e29Smrg{ 1963f012e29Smrg switch (mode) { 1973f012e29Smrg case G2D_SELECT_MODE_NORMAL: 1983f012e29Smrg case G2D_SELECT_MODE_FGCOLOR: 1993f012e29Smrg case G2D_SELECT_MODE_BGCOLOR: 2003f012e29Smrg return 1; 2013f012e29Smrg } 2023f012e29Smrg 2033f012e29Smrg return 0; 2043f012e29Smrg} 2053f012e29Smrg 2063f012e29Smrg/* 2073f012e29Smrg * g2d_validate_blending_op - validate blending operation. 2083f012e29Smrg * 2093f012e29Smrg * @operation: the operation to validate 2103f012e29Smrg * 2113f012e29Smrg * Returns zero for an invalid mode and one otherwise. 2123f012e29Smrg */ 2133f012e29Smrgstatic int g2d_validate_blending_op( 2143f012e29Smrg enum e_g2d_op operation) 2153f012e29Smrg{ 2163f012e29Smrg switch (operation) { 2173f012e29Smrg case G2D_OP_CLEAR: 2183f012e29Smrg case G2D_OP_SRC: 2193f012e29Smrg case G2D_OP_DST: 2203f012e29Smrg case G2D_OP_OVER: 2213f012e29Smrg case G2D_OP_INTERPOLATE: 2223f012e29Smrg case G2D_OP_DISJOINT_CLEAR: 2233f012e29Smrg case G2D_OP_DISJOINT_SRC: 2243f012e29Smrg case G2D_OP_DISJOINT_DST: 2253f012e29Smrg case G2D_OP_CONJOINT_CLEAR: 2263f012e29Smrg case G2D_OP_CONJOINT_SRC: 2273f012e29Smrg case G2D_OP_CONJOINT_DST: 2283f012e29Smrg return 1; 2293f012e29Smrg } 2303f012e29Smrg 2313f012e29Smrg return 0; 2323f012e29Smrg} 2333f012e29Smrg 234e88f27b3Smrg/* 235e88f27b3Smrg * g2d_add_cmd - set given command and value to user side command buffer. 236e88f27b3Smrg * 237e88f27b3Smrg * @ctx: a pointer to g2d_context structure. 238e88f27b3Smrg * @cmd: command data. 239e88f27b3Smrg * @value: value data. 2403f012e29Smrg * 2413f012e29Smrg * The caller has to make sure that the commands buffers have enough space 2423f012e29Smrg * left to hold the command. Use g2d_check_space() to ensure this. 243e88f27b3Smrg */ 2443f012e29Smrgstatic void g2d_add_cmd(struct g2d_context *ctx, unsigned long cmd, 245e88f27b3Smrg unsigned long value) 246e88f27b3Smrg{ 247e88f27b3Smrg switch (cmd & ~(G2D_BUF_USERPTR)) { 248e88f27b3Smrg case SRC_BASE_ADDR_REG: 249e88f27b3Smrg case SRC_PLANE2_BASE_ADDR_REG: 250e88f27b3Smrg case DST_BASE_ADDR_REG: 251e88f27b3Smrg case DST_PLANE2_BASE_ADDR_REG: 252e88f27b3Smrg case PAT_BASE_ADDR_REG: 253e88f27b3Smrg case MASK_BASE_ADDR_REG: 2543f012e29Smrg assert(ctx->cmd_buf_nr < G2D_MAX_GEM_CMD_NR); 255e88f27b3Smrg 256e88f27b3Smrg ctx->cmd_buf[ctx->cmd_buf_nr].offset = cmd; 257e88f27b3Smrg ctx->cmd_buf[ctx->cmd_buf_nr].data = value; 258e88f27b3Smrg ctx->cmd_buf_nr++; 259e88f27b3Smrg break; 260e88f27b3Smrg default: 2613f012e29Smrg assert(ctx->cmd_nr < G2D_MAX_CMD_NR); 262e88f27b3Smrg 263e88f27b3Smrg ctx->cmd[ctx->cmd_nr].offset = cmd; 264e88f27b3Smrg ctx->cmd[ctx->cmd_nr].data = value; 265e88f27b3Smrg ctx->cmd_nr++; 266e88f27b3Smrg break; 267e88f27b3Smrg } 268e6188e58Smrg} 269e6188e58Smrg 270e6188e58Smrg/* 271e6188e58Smrg * g2d_add_base_addr - helper function to set dst/src base address register. 272e6188e58Smrg * 273e6188e58Smrg * @ctx: a pointer to g2d_context structure. 274e6188e58Smrg * @img: a pointer to the dst/src g2d_image structure. 275e6188e58Smrg * @reg: the register that should be set. 276e6188e58Smrg */ 277e6188e58Smrgstatic void g2d_add_base_addr(struct g2d_context *ctx, struct g2d_image *img, 278e6188e58Smrg enum g2d_base_addr_reg reg) 279e6188e58Smrg{ 280e6188e58Smrg const unsigned long cmd = (reg == g2d_dst) ? 281e6188e58Smrg DST_BASE_ADDR_REG : SRC_BASE_ADDR_REG; 282e6188e58Smrg 283e6188e58Smrg if (img->buf_type == G2D_IMGBUF_USERPTR) 284e6188e58Smrg g2d_add_cmd(ctx, cmd | G2D_BUF_USERPTR, 285e6188e58Smrg (unsigned long)&img->user_ptr[0]); 286e6188e58Smrg else 287e6188e58Smrg g2d_add_cmd(ctx, cmd, img->bo[0]); 288e88f27b3Smrg} 289e88f27b3Smrg 2903f012e29Smrg/* 2913f012e29Smrg * g2d_set_direction - setup direction register (useful for overlapping blits). 2923f012e29Smrg * 2933f012e29Smrg * @ctx: a pointer to g2d_context structure. 2943f012e29Smrg * @dir: a pointer to the g2d_direction_val structure. 2953f012e29Smrg */ 2963f012e29Smrgstatic void g2d_set_direction(struct g2d_context *ctx, 2973f012e29Smrg const union g2d_direction_val *dir) 2983f012e29Smrg{ 2993f012e29Smrg g2d_add_cmd(ctx, SRC_MASK_DIRECT_REG, dir->val[0]); 3003f012e29Smrg g2d_add_cmd(ctx, DST_PAT_DIRECT_REG, dir->val[1]); 3013f012e29Smrg} 3023f012e29Smrg 303e88f27b3Smrg/* 304e6188e58Smrg * g2d_flush - submit all commands and values in user side command buffer 305e88f27b3Smrg * to command queue aware of fimg2d dma. 306e88f27b3Smrg * 307e88f27b3Smrg * @ctx: a pointer to g2d_context structure. 308e88f27b3Smrg * 309e88f27b3Smrg * This function should be called after all commands and values to user 310e6188e58Smrg * side command buffer are set. It submits that buffer to the kernel side driver. 311e88f27b3Smrg */ 312e88f27b3Smrgstatic int g2d_flush(struct g2d_context *ctx) 313e88f27b3Smrg{ 314e88f27b3Smrg int ret; 315e6188e58Smrg struct drm_exynos_g2d_set_cmdlist cmdlist = {0}; 316e88f27b3Smrg 317e6188e58Smrg if (ctx->cmd_nr == 0 && ctx->cmd_buf_nr == 0) 3183f012e29Smrg return 0; 319e88f27b3Smrg 320e88f27b3Smrg if (ctx->cmdlist_nr >= G2D_MAX_CMD_LIST_NR) { 3213f012e29Smrg fprintf(stderr, MSG_PREFIX "command list overflow.\n"); 322e88f27b3Smrg return -EINVAL; 323e88f27b3Smrg } 324e88f27b3Smrg 325baaff307Smrg cmdlist.cmd = (uint64_t)(uintptr_t)&ctx->cmd[0]; 326baaff307Smrg cmdlist.cmd_buf = (uint64_t)(uintptr_t)&ctx->cmd_buf[0]; 327e88f27b3Smrg cmdlist.cmd_nr = ctx->cmd_nr; 328e88f27b3Smrg cmdlist.cmd_buf_nr = ctx->cmd_buf_nr; 3293f012e29Smrg 3303f012e29Smrg if (ctx->event_userdata) { 3313f012e29Smrg cmdlist.event_type = G2D_EVENT_NONSTOP; 3323f012e29Smrg cmdlist.user_data = (uint64_t)(uintptr_t)(ctx->event_userdata); 3333f012e29Smrg ctx->event_userdata = NULL; 3343f012e29Smrg } else { 3353f012e29Smrg cmdlist.event_type = G2D_EVENT_NOT; 3363f012e29Smrg cmdlist.user_data = 0; 3373f012e29Smrg } 338e88f27b3Smrg 339e88f27b3Smrg ctx->cmd_nr = 0; 340e88f27b3Smrg ctx->cmd_buf_nr = 0; 341e88f27b3Smrg 342e88f27b3Smrg ret = drmIoctl(ctx->fd, DRM_IOCTL_EXYNOS_G2D_SET_CMDLIST, &cmdlist); 343e88f27b3Smrg if (ret < 0) { 3443f012e29Smrg fprintf(stderr, MSG_PREFIX "failed to set cmdlist.\n"); 345e88f27b3Smrg return ret; 346e88f27b3Smrg } 347e88f27b3Smrg 348e88f27b3Smrg ctx->cmdlist_nr++; 349e88f27b3Smrg 350e88f27b3Smrg return ret; 351e88f27b3Smrg} 352e88f27b3Smrg 353e88f27b3Smrg/** 354e88f27b3Smrg * g2d_init - create a new g2d context and get hardware version. 355e88f27b3Smrg * 356e6188e58Smrg * fd: a file descriptor to an opened drm device. 357e88f27b3Smrg */ 3587cdc0497Smrgdrm_public struct g2d_context *g2d_init(int fd) 359e88f27b3Smrg{ 360e88f27b3Smrg struct drm_exynos_g2d_get_ver ver; 361e88f27b3Smrg struct g2d_context *ctx; 362e88f27b3Smrg int ret; 363e88f27b3Smrg 364e88f27b3Smrg ctx = calloc(1, sizeof(*ctx)); 365e88f27b3Smrg if (!ctx) { 3663f012e29Smrg fprintf(stderr, MSG_PREFIX "failed to allocate context.\n"); 367e88f27b3Smrg return NULL; 368e88f27b3Smrg } 369e88f27b3Smrg 370e88f27b3Smrg ctx->fd = fd; 371e88f27b3Smrg 372e88f27b3Smrg ret = drmIoctl(fd, DRM_IOCTL_EXYNOS_G2D_GET_VER, &ver); 373e88f27b3Smrg if (ret < 0) { 3743f012e29Smrg fprintf(stderr, MSG_PREFIX "failed to get version.\n"); 375e88f27b3Smrg free(ctx); 376e88f27b3Smrg return NULL; 377e88f27b3Smrg } 378e88f27b3Smrg 379e88f27b3Smrg ctx->major = ver.major; 380e88f27b3Smrg ctx->minor = ver.minor; 381e88f27b3Smrg 3823f012e29Smrg printf(MSG_PREFIX "G2D version (%d.%d).\n", ctx->major, ctx->minor); 383e88f27b3Smrg return ctx; 384e88f27b3Smrg} 385e88f27b3Smrg 3867cdc0497Smrgdrm_public void g2d_fini(struct g2d_context *ctx) 387e88f27b3Smrg{ 388e6188e58Smrg free(ctx); 389e88f27b3Smrg} 390e88f27b3Smrg 3913f012e29Smrg/** 3923f012e29Smrg * g2d_config_event - setup userdata configuration for a g2d event. 3933f012e29Smrg * The next invocation of a g2d call (e.g. g2d_solid_fill) is 3943f012e29Smrg * then going to flag the command buffer as 'nonstop'. 3953f012e29Smrg * Completion of the command buffer execution can then be 3963f012e29Smrg * determined by using drmHandleEvent on the DRM fd. 3973f012e29Smrg * The userdata is 'consumed' in the process. 3983f012e29Smrg * 3993f012e29Smrg * @ctx: a pointer to g2d_context structure. 4003f012e29Smrg * @userdata: a pointer to the user data 4013f012e29Smrg */ 4027cdc0497Smrgdrm_public void g2d_config_event(struct g2d_context *ctx, void *userdata) 4033f012e29Smrg{ 4043f012e29Smrg ctx->event_userdata = userdata; 4053f012e29Smrg} 4063f012e29Smrg 407e88f27b3Smrg/** 408e88f27b3Smrg * g2d_exec - start the dma to process all commands summited by g2d_flush(). 409e88f27b3Smrg * 410e88f27b3Smrg * @ctx: a pointer to g2d_context structure. 411e88f27b3Smrg */ 4127cdc0497Smrgdrm_public int g2d_exec(struct g2d_context *ctx) 413e88f27b3Smrg{ 414e88f27b3Smrg struct drm_exynos_g2d_exec exec; 415e88f27b3Smrg int ret; 416e88f27b3Smrg 417e88f27b3Smrg if (ctx->cmdlist_nr == 0) 418e88f27b3Smrg return -EINVAL; 419e88f27b3Smrg 420e88f27b3Smrg exec.async = 0; 421e88f27b3Smrg 422e88f27b3Smrg ret = drmIoctl(ctx->fd, DRM_IOCTL_EXYNOS_G2D_EXEC, &exec); 423e88f27b3Smrg if (ret < 0) { 4243f012e29Smrg fprintf(stderr, MSG_PREFIX "failed to execute.\n"); 425e88f27b3Smrg return ret; 426e88f27b3Smrg } 427e88f27b3Smrg 428e88f27b3Smrg ctx->cmdlist_nr = 0; 429e88f27b3Smrg 430e88f27b3Smrg return ret; 431e88f27b3Smrg} 432e88f27b3Smrg 433e88f27b3Smrg/** 434e88f27b3Smrg * g2d_solid_fill - fill given buffer with given color data. 435e88f27b3Smrg * 436e88f27b3Smrg * @ctx: a pointer to g2d_context structure. 437e88f27b3Smrg * @img: a pointer to g2d_image structure including image and buffer 438e88f27b3Smrg * information. 439e88f27b3Smrg * @x: x start position to buffer filled with given color data. 440e88f27b3Smrg * @y: y start position to buffer filled with given color data. 441e88f27b3Smrg * @w: width value to buffer filled with given color data. 442e88f27b3Smrg * @h: height value to buffer filled with given color data. 443e88f27b3Smrg */ 4447cdc0497Smrgdrm_public int 445baaff307Smrgg2d_solid_fill(struct g2d_context *ctx, struct g2d_image *img, 446e88f27b3Smrg unsigned int x, unsigned int y, unsigned int w, 447e88f27b3Smrg unsigned int h) 448e88f27b3Smrg{ 449e88f27b3Smrg union g2d_bitblt_cmd_val bitblt; 450e88f27b3Smrg union g2d_point_val pt; 451e88f27b3Smrg 4523f012e29Smrg if (g2d_check_space(ctx, 7, 1)) 4533f012e29Smrg return -ENOSPC; 4543f012e29Smrg 455e88f27b3Smrg g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL); 456e88f27b3Smrg g2d_add_cmd(ctx, DST_COLOR_MODE_REG, img->color_mode); 457e6188e58Smrg g2d_add_base_addr(ctx, img, g2d_dst); 458e88f27b3Smrg g2d_add_cmd(ctx, DST_STRIDE_REG, img->stride); 459e88f27b3Smrg 460e88f27b3Smrg if (x + w > img->width) 461e88f27b3Smrg w = img->width - x; 462e88f27b3Smrg if (y + h > img->height) 463e88f27b3Smrg h = img->height - y; 464e88f27b3Smrg 465e88f27b3Smrg pt.data.x = x; 466e88f27b3Smrg pt.data.y = y; 467e88f27b3Smrg g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val); 468e88f27b3Smrg 469e88f27b3Smrg pt.data.x = x + w; 470e88f27b3Smrg pt.data.y = y + h; 471e88f27b3Smrg g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val); 472e88f27b3Smrg 473e88f27b3Smrg g2d_add_cmd(ctx, SF_COLOR_REG, img->color); 474e88f27b3Smrg 475e88f27b3Smrg bitblt.val = 0; 476e88f27b3Smrg bitblt.data.fast_solid_color_fill_en = 1; 477e88f27b3Smrg g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val); 478e88f27b3Smrg 479e6188e58Smrg return g2d_flush(ctx); 480e88f27b3Smrg} 481e88f27b3Smrg 482e88f27b3Smrg/** 483e88f27b3Smrg * g2d_copy - copy contents in source buffer to destination buffer. 484e88f27b3Smrg * 485e88f27b3Smrg * @ctx: a pointer to g2d_context structure. 486e88f27b3Smrg * @src: a pointer to g2d_image structure including image and buffer 487e88f27b3Smrg * information to source. 488e88f27b3Smrg * @dst: a pointer to g2d_image structure including image and buffer 489e88f27b3Smrg * information to destination. 490e88f27b3Smrg * @src_x: x start position to source buffer. 491e88f27b3Smrg * @src_y: y start position to source buffer. 492e88f27b3Smrg * @dst_x: x start position to destination buffer. 493e88f27b3Smrg * @dst_y: y start position to destination buffer. 494e88f27b3Smrg * @w: width value to source and destination buffers. 495e88f27b3Smrg * @h: height value to source and destination buffers. 496e88f27b3Smrg */ 4977cdc0497Smrgdrm_public int 498baaff307Smrgg2d_copy(struct g2d_context *ctx, struct g2d_image *src, 499e88f27b3Smrg struct g2d_image *dst, unsigned int src_x, unsigned int src_y, 500e88f27b3Smrg unsigned int dst_x, unsigned dst_y, unsigned int w, 501e88f27b3Smrg unsigned int h) 502e88f27b3Smrg{ 503e88f27b3Smrg union g2d_rop4_val rop4; 504e88f27b3Smrg union g2d_point_val pt; 5053f012e29Smrg unsigned int src_w, src_h, dst_w, dst_h; 5063f012e29Smrg 5073f012e29Smrg src_w = w; 5083f012e29Smrg src_h = h; 5093f012e29Smrg if (src_x + src->width > w) 5103f012e29Smrg src_w = src->width - src_x; 5113f012e29Smrg if (src_y + src->height > h) 5123f012e29Smrg src_h = src->height - src_y; 5133f012e29Smrg 5143f012e29Smrg dst_w = w; 5153f012e29Smrg dst_h = w; 5163f012e29Smrg if (dst_x + dst->width > w) 5173f012e29Smrg dst_w = dst->width - dst_x; 5183f012e29Smrg if (dst_y + dst->height > h) 5193f012e29Smrg dst_h = dst->height - dst_y; 5203f012e29Smrg 5213f012e29Smrg w = MIN(src_w, dst_w); 5223f012e29Smrg h = MIN(src_h, dst_h); 5233f012e29Smrg 5243f012e29Smrg if (w <= 0 || h <= 0) { 5253f012e29Smrg fprintf(stderr, MSG_PREFIX "invalid width or height.\n"); 5263f012e29Smrg return -EINVAL; 5273f012e29Smrg } 5283f012e29Smrg 5293f012e29Smrg if (g2d_check_space(ctx, 11, 2)) 5303f012e29Smrg return -ENOSPC; 531e88f27b3Smrg 532e88f27b3Smrg g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR); 533e88f27b3Smrg g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode); 534e6188e58Smrg g2d_add_base_addr(ctx, dst, g2d_dst); 535e88f27b3Smrg g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride); 536e88f27b3Smrg 537e88f27b3Smrg g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL); 538e88f27b3Smrg g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode); 539e6188e58Smrg g2d_add_base_addr(ctx, src, g2d_src); 540e88f27b3Smrg g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride); 541e88f27b3Smrg 5423f012e29Smrg pt.data.x = src_x; 5433f012e29Smrg pt.data.y = src_y; 5443f012e29Smrg g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val); 5453f012e29Smrg pt.data.x = src_x + w; 5463f012e29Smrg pt.data.y = src_y + h; 5473f012e29Smrg g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val); 5483f012e29Smrg 5493f012e29Smrg pt.data.x = dst_x; 5503f012e29Smrg pt.data.y = dst_y; 5513f012e29Smrg g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val); 5523f012e29Smrg pt.data.x = dst_x + w; 5533f012e29Smrg pt.data.y = dst_y + h; 5543f012e29Smrg g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val); 5553f012e29Smrg 5563f012e29Smrg rop4.val = 0; 5573f012e29Smrg rop4.data.unmasked_rop3 = G2D_ROP3_SRC; 5583f012e29Smrg g2d_add_cmd(ctx, ROP4_REG, rop4.val); 5593f012e29Smrg 5603f012e29Smrg return g2d_flush(ctx); 5613f012e29Smrg} 5623f012e29Smrg 5633f012e29Smrg/** 5643f012e29Smrg * g2d_move - copy content inside single buffer. 5653f012e29Smrg * Similar to libc's memmove() this copies a rectangular 5663f012e29Smrg * region of the provided buffer to another location, while 5673f012e29Smrg * properly handling the situation where source and 5683f012e29Smrg * destination rectangle overlap. 5693f012e29Smrg * 5703f012e29Smrg * @ctx: a pointer to g2d_context structure. 5713f012e29Smrg * @img: a pointer to g2d_image structure providing 5723f012e29Smrg * buffer information. 5733f012e29Smrg * @src_x: x position of source rectangle. 5743f012e29Smrg * @src_y: y position of source rectangle. 5753f012e29Smrg * @dst_x: x position of destination rectangle. 5763f012e29Smrg * @dst_y: y position of destination rectangle. 5773f012e29Smrg * @w: width of rectangle to move. 5783f012e29Smrg * @h: height of rectangle to move. 5793f012e29Smrg */ 5807cdc0497Smrgdrm_public int 5813f012e29Smrgg2d_move(struct g2d_context *ctx, struct g2d_image *img, 5823f012e29Smrg unsigned int src_x, unsigned int src_y, 5833f012e29Smrg unsigned int dst_x, unsigned dst_y, unsigned int w, 5843f012e29Smrg unsigned int h) 5853f012e29Smrg{ 5863f012e29Smrg union g2d_rop4_val rop4; 5873f012e29Smrg union g2d_point_val pt; 5883f012e29Smrg union g2d_direction_val dir; 5893f012e29Smrg unsigned int src_w, src_h, dst_w, dst_h; 5903f012e29Smrg 591e88f27b3Smrg src_w = w; 592e88f27b3Smrg src_h = h; 5933f012e29Smrg if (src_x + img->width > w) 5943f012e29Smrg src_w = img->width - src_x; 5953f012e29Smrg if (src_y + img->height > h) 5963f012e29Smrg src_h = img->height - src_y; 597e88f27b3Smrg 598e88f27b3Smrg dst_w = w; 599e88f27b3Smrg dst_h = w; 6003f012e29Smrg if (dst_x + img->width > w) 6013f012e29Smrg dst_w = img->width - dst_x; 6023f012e29Smrg if (dst_y + img->height > h) 6033f012e29Smrg dst_h = img->height - dst_y; 604e88f27b3Smrg 605e88f27b3Smrg w = MIN(src_w, dst_w); 606e88f27b3Smrg h = MIN(src_h, dst_h); 607e88f27b3Smrg 6083f012e29Smrg if (w == 0 || h == 0) { 6093f012e29Smrg fprintf(stderr, MSG_PREFIX "invalid width or height.\n"); 610e88f27b3Smrg return -EINVAL; 611e88f27b3Smrg } 612e88f27b3Smrg 6133f012e29Smrg if (g2d_check_space(ctx, 13, 2)) 6143f012e29Smrg return -ENOSPC; 6153f012e29Smrg 6163f012e29Smrg g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR); 6173f012e29Smrg g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL); 6183f012e29Smrg 6193f012e29Smrg g2d_add_cmd(ctx, DST_COLOR_MODE_REG, img->color_mode); 6203f012e29Smrg g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, img->color_mode); 6213f012e29Smrg 6223f012e29Smrg g2d_add_base_addr(ctx, img, g2d_dst); 6233f012e29Smrg g2d_add_base_addr(ctx, img, g2d_src); 6243f012e29Smrg 6253f012e29Smrg g2d_add_cmd(ctx, DST_STRIDE_REG, img->stride); 6263f012e29Smrg g2d_add_cmd(ctx, SRC_STRIDE_REG, img->stride); 6273f012e29Smrg 6283f012e29Smrg dir.val[0] = dir.val[1] = 0; 6293f012e29Smrg 6303f012e29Smrg if (dst_x >= src_x) 6313f012e29Smrg dir.data.src_x_direction = dir.data.dst_x_direction = 1; 6323f012e29Smrg if (dst_y >= src_y) 6333f012e29Smrg dir.data.src_y_direction = dir.data.dst_y_direction = 1; 6343f012e29Smrg 6353f012e29Smrg g2d_set_direction(ctx, &dir); 6363f012e29Smrg 637e88f27b3Smrg pt.data.x = src_x; 638e88f27b3Smrg pt.data.y = src_y; 639e88f27b3Smrg g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val); 640e88f27b3Smrg pt.data.x = src_x + w; 641e88f27b3Smrg pt.data.y = src_y + h; 642e88f27b3Smrg g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val); 643e88f27b3Smrg 644e88f27b3Smrg pt.data.x = dst_x; 645e88f27b3Smrg pt.data.y = dst_y; 646e88f27b3Smrg g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val); 647e88f27b3Smrg pt.data.x = dst_x + w; 648baaff307Smrg pt.data.y = dst_y + h; 649e88f27b3Smrg g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val); 650e88f27b3Smrg 651e88f27b3Smrg rop4.val = 0; 652e88f27b3Smrg rop4.data.unmasked_rop3 = G2D_ROP3_SRC; 653e88f27b3Smrg g2d_add_cmd(ctx, ROP4_REG, rop4.val); 654e88f27b3Smrg 655e6188e58Smrg return g2d_flush(ctx); 656e88f27b3Smrg} 657e88f27b3Smrg 658e88f27b3Smrg/** 659e88f27b3Smrg * g2d_copy_with_scale - copy contents in source buffer to destination buffer 660e88f27b3Smrg * scaling up or down properly. 661e88f27b3Smrg * 662e88f27b3Smrg * @ctx: a pointer to g2d_context structure. 663e88f27b3Smrg * @src: a pointer to g2d_image structure including image and buffer 664e88f27b3Smrg * information to source. 665e88f27b3Smrg * @dst: a pointer to g2d_image structure including image and buffer 666e88f27b3Smrg * information to destination. 667e88f27b3Smrg * @src_x: x start position to source buffer. 668e88f27b3Smrg * @src_y: y start position to source buffer. 669e88f27b3Smrg * @src_w: width value to source buffer. 670e88f27b3Smrg * @src_h: height value to source buffer. 671e88f27b3Smrg * @dst_x: x start position to destination buffer. 672e88f27b3Smrg * @dst_y: y start position to destination buffer. 673e88f27b3Smrg * @dst_w: width value to destination buffer. 674e88f27b3Smrg * @dst_h: height value to destination buffer. 675e88f27b3Smrg * @negative: indicate that it uses color negative to source and 676e88f27b3Smrg * destination buffers. 677e88f27b3Smrg */ 6787cdc0497Smrgdrm_public int 679baaff307Smrgg2d_copy_with_scale(struct g2d_context *ctx, struct g2d_image *src, 680e88f27b3Smrg struct g2d_image *dst, unsigned int src_x, 681e88f27b3Smrg unsigned int src_y, unsigned int src_w, 682e88f27b3Smrg unsigned int src_h, unsigned int dst_x, 683e88f27b3Smrg unsigned int dst_y, unsigned int dst_w, 684e88f27b3Smrg unsigned int dst_h, unsigned int negative) 685e88f27b3Smrg{ 686e88f27b3Smrg union g2d_rop4_val rop4; 687e88f27b3Smrg union g2d_point_val pt; 6883f012e29Smrg unsigned int scale, repeat_pad; 689e6188e58Smrg unsigned int scale_x, scale_y; 690e88f27b3Smrg 6913f012e29Smrg /* Sanitize this parameter to facilitate space computation below. */ 6923f012e29Smrg if (negative) 6933f012e29Smrg negative = 1; 694e88f27b3Smrg 695e88f27b3Smrg if (src_w == dst_w && src_h == dst_h) 696e88f27b3Smrg scale = 0; 697e88f27b3Smrg else { 698e88f27b3Smrg scale = 1; 699e6188e58Smrg scale_x = g2d_get_scaling(src_w, dst_w); 700e6188e58Smrg scale_y = g2d_get_scaling(src_h, dst_h); 701e88f27b3Smrg } 702e88f27b3Smrg 7033f012e29Smrg repeat_pad = src->repeat_mode == G2D_REPEAT_MODE_PAD ? 1 : 0; 7043f012e29Smrg 705e88f27b3Smrg if (src_x + src_w > src->width) 706e88f27b3Smrg src_w = src->width - src_x; 707e88f27b3Smrg if (src_y + src_h > src->height) 708e88f27b3Smrg src_h = src->height - src_y; 709e88f27b3Smrg 710e88f27b3Smrg if (dst_x + dst_w > dst->width) 711e88f27b3Smrg dst_w = dst->width - dst_x; 712e88f27b3Smrg if (dst_y + dst_h > dst->height) 713e88f27b3Smrg dst_h = dst->height - dst_y; 714e88f27b3Smrg 715e88f27b3Smrg if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) { 7163f012e29Smrg fprintf(stderr, MSG_PREFIX "invalid width or height.\n"); 717e88f27b3Smrg return -EINVAL; 718e88f27b3Smrg } 719e88f27b3Smrg 7203f012e29Smrg if (g2d_check_space(ctx, 12 + scale * 3 + negative + repeat_pad, 2)) 7213f012e29Smrg return -ENOSPC; 7223f012e29Smrg 7233f012e29Smrg g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR); 7243f012e29Smrg g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode); 7253f012e29Smrg g2d_add_base_addr(ctx, dst, g2d_dst); 7263f012e29Smrg g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride); 7273f012e29Smrg 7283f012e29Smrg g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL); 7293f012e29Smrg g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode); 7303f012e29Smrg 7313f012e29Smrg g2d_add_cmd(ctx, SRC_REPEAT_MODE_REG, src->repeat_mode); 7323f012e29Smrg if (repeat_pad) 7333f012e29Smrg g2d_add_cmd(ctx, SRC_PAD_VALUE_REG, dst->color); 7343f012e29Smrg 7353f012e29Smrg g2d_add_base_addr(ctx, src, g2d_src); 7363f012e29Smrg g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride); 7373f012e29Smrg 7383f012e29Smrg rop4.val = 0; 7393f012e29Smrg rop4.data.unmasked_rop3 = G2D_ROP3_SRC; 7403f012e29Smrg 741e88f27b3Smrg if (negative) { 742e88f27b3Smrg g2d_add_cmd(ctx, BG_COLOR_REG, 0x00FFFFFF); 7433f012e29Smrg rop4.data.unmasked_rop3 ^= G2D_ROP3_DST; 744e88f27b3Smrg } 745e88f27b3Smrg 7463f012e29Smrg g2d_add_cmd(ctx, ROP4_REG, rop4.val); 7473f012e29Smrg 748e88f27b3Smrg if (scale) { 749e88f27b3Smrg g2d_add_cmd(ctx, SRC_SCALE_CTRL_REG, G2D_SCALE_MODE_BILINEAR); 750e6188e58Smrg g2d_add_cmd(ctx, SRC_XSCALE_REG, scale_x); 751e6188e58Smrg g2d_add_cmd(ctx, SRC_YSCALE_REG, scale_y); 752e88f27b3Smrg } 753e88f27b3Smrg 754e88f27b3Smrg pt.data.x = src_x; 755e88f27b3Smrg pt.data.y = src_y; 756e88f27b3Smrg g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val); 757e88f27b3Smrg pt.data.x = src_x + src_w; 758e88f27b3Smrg pt.data.y = src_y + src_h; 759e88f27b3Smrg g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val); 760e88f27b3Smrg 761e88f27b3Smrg pt.data.x = dst_x; 762e88f27b3Smrg pt.data.y = dst_y; 763e88f27b3Smrg g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val); 764e88f27b3Smrg pt.data.x = dst_x + dst_w; 765e88f27b3Smrg pt.data.y = dst_y + dst_h; 766e88f27b3Smrg g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val); 767e88f27b3Smrg 768e6188e58Smrg return g2d_flush(ctx); 769e88f27b3Smrg} 770e88f27b3Smrg 771e88f27b3Smrg/** 772e6188e58Smrg * g2d_blend - blend image data in source and destination buffers. 773e88f27b3Smrg * 774e88f27b3Smrg * @ctx: a pointer to g2d_context structure. 775e88f27b3Smrg * @src: a pointer to g2d_image structure including image and buffer 776e88f27b3Smrg * information to source. 777e88f27b3Smrg * @dst: a pointer to g2d_image structure including image and buffer 778e88f27b3Smrg * information to destination. 779e88f27b3Smrg * @src_x: x start position to source buffer. 780e88f27b3Smrg * @src_y: y start position to source buffer. 781e88f27b3Smrg * @dst_x: x start position to destination buffer. 782e88f27b3Smrg * @dst_y: y start position to destination buffer. 783e88f27b3Smrg * @w: width value to source and destination buffer. 784e88f27b3Smrg * @h: height value to source and destination buffer. 785e88f27b3Smrg * @op: blend operation type. 786e88f27b3Smrg */ 7877cdc0497Smrgdrm_public int 788baaff307Smrgg2d_blend(struct g2d_context *ctx, struct g2d_image *src, 789e88f27b3Smrg struct g2d_image *dst, unsigned int src_x, 790e88f27b3Smrg unsigned int src_y, unsigned int dst_x, unsigned int dst_y, 791e88f27b3Smrg unsigned int w, unsigned int h, enum e_g2d_op op) 792e88f27b3Smrg{ 793e88f27b3Smrg union g2d_point_val pt; 794e88f27b3Smrg union g2d_bitblt_cmd_val bitblt; 795e88f27b3Smrg union g2d_blend_func_val blend; 7963f012e29Smrg unsigned int gem_space; 7973f012e29Smrg unsigned int src_w, src_h, dst_w, dst_h; 7983f012e29Smrg 7993f012e29Smrg src_w = w; 8003f012e29Smrg src_h = h; 8013f012e29Smrg if (src_x + w > src->width) 8023f012e29Smrg src_w = src->width - src_x; 8033f012e29Smrg if (src_y + h > src->height) 8043f012e29Smrg src_h = src->height - src_y; 8053f012e29Smrg 8063f012e29Smrg dst_w = w; 8073f012e29Smrg dst_h = h; 8083f012e29Smrg if (dst_x + w > dst->width) 8093f012e29Smrg dst_w = dst->width - dst_x; 8103f012e29Smrg if (dst_y + h > dst->height) 8113f012e29Smrg dst_h = dst->height - dst_y; 8123f012e29Smrg 8133f012e29Smrg w = MIN(src_w, dst_w); 8143f012e29Smrg h = MIN(src_h, dst_h); 8153f012e29Smrg 8163f012e29Smrg if (w <= 0 || h <= 0) { 8173f012e29Smrg fprintf(stderr, MSG_PREFIX "invalid width or height.\n"); 8183f012e29Smrg return -EINVAL; 8193f012e29Smrg } 8203f012e29Smrg 8213f012e29Smrg if (!g2d_validate_select_mode(src->select_mode)) { 8223f012e29Smrg fprintf(stderr , MSG_PREFIX "invalid select mode for source.\n"); 8233f012e29Smrg return -EINVAL; 8243f012e29Smrg } 8253f012e29Smrg 8263f012e29Smrg if (!g2d_validate_blending_op(op)) { 8273f012e29Smrg fprintf(stderr , MSG_PREFIX "unsupported blending operation.\n"); 8283f012e29Smrg return -EINVAL; 8293f012e29Smrg } 8303f012e29Smrg 8313f012e29Smrg gem_space = src->select_mode == G2D_SELECT_MODE_NORMAL ? 2 : 1; 8323f012e29Smrg 8333f012e29Smrg if (g2d_check_space(ctx, 12, gem_space)) 8343f012e29Smrg return -ENOSPC; 835e88f27b3Smrg 836e88f27b3Smrg bitblt.val = 0; 837e88f27b3Smrg blend.val = 0; 838e88f27b3Smrg 839e88f27b3Smrg if (op == G2D_OP_SRC || op == G2D_OP_CLEAR) 840e88f27b3Smrg g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR); 841e88f27b3Smrg else 842e88f27b3Smrg g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL); 843e88f27b3Smrg 844e88f27b3Smrg g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode); 845e6188e58Smrg g2d_add_base_addr(ctx, dst, g2d_dst); 846e88f27b3Smrg g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride); 847e88f27b3Smrg 848e88f27b3Smrg g2d_add_cmd(ctx, SRC_SELECT_REG, src->select_mode); 849e88f27b3Smrg g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode); 850e88f27b3Smrg 851e88f27b3Smrg switch (src->select_mode) { 852e88f27b3Smrg case G2D_SELECT_MODE_NORMAL: 853e6188e58Smrg g2d_add_base_addr(ctx, src, g2d_src); 854e88f27b3Smrg g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride); 855e88f27b3Smrg break; 856e88f27b3Smrg case G2D_SELECT_MODE_FGCOLOR: 857e88f27b3Smrg g2d_add_cmd(ctx, FG_COLOR_REG, src->color); 858e88f27b3Smrg break; 859e88f27b3Smrg case G2D_SELECT_MODE_BGCOLOR: 860e88f27b3Smrg g2d_add_cmd(ctx, BG_COLOR_REG, src->color); 861e88f27b3Smrg break; 862e88f27b3Smrg } 863e88f27b3Smrg 864e88f27b3Smrg bitblt.data.alpha_blend_mode = G2D_ALPHA_BLEND_MODE_ENABLE; 865e88f27b3Smrg blend.val = g2d_get_blend_op(op); 866e88f27b3Smrg g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val); 867e88f27b3Smrg g2d_add_cmd(ctx, BLEND_FUNCTION_REG, blend.val); 868e88f27b3Smrg 869e88f27b3Smrg pt.data.x = src_x; 870e88f27b3Smrg pt.data.y = src_y; 871e88f27b3Smrg g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val); 872e88f27b3Smrg pt.data.x = src_x + w; 873e88f27b3Smrg pt.data.y = src_y + h; 874e88f27b3Smrg g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val); 875e88f27b3Smrg 876e88f27b3Smrg pt.data.x = dst_x; 877e88f27b3Smrg pt.data.y = dst_y; 878e88f27b3Smrg g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val); 879e88f27b3Smrg pt.data.x = dst_x + w; 880e88f27b3Smrg pt.data.y = dst_y + h; 881e88f27b3Smrg g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val); 882e88f27b3Smrg 883e6188e58Smrg return g2d_flush(ctx); 884e88f27b3Smrg} 885e88f27b3Smrg 886e6188e58Smrg/** 887e6188e58Smrg * g2d_scale_and_blend - apply scaling to source buffer and then blend to destination buffer 888e6188e58Smrg * 889e6188e58Smrg * @ctx: a pointer to g2d_context structure. 890e6188e58Smrg * @src: a pointer to g2d_image structure including image and buffer 891e6188e58Smrg * information to source. 892e6188e58Smrg * @dst: a pointer to g2d_image structure including image and buffer 893e6188e58Smrg * information to destination. 894e6188e58Smrg * @src_x: x start position to source buffer. 895e6188e58Smrg * @src_y: y start position to source buffer. 896e6188e58Smrg * @src_w: width value to source buffer. 897e6188e58Smrg * @src_h: height value to source buffer. 898e6188e58Smrg * @dst_x: x start position to destination buffer. 899e6188e58Smrg * @dst_y: y start position to destination buffer. 900e6188e58Smrg * @dst_w: width value to destination buffer. 901e6188e58Smrg * @dst_h: height value to destination buffer. 902e6188e58Smrg * @op: blend operation type. 903e6188e58Smrg */ 9047cdc0497Smrgdrm_public int 905e6188e58Smrgg2d_scale_and_blend(struct g2d_context *ctx, struct g2d_image *src, 906e6188e58Smrg struct g2d_image *dst, unsigned int src_x, unsigned int src_y, 907e6188e58Smrg unsigned int src_w, unsigned int src_h, unsigned int dst_x, 908e6188e58Smrg unsigned int dst_y, unsigned int dst_w, unsigned int dst_h, 909e6188e58Smrg enum e_g2d_op op) 910e6188e58Smrg{ 911e6188e58Smrg union g2d_point_val pt; 912e6188e58Smrg union g2d_bitblt_cmd_val bitblt; 913e6188e58Smrg union g2d_blend_func_val blend; 9143f012e29Smrg unsigned int scale, gem_space; 915e6188e58Smrg unsigned int scale_x, scale_y; 916e6188e58Smrg 9173f012e29Smrg if (src_w == dst_w && src_h == dst_h) 9183f012e29Smrg scale = 0; 9193f012e29Smrg else { 9203f012e29Smrg scale = 1; 9213f012e29Smrg scale_x = g2d_get_scaling(src_w, dst_w); 9223f012e29Smrg scale_y = g2d_get_scaling(src_h, dst_h); 9233f012e29Smrg } 9243f012e29Smrg 9253f012e29Smrg if (src_x + src_w > src->width) 9263f012e29Smrg src_w = src->width - src_x; 9273f012e29Smrg if (src_y + src_h > src->height) 9283f012e29Smrg src_h = src->height - src_y; 9293f012e29Smrg 9303f012e29Smrg if (dst_x + dst_w > dst->width) 9313f012e29Smrg dst_w = dst->width - dst_x; 9323f012e29Smrg if (dst_y + dst_h > dst->height) 9333f012e29Smrg dst_h = dst->height - dst_y; 9343f012e29Smrg 9353f012e29Smrg if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) { 9363f012e29Smrg fprintf(stderr, MSG_PREFIX "invalid width or height.\n"); 9373f012e29Smrg return -EINVAL; 9383f012e29Smrg } 9393f012e29Smrg 9403f012e29Smrg if (!g2d_validate_select_mode(src->select_mode)) { 9413f012e29Smrg fprintf(stderr , MSG_PREFIX "invalid select mode for source.\n"); 9423f012e29Smrg return -EINVAL; 9433f012e29Smrg } 9443f012e29Smrg 9453f012e29Smrg if (!g2d_validate_blending_op(op)) { 9463f012e29Smrg fprintf(stderr , MSG_PREFIX "unsupported blending operation.\n"); 9473f012e29Smrg return -EINVAL; 9483f012e29Smrg } 9493f012e29Smrg 9503f012e29Smrg gem_space = src->select_mode == G2D_SELECT_MODE_NORMAL ? 2 : 1; 9513f012e29Smrg 9523f012e29Smrg if (g2d_check_space(ctx, 12 + scale * 3, gem_space)) 9533f012e29Smrg return -ENOSPC; 9543f012e29Smrg 955e6188e58Smrg bitblt.val = 0; 956e6188e58Smrg blend.val = 0; 957e6188e58Smrg 958e6188e58Smrg if (op == G2D_OP_SRC || op == G2D_OP_CLEAR) 959e6188e58Smrg g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR); 960e6188e58Smrg else 961e6188e58Smrg g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL); 962e6188e58Smrg 963e6188e58Smrg g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode); 9643f012e29Smrg g2d_add_base_addr(ctx, dst, g2d_dst); 965e6188e58Smrg g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride); 966e6188e58Smrg 967e6188e58Smrg g2d_add_cmd(ctx, SRC_SELECT_REG, src->select_mode); 968e6188e58Smrg g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode); 969e6188e58Smrg 970e6188e58Smrg switch (src->select_mode) { 971e6188e58Smrg case G2D_SELECT_MODE_NORMAL: 9723f012e29Smrg g2d_add_base_addr(ctx, src, g2d_src); 973e6188e58Smrg g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride); 974e6188e58Smrg break; 975e6188e58Smrg case G2D_SELECT_MODE_FGCOLOR: 976e6188e58Smrg g2d_add_cmd(ctx, FG_COLOR_REG, src->color); 977e6188e58Smrg break; 978e6188e58Smrg case G2D_SELECT_MODE_BGCOLOR: 979e6188e58Smrg g2d_add_cmd(ctx, BG_COLOR_REG, src->color); 980e6188e58Smrg break; 981e6188e58Smrg } 982e6188e58Smrg 983e6188e58Smrg if (scale) { 984e6188e58Smrg g2d_add_cmd(ctx, SRC_SCALE_CTRL_REG, G2D_SCALE_MODE_BILINEAR); 985e6188e58Smrg g2d_add_cmd(ctx, SRC_XSCALE_REG, scale_x); 986e6188e58Smrg g2d_add_cmd(ctx, SRC_YSCALE_REG, scale_y); 987e6188e58Smrg } 988e6188e58Smrg 989e6188e58Smrg bitblt.data.alpha_blend_mode = G2D_ALPHA_BLEND_MODE_ENABLE; 990e6188e58Smrg blend.val = g2d_get_blend_op(op); 991e6188e58Smrg g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val); 992e6188e58Smrg g2d_add_cmd(ctx, BLEND_FUNCTION_REG, blend.val); 993e6188e58Smrg 994e6188e58Smrg pt.data.x = src_x; 995e6188e58Smrg pt.data.y = src_y; 996e6188e58Smrg g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val); 997e6188e58Smrg pt.data.x = src_x + src_w; 998e6188e58Smrg pt.data.y = src_y + src_h; 999e6188e58Smrg g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val); 1000e6188e58Smrg 1001e6188e58Smrg pt.data.x = dst_x; 1002e6188e58Smrg pt.data.y = dst_y; 1003e6188e58Smrg g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val); 1004e6188e58Smrg pt.data.x = dst_x + dst_w; 1005e6188e58Smrg pt.data.y = dst_y + dst_h; 1006e6188e58Smrg g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val); 1007e6188e58Smrg 1008e6188e58Smrg return g2d_flush(ctx); 1009e6188e58Smrg} 1010