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