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