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