exynos_fimg2d.c revision 3f012e29
1e88f27b3Smrg/*
2e88f27b3Smrg * Copyright (C) 2013 Samsung Electronics Co.Ltd
3e88f27b3Smrg * Authors:
4e88f27b3Smrg *	Inki Dae <inki.dae@samsung.com>
5e88f27b3Smrg *
6e88f27b3Smrg * This program is free software; you can redistribute  it and/or modify it
7e88f27b3Smrg * under  the terms of  the GNU General  Public License as published by the
8e88f27b3Smrg * Free Software Foundation;  either version 2 of the  License, or (at your
9e88f27b3Smrg * option) any later version.
10e88f27b3Smrg *
11e88f27b3Smrg */
12e88f27b3Smrg
13e88f27b3Smrg#ifdef HAVE_CONFIG_H
14e88f27b3Smrg#include "config.h"
15e88f27b3Smrg#endif
16e88f27b3Smrg
17e88f27b3Smrg#include <stdlib.h>
18e88f27b3Smrg#include <stdio.h>
19e88f27b3Smrg#include <string.h>
20e88f27b3Smrg#include <errno.h>
213f012e29Smrg#include <assert.h>
22e88f27b3Smrg
23e88f27b3Smrg#include <sys/mman.h>
24e88f27b3Smrg#include <linux/stddef.h>
25e88f27b3Smrg
26e88f27b3Smrg#include <xf86drm.h>
27e88f27b3Smrg
28e6188e58Smrg#include "libdrm_macros.h"
29e88f27b3Smrg#include "exynos_drm.h"
30e88f27b3Smrg#include "fimg2d_reg.h"
31e6188e58Smrg#include "exynos_fimg2d.h"
32e88f27b3Smrg
33e88f27b3Smrg#define		SET_BF(val, sc, si, scsa, scda, dc, di, dcsa, dcda) \
34e88f27b3Smrg			val.data.src_coeff = sc;		\
35e88f27b3Smrg			val.data.inv_src_color_coeff = si;	\
36e88f27b3Smrg			val.data.src_coeff_src_a = scsa;	\
37e88f27b3Smrg			val.data.src_coeff_dst_a = scda;	\
38e88f27b3Smrg			val.data.dst_coeff = dc;		\
39e88f27b3Smrg			val.data.inv_dst_color_coeff = di;	\
40e88f27b3Smrg			val.data.dst_coeff_src_a = dcsa;	\
41e88f27b3Smrg			val.data.dst_coeff_dst_a = dcda;
42e88f27b3Smrg
43e88f27b3Smrg#define MIN(a, b)	((a) < (b) ? (a) : (b))
44e88f27b3Smrg
453f012e29Smrg#define MSG_PREFIX "exynos/fimg2d: "
463f012e29Smrg
473f012e29Smrg#define G2D_MAX_CMD_NR		64
483f012e29Smrg#define G2D_MAX_GEM_CMD_NR	64
493f012e29Smrg#define G2D_MAX_CMD_LIST_NR	64
503f012e29Smrg
513f012e29Smrgstruct g2d_context {
523f012e29Smrg	int				fd;
533f012e29Smrg	unsigned int			major;
543f012e29Smrg	unsigned int			minor;
553f012e29Smrg	struct drm_exynos_g2d_cmd	cmd[G2D_MAX_CMD_NR];
563f012e29Smrg	struct drm_exynos_g2d_cmd	cmd_buf[G2D_MAX_GEM_CMD_NR];
573f012e29Smrg	unsigned int			cmd_nr;
583f012e29Smrg	unsigned int			cmd_buf_nr;
593f012e29Smrg	unsigned int			cmdlist_nr;
603f012e29Smrg	void				*event_userdata;
613f012e29Smrg};
623f012e29Smrg
63e6188e58Smrgenum g2d_base_addr_reg {
64e6188e58Smrg	g2d_dst = 0,
65e6188e58Smrg	g2d_src
66e6188e58Smrg};
67e6188e58Smrg
683f012e29Smrgenum e_g2d_dir_mode {
693f012e29Smrg	G2D_DIR_MODE_POSITIVE = 0,
703f012e29Smrg	G2D_DIR_MODE_NEGATIVE = 1
713f012e29Smrg};
723f012e29Smrg
733f012e29Smrgunion g2d_direction_val {
743f012e29Smrg	unsigned int val[2];
753f012e29Smrg	struct {
763f012e29Smrg		/* SRC_MSK_DIRECT_REG [0:1] (source) */
773f012e29Smrg		enum e_g2d_dir_mode		src_x_direction:1;
783f012e29Smrg		enum e_g2d_dir_mode		src_y_direction:1;
793f012e29Smrg
803f012e29Smrg		/* SRC_MSK_DIRECT_REG [2:3] */
813f012e29Smrg		unsigned int			reversed1:2;
823f012e29Smrg
833f012e29Smrg		/* SRC_MSK_DIRECT_REG [4:5] (mask) */
843f012e29Smrg		enum e_g2d_dir_mode		mask_x_direction:1;
853f012e29Smrg		enum e_g2d_dir_mode		mask_y_direction:1;
863f012e29Smrg
873f012e29Smrg		/* SRC_MSK_DIRECT_REG [6:31] */
883f012e29Smrg		unsigned int			padding1:26;
893f012e29Smrg
903f012e29Smrg		/* DST_PAT_DIRECT_REG [0:1] (destination) */
913f012e29Smrg		enum e_g2d_dir_mode		dst_x_direction:1;
923f012e29Smrg		enum e_g2d_dir_mode		dst_y_direction:1;
933f012e29Smrg
943f012e29Smrg		/* DST_PAT_DIRECT_REG [2:3] */
953f012e29Smrg		unsigned int			reversed2:2;
963f012e29Smrg
973f012e29Smrg		/* DST_PAT_DIRECT_REG [4:5] (pattern) */
983f012e29Smrg		enum e_g2d_dir_mode		pat_x_direction:1;
993f012e29Smrg		enum e_g2d_dir_mode		pat_y_direction:1;
1003f012e29Smrg
1013f012e29Smrg		/* DST_PAT_DIRECT_REG [6:31] */
1023f012e29Smrg		unsigned int			padding2:26;
1033f012e29Smrg	} data;
1043f012e29Smrg};
1053f012e29Smrg
106e6188e58Smrgstatic unsigned int g2d_get_scaling(unsigned int src, unsigned int dst)
107e6188e58Smrg{
108e6188e58Smrg	/*
109e6188e58Smrg	 * The G2D hw scaling factor is a normalized inverse of the scaling factor.
110e6188e58Smrg	 * For example: When source width is 100 and destination width is 200
111e6188e58Smrg	 * (scaling of 2x), then the hw factor is NC * 100 / 200.
112e6188e58Smrg	 * The normalization factor (NC) is 2^16 = 0x10000.
113e6188e58Smrg	 */
114e6188e58Smrg
115e6188e58Smrg	return ((src << 16) / dst);
116e6188e58Smrg}
117e6188e58Smrg
118e88f27b3Smrgstatic unsigned int g2d_get_blend_op(enum e_g2d_op op)
119e88f27b3Smrg{
120e88f27b3Smrg	union g2d_blend_func_val val;
121e88f27b3Smrg
122e88f27b3Smrg	val.val = 0;
123e88f27b3Smrg
1243f012e29Smrg	/*
1253f012e29Smrg	 * The switch statement is missing the default branch since
1263f012e29Smrg	 * we assume that the caller checks the blending operation
1273f012e29Smrg	 * via g2d_validate_blending_op() first.
1283f012e29Smrg	 */
129e88f27b3Smrg	switch (op) {
130e88f27b3Smrg	case G2D_OP_CLEAR:
131e88f27b3Smrg	case G2D_OP_DISJOINT_CLEAR:
132e88f27b3Smrg	case G2D_OP_CONJOINT_CLEAR:
133e88f27b3Smrg		SET_BF(val, G2D_COEFF_MODE_ZERO, 0, 0, 0, G2D_COEFF_MODE_ZERO,
134e88f27b3Smrg				0, 0, 0);
135e88f27b3Smrg		break;
136e88f27b3Smrg	case G2D_OP_SRC:
137e88f27b3Smrg	case G2D_OP_DISJOINT_SRC:
138e88f27b3Smrg	case G2D_OP_CONJOINT_SRC:
139e88f27b3Smrg		SET_BF(val, G2D_COEFF_MODE_ONE, 0, 0, 0, G2D_COEFF_MODE_ZERO,
140e88f27b3Smrg				0, 0, 0);
141e88f27b3Smrg		break;
142e88f27b3Smrg	case G2D_OP_DST:
143e88f27b3Smrg	case G2D_OP_DISJOINT_DST:
144e88f27b3Smrg	case G2D_OP_CONJOINT_DST:
145e88f27b3Smrg		SET_BF(val, G2D_COEFF_MODE_ZERO, 0, 0, 0, G2D_COEFF_MODE_ONE,
146e88f27b3Smrg				0, 0, 0);
147e88f27b3Smrg		break;
148e88f27b3Smrg	case G2D_OP_OVER:
149e88f27b3Smrg		SET_BF(val, G2D_COEFF_MODE_ONE, 0, 0, 0,
150e88f27b3Smrg				G2D_COEFF_MODE_SRC_ALPHA, 1, 0, 0);
151e88f27b3Smrg		break;
152e6188e58Smrg	case G2D_OP_INTERPOLATE:
153e6188e58Smrg		SET_BF(val, G2D_COEFF_MODE_SRC_ALPHA, 0, 0, 0,
154e6188e58Smrg				G2D_COEFF_MODE_SRC_ALPHA, 1, 0, 0);
155e6188e58Smrg		break;
156e88f27b3Smrg	}
157e88f27b3Smrg
158e88f27b3Smrg	return val.val;
159e88f27b3Smrg}
160e88f27b3Smrg
1613f012e29Smrg/*
1623f012e29Smrg * g2d_check_space - check if command buffers have enough space left.
1633f012e29Smrg *
1643f012e29Smrg * @ctx: a pointer to g2d_context structure.
1653f012e29Smrg * @num_cmds: number of (regular) commands.
1663f012e29Smrg * @num_gem_cmds: number of GEM commands.
1673f012e29Smrg */
1683f012e29Smrgstatic unsigned int g2d_check_space(const struct g2d_context *ctx,
1693f012e29Smrg	unsigned int num_cmds, unsigned int num_gem_cmds)
1703f012e29Smrg{
1713f012e29Smrg	if (ctx->cmd_nr + num_cmds >= G2D_MAX_CMD_NR ||
1723f012e29Smrg	    ctx->cmd_buf_nr + num_gem_cmds >= G2D_MAX_GEM_CMD_NR)
1733f012e29Smrg		return 1;
1743f012e29Smrg	else
1753f012e29Smrg		return 0;
1763f012e29Smrg}
1773f012e29Smrg
1783f012e29Smrg/*
1793f012e29Smrg * g2d_validate_select_mode - validate select mode.
1803f012e29Smrg *
1813f012e29Smrg * @mode: the mode to validate
1823f012e29Smrg *
1833f012e29Smrg * Returns zero for an invalid mode and one otherwise.
1843f012e29Smrg */
1853f012e29Smrgstatic int g2d_validate_select_mode(
1863f012e29Smrg	enum e_g2d_select_mode mode)
1873f012e29Smrg{
1883f012e29Smrg	switch (mode) {
1893f012e29Smrg	case G2D_SELECT_MODE_NORMAL:
1903f012e29Smrg	case G2D_SELECT_MODE_FGCOLOR:
1913f012e29Smrg	case G2D_SELECT_MODE_BGCOLOR:
1923f012e29Smrg		return 1;
1933f012e29Smrg	}
1943f012e29Smrg
1953f012e29Smrg	return 0;
1963f012e29Smrg}
1973f012e29Smrg
1983f012e29Smrg/*
1993f012e29Smrg * g2d_validate_blending_op - validate blending operation.
2003f012e29Smrg *
2013f012e29Smrg * @operation: the operation to validate
2023f012e29Smrg *
2033f012e29Smrg * Returns zero for an invalid mode and one otherwise.
2043f012e29Smrg */
2053f012e29Smrgstatic int g2d_validate_blending_op(
2063f012e29Smrg	enum e_g2d_op operation)
2073f012e29Smrg{
2083f012e29Smrg	switch (operation) {
2093f012e29Smrg	case G2D_OP_CLEAR:
2103f012e29Smrg	case G2D_OP_SRC:
2113f012e29Smrg	case G2D_OP_DST:
2123f012e29Smrg	case G2D_OP_OVER:
2133f012e29Smrg	case G2D_OP_INTERPOLATE:
2143f012e29Smrg	case G2D_OP_DISJOINT_CLEAR:
2153f012e29Smrg	case G2D_OP_DISJOINT_SRC:
2163f012e29Smrg	case G2D_OP_DISJOINT_DST:
2173f012e29Smrg	case G2D_OP_CONJOINT_CLEAR:
2183f012e29Smrg	case G2D_OP_CONJOINT_SRC:
2193f012e29Smrg	case G2D_OP_CONJOINT_DST:
2203f012e29Smrg		return 1;
2213f012e29Smrg	}
2223f012e29Smrg
2233f012e29Smrg	return 0;
2243f012e29Smrg}
2253f012e29Smrg
226e88f27b3Smrg/*
227e88f27b3Smrg * g2d_add_cmd - set given command and value to user side command buffer.
228e88f27b3Smrg *
229e88f27b3Smrg * @ctx: a pointer to g2d_context structure.
230e88f27b3Smrg * @cmd: command data.
231e88f27b3Smrg * @value: value data.
2323f012e29Smrg *
2333f012e29Smrg * The caller has to make sure that the commands buffers have enough space
2343f012e29Smrg * left to hold the command. Use g2d_check_space() to ensure this.
235e88f27b3Smrg */
2363f012e29Smrgstatic void g2d_add_cmd(struct g2d_context *ctx, unsigned long cmd,
237e88f27b3Smrg			unsigned long value)
238e88f27b3Smrg{
239e88f27b3Smrg	switch (cmd & ~(G2D_BUF_USERPTR)) {
240e88f27b3Smrg	case SRC_BASE_ADDR_REG:
241e88f27b3Smrg	case SRC_PLANE2_BASE_ADDR_REG:
242e88f27b3Smrg	case DST_BASE_ADDR_REG:
243e88f27b3Smrg	case DST_PLANE2_BASE_ADDR_REG:
244e88f27b3Smrg	case PAT_BASE_ADDR_REG:
245e88f27b3Smrg	case MASK_BASE_ADDR_REG:
2463f012e29Smrg		assert(ctx->cmd_buf_nr < G2D_MAX_GEM_CMD_NR);
247e88f27b3Smrg
248e88f27b3Smrg		ctx->cmd_buf[ctx->cmd_buf_nr].offset = cmd;
249e88f27b3Smrg		ctx->cmd_buf[ctx->cmd_buf_nr].data = value;
250e88f27b3Smrg		ctx->cmd_buf_nr++;
251e88f27b3Smrg		break;
252e88f27b3Smrg	default:
2533f012e29Smrg		assert(ctx->cmd_nr < G2D_MAX_CMD_NR);
254e88f27b3Smrg
255e88f27b3Smrg		ctx->cmd[ctx->cmd_nr].offset = cmd;
256e88f27b3Smrg		ctx->cmd[ctx->cmd_nr].data = value;
257e88f27b3Smrg		ctx->cmd_nr++;
258e88f27b3Smrg		break;
259e88f27b3Smrg	}
260e6188e58Smrg}
261e6188e58Smrg
262e6188e58Smrg/*
263e6188e58Smrg * g2d_add_base_addr - helper function to set dst/src base address register.
264e6188e58Smrg *
265e6188e58Smrg * @ctx: a pointer to g2d_context structure.
266e6188e58Smrg * @img: a pointer to the dst/src g2d_image structure.
267e6188e58Smrg * @reg: the register that should be set.
268e6188e58Smrg */
269e6188e58Smrgstatic void g2d_add_base_addr(struct g2d_context *ctx, struct g2d_image *img,
270e6188e58Smrg			enum g2d_base_addr_reg reg)
271e6188e58Smrg{
272e6188e58Smrg	const unsigned long cmd = (reg == g2d_dst) ?
273e6188e58Smrg		DST_BASE_ADDR_REG : SRC_BASE_ADDR_REG;
274e6188e58Smrg
275e6188e58Smrg	if (img->buf_type == G2D_IMGBUF_USERPTR)
276e6188e58Smrg		g2d_add_cmd(ctx, cmd | G2D_BUF_USERPTR,
277e6188e58Smrg				(unsigned long)&img->user_ptr[0]);
278e6188e58Smrg	else
279e6188e58Smrg		g2d_add_cmd(ctx, cmd, img->bo[0]);
280e88f27b3Smrg}
281e88f27b3Smrg
2823f012e29Smrg/*
2833f012e29Smrg * g2d_set_direction - setup direction register (useful for overlapping blits).
2843f012e29Smrg *
2853f012e29Smrg * @ctx: a pointer to g2d_context structure.
2863f012e29Smrg * @dir: a pointer to the g2d_direction_val structure.
2873f012e29Smrg */
2883f012e29Smrgstatic void g2d_set_direction(struct g2d_context *ctx,
2893f012e29Smrg			const union g2d_direction_val *dir)
2903f012e29Smrg{
2913f012e29Smrg	g2d_add_cmd(ctx, SRC_MASK_DIRECT_REG, dir->val[0]);
2923f012e29Smrg	g2d_add_cmd(ctx, DST_PAT_DIRECT_REG, dir->val[1]);
2933f012e29Smrg}
2943f012e29Smrg
295e88f27b3Smrg/*
296e88f27b3Smrg * g2d_reset - reset fimg2d hardware.
297e88f27b3Smrg *
298e88f27b3Smrg * @ctx: a pointer to g2d_context structure.
299e88f27b3Smrg *
300e88f27b3Smrg */
301e88f27b3Smrgstatic void g2d_reset(struct g2d_context *ctx)
302e88f27b3Smrg{
303e88f27b3Smrg	ctx->cmd_nr = 0;
304e88f27b3Smrg	ctx->cmd_buf_nr = 0;
305e88f27b3Smrg
306e88f27b3Smrg	g2d_add_cmd(ctx, SOFT_RESET_REG, 0x01);
307e88f27b3Smrg}
308e88f27b3Smrg
309e88f27b3Smrg/*
310e6188e58Smrg * g2d_flush - submit all commands and values in user side command buffer
311e88f27b3Smrg *		to command queue aware of fimg2d dma.
312e88f27b3Smrg *
313e88f27b3Smrg * @ctx: a pointer to g2d_context structure.
314e88f27b3Smrg *
315e88f27b3Smrg * This function should be called after all commands and values to user
316e6188e58Smrg * side command buffer are set. It submits that buffer to the kernel side driver.
317e88f27b3Smrg */
318e88f27b3Smrgstatic int g2d_flush(struct g2d_context *ctx)
319e88f27b3Smrg{
320e88f27b3Smrg	int ret;
321e6188e58Smrg	struct drm_exynos_g2d_set_cmdlist cmdlist = {0};
322e88f27b3Smrg
323e6188e58Smrg	if (ctx->cmd_nr == 0 && ctx->cmd_buf_nr == 0)
3243f012e29Smrg		return 0;
325e88f27b3Smrg
326e88f27b3Smrg	if (ctx->cmdlist_nr >= G2D_MAX_CMD_LIST_NR) {
3273f012e29Smrg		fprintf(stderr, MSG_PREFIX "command list overflow.\n");
328e88f27b3Smrg		return -EINVAL;
329e88f27b3Smrg	}
330e88f27b3Smrg
331baaff307Smrg	cmdlist.cmd = (uint64_t)(uintptr_t)&ctx->cmd[0];
332baaff307Smrg	cmdlist.cmd_buf = (uint64_t)(uintptr_t)&ctx->cmd_buf[0];
333e88f27b3Smrg	cmdlist.cmd_nr = ctx->cmd_nr;
334e88f27b3Smrg	cmdlist.cmd_buf_nr = ctx->cmd_buf_nr;
3353f012e29Smrg
3363f012e29Smrg	if (ctx->event_userdata) {
3373f012e29Smrg		cmdlist.event_type = G2D_EVENT_NONSTOP;
3383f012e29Smrg		cmdlist.user_data = (uint64_t)(uintptr_t)(ctx->event_userdata);
3393f012e29Smrg		ctx->event_userdata = NULL;
3403f012e29Smrg	} else {
3413f012e29Smrg		cmdlist.event_type = G2D_EVENT_NOT;
3423f012e29Smrg		cmdlist.user_data = 0;
3433f012e29Smrg	}
344e88f27b3Smrg
345e88f27b3Smrg	ctx->cmd_nr = 0;
346e88f27b3Smrg	ctx->cmd_buf_nr = 0;
347e88f27b3Smrg
348e88f27b3Smrg	ret = drmIoctl(ctx->fd, DRM_IOCTL_EXYNOS_G2D_SET_CMDLIST, &cmdlist);
349e88f27b3Smrg	if (ret < 0) {
3503f012e29Smrg		fprintf(stderr, MSG_PREFIX "failed to set cmdlist.\n");
351e88f27b3Smrg		return ret;
352e88f27b3Smrg	}
353e88f27b3Smrg
354e88f27b3Smrg	ctx->cmdlist_nr++;
355e88f27b3Smrg
356e88f27b3Smrg	return ret;
357e88f27b3Smrg}
358e88f27b3Smrg
359e88f27b3Smrg/**
360e88f27b3Smrg * g2d_init - create a new g2d context and get hardware version.
361e88f27b3Smrg *
362e6188e58Smrg * fd: a file descriptor to an opened drm device.
363e88f27b3Smrg */
364e6188e58Smrgstruct g2d_context *g2d_init(int fd)
365e88f27b3Smrg{
366e88f27b3Smrg	struct drm_exynos_g2d_get_ver ver;
367e88f27b3Smrg	struct g2d_context *ctx;
368e88f27b3Smrg	int ret;
369e88f27b3Smrg
370e88f27b3Smrg	ctx = calloc(1, sizeof(*ctx));
371e88f27b3Smrg	if (!ctx) {
3723f012e29Smrg		fprintf(stderr, MSG_PREFIX "failed to allocate context.\n");
373e88f27b3Smrg		return NULL;
374e88f27b3Smrg	}
375e88f27b3Smrg
376e88f27b3Smrg	ctx->fd = fd;
377e88f27b3Smrg
378e88f27b3Smrg	ret = drmIoctl(fd, DRM_IOCTL_EXYNOS_G2D_GET_VER, &ver);
379e88f27b3Smrg	if (ret < 0) {
3803f012e29Smrg		fprintf(stderr, MSG_PREFIX "failed to get version.\n");
381e88f27b3Smrg		free(ctx);
382e88f27b3Smrg		return NULL;
383e88f27b3Smrg	}
384e88f27b3Smrg
385e88f27b3Smrg	ctx->major = ver.major;
386e88f27b3Smrg	ctx->minor = ver.minor;
387e88f27b3Smrg
3883f012e29Smrg	printf(MSG_PREFIX "G2D version (%d.%d).\n", ctx->major, ctx->minor);
389e88f27b3Smrg	return ctx;
390e88f27b3Smrg}
391e88f27b3Smrg
392e6188e58Smrgvoid g2d_fini(struct g2d_context *ctx)
393e88f27b3Smrg{
394e6188e58Smrg	free(ctx);
395e88f27b3Smrg}
396e88f27b3Smrg
3973f012e29Smrg/**
3983f012e29Smrg * g2d_config_event - setup userdata configuration for a g2d event.
3993f012e29Smrg *		The next invocation of a g2d call (e.g. g2d_solid_fill) is
4003f012e29Smrg *		then going to flag the command buffer as 'nonstop'.
4013f012e29Smrg *		Completion of the command buffer execution can then be
4023f012e29Smrg *		determined by using drmHandleEvent on the DRM fd.
4033f012e29Smrg *		The userdata is 'consumed' in the process.
4043f012e29Smrg *
4053f012e29Smrg * @ctx: a pointer to g2d_context structure.
4063f012e29Smrg * @userdata: a pointer to the user data
4073f012e29Smrg */
4083f012e29Smrgvoid g2d_config_event(struct g2d_context *ctx, void *userdata)
4093f012e29Smrg{
4103f012e29Smrg	ctx->event_userdata = userdata;
4113f012e29Smrg}
4123f012e29Smrg
413e88f27b3Smrg/**
414e88f27b3Smrg * g2d_exec - start the dma to process all commands summited by g2d_flush().
415e88f27b3Smrg *
416e88f27b3Smrg * @ctx: a pointer to g2d_context structure.
417e88f27b3Smrg */
418e6188e58Smrgint g2d_exec(struct g2d_context *ctx)
419e88f27b3Smrg{
420e88f27b3Smrg	struct drm_exynos_g2d_exec exec;
421e88f27b3Smrg	int ret;
422e88f27b3Smrg
423e88f27b3Smrg	if (ctx->cmdlist_nr == 0)
424e88f27b3Smrg		return -EINVAL;
425e88f27b3Smrg
426e88f27b3Smrg	exec.async = 0;
427e88f27b3Smrg
428e88f27b3Smrg	ret = drmIoctl(ctx->fd, DRM_IOCTL_EXYNOS_G2D_EXEC, &exec);
429e88f27b3Smrg	if (ret < 0) {
4303f012e29Smrg		fprintf(stderr, MSG_PREFIX "failed to execute.\n");
431e88f27b3Smrg		return ret;
432e88f27b3Smrg	}
433e88f27b3Smrg
434e88f27b3Smrg	ctx->cmdlist_nr = 0;
435e88f27b3Smrg
436e88f27b3Smrg	return ret;
437e88f27b3Smrg}
438e88f27b3Smrg
439e88f27b3Smrg/**
440e88f27b3Smrg * g2d_solid_fill - fill given buffer with given color data.
441e88f27b3Smrg *
442e88f27b3Smrg * @ctx: a pointer to g2d_context structure.
443e88f27b3Smrg * @img: a pointer to g2d_image structure including image and buffer
444e88f27b3Smrg *	information.
445e88f27b3Smrg * @x: x start position to buffer filled with given color data.
446e88f27b3Smrg * @y: y start position to buffer filled with given color data.
447e88f27b3Smrg * @w: width value to buffer filled with given color data.
448e88f27b3Smrg * @h: height value to buffer filled with given color data.
449e88f27b3Smrg */
450e6188e58Smrgint
451baaff307Smrgg2d_solid_fill(struct g2d_context *ctx, struct g2d_image *img,
452e88f27b3Smrg			unsigned int x, unsigned int y, unsigned int w,
453e88f27b3Smrg			unsigned int h)
454e88f27b3Smrg{
455e88f27b3Smrg	union g2d_bitblt_cmd_val bitblt;
456e88f27b3Smrg	union g2d_point_val pt;
457e88f27b3Smrg
4583f012e29Smrg	if (g2d_check_space(ctx, 7, 1))
4593f012e29Smrg		return -ENOSPC;
4603f012e29Smrg
461e88f27b3Smrg	g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL);
462e88f27b3Smrg	g2d_add_cmd(ctx, DST_COLOR_MODE_REG, img->color_mode);
463e6188e58Smrg	g2d_add_base_addr(ctx, img, g2d_dst);
464e88f27b3Smrg	g2d_add_cmd(ctx, DST_STRIDE_REG, img->stride);
465e88f27b3Smrg
466e88f27b3Smrg	if (x + w > img->width)
467e88f27b3Smrg		w = img->width - x;
468e88f27b3Smrg	if (y + h > img->height)
469e88f27b3Smrg		h = img->height - y;
470e88f27b3Smrg
471e88f27b3Smrg	pt.data.x = x;
472e88f27b3Smrg	pt.data.y = y;
473e88f27b3Smrg	g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
474e88f27b3Smrg
475e88f27b3Smrg	pt.data.x = x + w;
476e88f27b3Smrg	pt.data.y = y + h;
477e88f27b3Smrg	g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
478e88f27b3Smrg
479e88f27b3Smrg	g2d_add_cmd(ctx, SF_COLOR_REG, img->color);
480e88f27b3Smrg
481e88f27b3Smrg	bitblt.val = 0;
482e88f27b3Smrg	bitblt.data.fast_solid_color_fill_en = 1;
483e88f27b3Smrg	g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val);
484e88f27b3Smrg
485e6188e58Smrg	return g2d_flush(ctx);
486e88f27b3Smrg}
487e88f27b3Smrg
488e88f27b3Smrg/**
489e88f27b3Smrg * g2d_copy - copy contents in source buffer to destination buffer.
490e88f27b3Smrg *
491e88f27b3Smrg * @ctx: a pointer to g2d_context structure.
492e88f27b3Smrg * @src: a pointer to g2d_image structure including image and buffer
493e88f27b3Smrg *	information to source.
494e88f27b3Smrg * @dst: a pointer to g2d_image structure including image and buffer
495e88f27b3Smrg *	information to destination.
496e88f27b3Smrg * @src_x: x start position to source buffer.
497e88f27b3Smrg * @src_y: y start position to source buffer.
498e88f27b3Smrg * @dst_x: x start position to destination buffer.
499e88f27b3Smrg * @dst_y: y start position to destination buffer.
500e88f27b3Smrg * @w: width value to source and destination buffers.
501e88f27b3Smrg * @h: height value to source and destination buffers.
502e88f27b3Smrg */
503e6188e58Smrgint
504baaff307Smrgg2d_copy(struct g2d_context *ctx, struct g2d_image *src,
505e88f27b3Smrg		struct g2d_image *dst, unsigned int src_x, unsigned int src_y,
506e88f27b3Smrg		unsigned int dst_x, unsigned dst_y, unsigned int w,
507e88f27b3Smrg		unsigned int h)
508e88f27b3Smrg{
509e88f27b3Smrg	union g2d_rop4_val rop4;
510e88f27b3Smrg	union g2d_point_val pt;
5113f012e29Smrg	unsigned int src_w, src_h, dst_w, dst_h;
5123f012e29Smrg
5133f012e29Smrg	src_w = w;
5143f012e29Smrg	src_h = h;
5153f012e29Smrg	if (src_x + src->width > w)
5163f012e29Smrg		src_w = src->width - src_x;
5173f012e29Smrg	if (src_y + src->height > h)
5183f012e29Smrg		src_h = src->height - src_y;
5193f012e29Smrg
5203f012e29Smrg	dst_w = w;
5213f012e29Smrg	dst_h = w;
5223f012e29Smrg	if (dst_x + dst->width > w)
5233f012e29Smrg		dst_w = dst->width - dst_x;
5243f012e29Smrg	if (dst_y + dst->height > h)
5253f012e29Smrg		dst_h = dst->height - dst_y;
5263f012e29Smrg
5273f012e29Smrg	w = MIN(src_w, dst_w);
5283f012e29Smrg	h = MIN(src_h, dst_h);
5293f012e29Smrg
5303f012e29Smrg	if (w <= 0 || h <= 0) {
5313f012e29Smrg		fprintf(stderr, MSG_PREFIX "invalid width or height.\n");
5323f012e29Smrg		return -EINVAL;
5333f012e29Smrg	}
5343f012e29Smrg
5353f012e29Smrg	if (g2d_check_space(ctx, 11, 2))
5363f012e29Smrg		return -ENOSPC;
537e88f27b3Smrg
538e88f27b3Smrg	g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
539e88f27b3Smrg	g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode);
540e6188e58Smrg	g2d_add_base_addr(ctx, dst, g2d_dst);
541e88f27b3Smrg	g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride);
542e88f27b3Smrg
543e88f27b3Smrg	g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL);
544e88f27b3Smrg	g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode);
545e6188e58Smrg	g2d_add_base_addr(ctx, src, g2d_src);
546e88f27b3Smrg	g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride);
547e88f27b3Smrg
5483f012e29Smrg	pt.data.x = src_x;
5493f012e29Smrg	pt.data.y = src_y;
5503f012e29Smrg	g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
5513f012e29Smrg	pt.data.x = src_x + w;
5523f012e29Smrg	pt.data.y = src_y + h;
5533f012e29Smrg	g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
5543f012e29Smrg
5553f012e29Smrg	pt.data.x = dst_x;
5563f012e29Smrg	pt.data.y = dst_y;
5573f012e29Smrg	g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
5583f012e29Smrg	pt.data.x = dst_x + w;
5593f012e29Smrg	pt.data.y = dst_y + h;
5603f012e29Smrg	g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
5613f012e29Smrg
5623f012e29Smrg	rop4.val = 0;
5633f012e29Smrg	rop4.data.unmasked_rop3 = G2D_ROP3_SRC;
5643f012e29Smrg	g2d_add_cmd(ctx, ROP4_REG, rop4.val);
5653f012e29Smrg
5663f012e29Smrg	return g2d_flush(ctx);
5673f012e29Smrg}
5683f012e29Smrg
5693f012e29Smrg/**
5703f012e29Smrg * g2d_move - copy content inside single buffer.
5713f012e29Smrg *	Similar to libc's memmove() this copies a rectangular
5723f012e29Smrg *	region of the provided buffer to another location, while
5733f012e29Smrg *	properly handling the situation where source and
5743f012e29Smrg *	destination rectangle overlap.
5753f012e29Smrg *
5763f012e29Smrg * @ctx: a pointer to g2d_context structure.
5773f012e29Smrg * @img: a pointer to g2d_image structure providing
5783f012e29Smrg *	buffer information.
5793f012e29Smrg * @src_x: x position of source rectangle.
5803f012e29Smrg * @src_y: y position of source rectangle.
5813f012e29Smrg * @dst_x: x position of destination rectangle.
5823f012e29Smrg * @dst_y: y position of destination rectangle.
5833f012e29Smrg * @w: width of rectangle to move.
5843f012e29Smrg * @h: height of rectangle to move.
5853f012e29Smrg */
5863f012e29Smrgint
5873f012e29Smrgg2d_move(struct g2d_context *ctx, struct g2d_image *img,
5883f012e29Smrg		unsigned int src_x, unsigned int src_y,
5893f012e29Smrg		unsigned int dst_x, unsigned dst_y, unsigned int w,
5903f012e29Smrg		unsigned int h)
5913f012e29Smrg{
5923f012e29Smrg	union g2d_rop4_val rop4;
5933f012e29Smrg	union g2d_point_val pt;
5943f012e29Smrg	union g2d_direction_val dir;
5953f012e29Smrg	unsigned int src_w, src_h, dst_w, dst_h;
5963f012e29Smrg
597e88f27b3Smrg	src_w = w;
598e88f27b3Smrg	src_h = h;
5993f012e29Smrg	if (src_x + img->width > w)
6003f012e29Smrg		src_w = img->width - src_x;
6013f012e29Smrg	if (src_y + img->height > h)
6023f012e29Smrg		src_h = img->height - src_y;
603e88f27b3Smrg
604e88f27b3Smrg	dst_w = w;
605e88f27b3Smrg	dst_h = w;
6063f012e29Smrg	if (dst_x + img->width > w)
6073f012e29Smrg		dst_w = img->width - dst_x;
6083f012e29Smrg	if (dst_y + img->height > h)
6093f012e29Smrg		dst_h = img->height - dst_y;
610e88f27b3Smrg
611e88f27b3Smrg	w = MIN(src_w, dst_w);
612e88f27b3Smrg	h = MIN(src_h, dst_h);
613e88f27b3Smrg
6143f012e29Smrg	if (w == 0 || h == 0) {
6153f012e29Smrg		fprintf(stderr, MSG_PREFIX "invalid width or height.\n");
616e88f27b3Smrg		return -EINVAL;
617e88f27b3Smrg	}
618e88f27b3Smrg
6193f012e29Smrg	if (g2d_check_space(ctx, 13, 2))
6203f012e29Smrg		return -ENOSPC;
6213f012e29Smrg
6223f012e29Smrg	g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
6233f012e29Smrg	g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL);
6243f012e29Smrg
6253f012e29Smrg	g2d_add_cmd(ctx, DST_COLOR_MODE_REG, img->color_mode);
6263f012e29Smrg	g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, img->color_mode);
6273f012e29Smrg
6283f012e29Smrg	g2d_add_base_addr(ctx, img, g2d_dst);
6293f012e29Smrg	g2d_add_base_addr(ctx, img, g2d_src);
6303f012e29Smrg
6313f012e29Smrg	g2d_add_cmd(ctx, DST_STRIDE_REG, img->stride);
6323f012e29Smrg	g2d_add_cmd(ctx, SRC_STRIDE_REG, img->stride);
6333f012e29Smrg
6343f012e29Smrg	dir.val[0] = dir.val[1] = 0;
6353f012e29Smrg
6363f012e29Smrg	if (dst_x >= src_x)
6373f012e29Smrg		dir.data.src_x_direction = dir.data.dst_x_direction = 1;
6383f012e29Smrg	if (dst_y >= src_y)
6393f012e29Smrg		dir.data.src_y_direction = dir.data.dst_y_direction = 1;
6403f012e29Smrg
6413f012e29Smrg	g2d_set_direction(ctx, &dir);
6423f012e29Smrg
643e88f27b3Smrg	pt.data.x = src_x;
644e88f27b3Smrg	pt.data.y = src_y;
645e88f27b3Smrg	g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
646e88f27b3Smrg	pt.data.x = src_x + w;
647e88f27b3Smrg	pt.data.y = src_y + h;
648e88f27b3Smrg	g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
649e88f27b3Smrg
650e88f27b3Smrg	pt.data.x = dst_x;
651e88f27b3Smrg	pt.data.y = dst_y;
652e88f27b3Smrg	g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
653e88f27b3Smrg	pt.data.x = dst_x + w;
654baaff307Smrg	pt.data.y = dst_y + h;
655e88f27b3Smrg	g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
656e88f27b3Smrg
657e88f27b3Smrg	rop4.val = 0;
658e88f27b3Smrg	rop4.data.unmasked_rop3 = G2D_ROP3_SRC;
659e88f27b3Smrg	g2d_add_cmd(ctx, ROP4_REG, rop4.val);
660e88f27b3Smrg
661e6188e58Smrg	return g2d_flush(ctx);
662e88f27b3Smrg}
663e88f27b3Smrg
664e88f27b3Smrg/**
665e88f27b3Smrg * g2d_copy_with_scale - copy contents in source buffer to destination buffer
666e88f27b3Smrg *	scaling up or down properly.
667e88f27b3Smrg *
668e88f27b3Smrg * @ctx: a pointer to g2d_context structure.
669e88f27b3Smrg * @src: a pointer to g2d_image structure including image and buffer
670e88f27b3Smrg *	information to source.
671e88f27b3Smrg * @dst: a pointer to g2d_image structure including image and buffer
672e88f27b3Smrg *	information to destination.
673e88f27b3Smrg * @src_x: x start position to source buffer.
674e88f27b3Smrg * @src_y: y start position to source buffer.
675e88f27b3Smrg * @src_w: width value to source buffer.
676e88f27b3Smrg * @src_h: height value to source buffer.
677e88f27b3Smrg * @dst_x: x start position to destination buffer.
678e88f27b3Smrg * @dst_y: y start position to destination buffer.
679e88f27b3Smrg * @dst_w: width value to destination buffer.
680e88f27b3Smrg * @dst_h: height value to destination buffer.
681e88f27b3Smrg * @negative: indicate that it uses color negative to source and
682e88f27b3Smrg *	destination buffers.
683e88f27b3Smrg */
684e6188e58Smrgint
685baaff307Smrgg2d_copy_with_scale(struct g2d_context *ctx, struct g2d_image *src,
686e88f27b3Smrg				struct g2d_image *dst, unsigned int src_x,
687e88f27b3Smrg				unsigned int src_y, unsigned int src_w,
688e88f27b3Smrg				unsigned int src_h, unsigned int dst_x,
689e88f27b3Smrg				unsigned int dst_y, unsigned int dst_w,
690e88f27b3Smrg				unsigned int dst_h, unsigned int negative)
691e88f27b3Smrg{
692e88f27b3Smrg	union g2d_rop4_val rop4;
693e88f27b3Smrg	union g2d_point_val pt;
6943f012e29Smrg	unsigned int scale, repeat_pad;
695e6188e58Smrg	unsigned int scale_x, scale_y;
696e88f27b3Smrg
6973f012e29Smrg	/* Sanitize this parameter to facilitate space computation below. */
6983f012e29Smrg	if (negative)
6993f012e29Smrg		negative = 1;
700e88f27b3Smrg
701e88f27b3Smrg	if (src_w == dst_w && src_h == dst_h)
702e88f27b3Smrg		scale = 0;
703e88f27b3Smrg	else {
704e88f27b3Smrg		scale = 1;
705e6188e58Smrg		scale_x = g2d_get_scaling(src_w, dst_w);
706e6188e58Smrg		scale_y = g2d_get_scaling(src_h, dst_h);
707e88f27b3Smrg	}
708e88f27b3Smrg
7093f012e29Smrg	repeat_pad = src->repeat_mode == G2D_REPEAT_MODE_PAD ? 1 : 0;
7103f012e29Smrg
711e88f27b3Smrg	if (src_x + src_w > src->width)
712e88f27b3Smrg		src_w = src->width - src_x;
713e88f27b3Smrg	if (src_y + src_h > src->height)
714e88f27b3Smrg		src_h = src->height - src_y;
715e88f27b3Smrg
716e88f27b3Smrg	if (dst_x + dst_w > dst->width)
717e88f27b3Smrg		dst_w = dst->width - dst_x;
718e88f27b3Smrg	if (dst_y + dst_h > dst->height)
719e88f27b3Smrg		dst_h = dst->height - dst_y;
720e88f27b3Smrg
721e88f27b3Smrg	if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) {
7223f012e29Smrg		fprintf(stderr, MSG_PREFIX "invalid width or height.\n");
723e88f27b3Smrg		return -EINVAL;
724e88f27b3Smrg	}
725e88f27b3Smrg
7263f012e29Smrg	if (g2d_check_space(ctx, 12 + scale * 3 + negative + repeat_pad, 2))
7273f012e29Smrg		return -ENOSPC;
7283f012e29Smrg
7293f012e29Smrg	g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
7303f012e29Smrg	g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode);
7313f012e29Smrg	g2d_add_base_addr(ctx, dst, g2d_dst);
7323f012e29Smrg	g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride);
7333f012e29Smrg
7343f012e29Smrg	g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL);
7353f012e29Smrg	g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode);
7363f012e29Smrg
7373f012e29Smrg	g2d_add_cmd(ctx, SRC_REPEAT_MODE_REG, src->repeat_mode);
7383f012e29Smrg	if (repeat_pad)
7393f012e29Smrg		g2d_add_cmd(ctx, SRC_PAD_VALUE_REG, dst->color);
7403f012e29Smrg
7413f012e29Smrg	g2d_add_base_addr(ctx, src, g2d_src);
7423f012e29Smrg	g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride);
7433f012e29Smrg
7443f012e29Smrg	rop4.val = 0;
7453f012e29Smrg	rop4.data.unmasked_rop3 = G2D_ROP3_SRC;
7463f012e29Smrg
747e88f27b3Smrg	if (negative) {
748e88f27b3Smrg		g2d_add_cmd(ctx, BG_COLOR_REG, 0x00FFFFFF);
7493f012e29Smrg		rop4.data.unmasked_rop3 ^= G2D_ROP3_DST;
750e88f27b3Smrg	}
751e88f27b3Smrg
7523f012e29Smrg	g2d_add_cmd(ctx, ROP4_REG, rop4.val);
7533f012e29Smrg
754e88f27b3Smrg	if (scale) {
755e88f27b3Smrg		g2d_add_cmd(ctx, SRC_SCALE_CTRL_REG, G2D_SCALE_MODE_BILINEAR);
756e6188e58Smrg		g2d_add_cmd(ctx, SRC_XSCALE_REG, scale_x);
757e6188e58Smrg		g2d_add_cmd(ctx, SRC_YSCALE_REG, scale_y);
758e88f27b3Smrg	}
759e88f27b3Smrg
760e88f27b3Smrg	pt.data.x = src_x;
761e88f27b3Smrg	pt.data.y = src_y;
762e88f27b3Smrg	g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
763e88f27b3Smrg	pt.data.x = src_x + src_w;
764e88f27b3Smrg	pt.data.y = src_y + src_h;
765e88f27b3Smrg	g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
766e88f27b3Smrg
767e88f27b3Smrg	pt.data.x = dst_x;
768e88f27b3Smrg	pt.data.y = dst_y;
769e88f27b3Smrg	g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
770e88f27b3Smrg	pt.data.x = dst_x + dst_w;
771e88f27b3Smrg	pt.data.y = dst_y + dst_h;
772e88f27b3Smrg	g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
773e88f27b3Smrg
774e6188e58Smrg	return g2d_flush(ctx);
775e88f27b3Smrg}
776e88f27b3Smrg
777e88f27b3Smrg/**
778e6188e58Smrg * g2d_blend - blend image data in source and destination buffers.
779e88f27b3Smrg *
780e88f27b3Smrg * @ctx: a pointer to g2d_context structure.
781e88f27b3Smrg * @src: a pointer to g2d_image structure including image and buffer
782e88f27b3Smrg *	information to source.
783e88f27b3Smrg * @dst: a pointer to g2d_image structure including image and buffer
784e88f27b3Smrg *	information to destination.
785e88f27b3Smrg * @src_x: x start position to source buffer.
786e88f27b3Smrg * @src_y: y start position to source buffer.
787e88f27b3Smrg * @dst_x: x start position to destination buffer.
788e88f27b3Smrg * @dst_y: y start position to destination buffer.
789e88f27b3Smrg * @w: width value to source and destination buffer.
790e88f27b3Smrg * @h: height value to source and destination buffer.
791e88f27b3Smrg * @op: blend operation type.
792e88f27b3Smrg */
793e6188e58Smrgint
794baaff307Smrgg2d_blend(struct g2d_context *ctx, struct g2d_image *src,
795e88f27b3Smrg		struct g2d_image *dst, unsigned int src_x,
796e88f27b3Smrg		unsigned int src_y, unsigned int dst_x, unsigned int dst_y,
797e88f27b3Smrg		unsigned int w, unsigned int h, enum e_g2d_op op)
798e88f27b3Smrg{
799e88f27b3Smrg	union g2d_point_val pt;
800e88f27b3Smrg	union g2d_bitblt_cmd_val bitblt;
801e88f27b3Smrg	union g2d_blend_func_val blend;
8023f012e29Smrg	unsigned int gem_space;
8033f012e29Smrg	unsigned int src_w, src_h, dst_w, dst_h;
8043f012e29Smrg
8053f012e29Smrg	src_w = w;
8063f012e29Smrg	src_h = h;
8073f012e29Smrg	if (src_x + w > src->width)
8083f012e29Smrg		src_w = src->width - src_x;
8093f012e29Smrg	if (src_y + h > src->height)
8103f012e29Smrg		src_h = src->height - src_y;
8113f012e29Smrg
8123f012e29Smrg	dst_w = w;
8133f012e29Smrg	dst_h = h;
8143f012e29Smrg	if (dst_x + w > dst->width)
8153f012e29Smrg		dst_w = dst->width - dst_x;
8163f012e29Smrg	if (dst_y + h > dst->height)
8173f012e29Smrg		dst_h = dst->height - dst_y;
8183f012e29Smrg
8193f012e29Smrg	w = MIN(src_w, dst_w);
8203f012e29Smrg	h = MIN(src_h, dst_h);
8213f012e29Smrg
8223f012e29Smrg	if (w <= 0 || h <= 0) {
8233f012e29Smrg		fprintf(stderr, MSG_PREFIX "invalid width or height.\n");
8243f012e29Smrg		return -EINVAL;
8253f012e29Smrg	}
8263f012e29Smrg
8273f012e29Smrg	if (!g2d_validate_select_mode(src->select_mode)) {
8283f012e29Smrg		fprintf(stderr , MSG_PREFIX "invalid select mode for source.\n");
8293f012e29Smrg		return -EINVAL;
8303f012e29Smrg	}
8313f012e29Smrg
8323f012e29Smrg	if (!g2d_validate_blending_op(op)) {
8333f012e29Smrg		fprintf(stderr , MSG_PREFIX "unsupported blending operation.\n");
8343f012e29Smrg		return -EINVAL;
8353f012e29Smrg	}
8363f012e29Smrg
8373f012e29Smrg	gem_space = src->select_mode == G2D_SELECT_MODE_NORMAL ? 2 : 1;
8383f012e29Smrg
8393f012e29Smrg	if (g2d_check_space(ctx, 12, gem_space))
8403f012e29Smrg		return -ENOSPC;
841e88f27b3Smrg
842e88f27b3Smrg	bitblt.val = 0;
843e88f27b3Smrg	blend.val = 0;
844e88f27b3Smrg
845e88f27b3Smrg	if (op == G2D_OP_SRC || op == G2D_OP_CLEAR)
846e88f27b3Smrg		g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
847e88f27b3Smrg	else
848e88f27b3Smrg		g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL);
849e88f27b3Smrg
850e88f27b3Smrg	g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode);
851e6188e58Smrg	g2d_add_base_addr(ctx, dst, g2d_dst);
852e88f27b3Smrg	g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride);
853e88f27b3Smrg
854e88f27b3Smrg	g2d_add_cmd(ctx, SRC_SELECT_REG, src->select_mode);
855e88f27b3Smrg	g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode);
856e88f27b3Smrg
857e88f27b3Smrg	switch (src->select_mode) {
858e88f27b3Smrg	case G2D_SELECT_MODE_NORMAL:
859e6188e58Smrg		g2d_add_base_addr(ctx, src, g2d_src);
860e88f27b3Smrg		g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride);
861e88f27b3Smrg		break;
862e88f27b3Smrg	case G2D_SELECT_MODE_FGCOLOR:
863e88f27b3Smrg		g2d_add_cmd(ctx, FG_COLOR_REG, src->color);
864e88f27b3Smrg		break;
865e88f27b3Smrg	case G2D_SELECT_MODE_BGCOLOR:
866e88f27b3Smrg		g2d_add_cmd(ctx, BG_COLOR_REG, src->color);
867e88f27b3Smrg		break;
868e88f27b3Smrg	}
869e88f27b3Smrg
870e88f27b3Smrg	bitblt.data.alpha_blend_mode = G2D_ALPHA_BLEND_MODE_ENABLE;
871e88f27b3Smrg	blend.val = g2d_get_blend_op(op);
872e88f27b3Smrg	g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val);
873e88f27b3Smrg	g2d_add_cmd(ctx, BLEND_FUNCTION_REG, blend.val);
874e88f27b3Smrg
875e88f27b3Smrg	pt.data.x = src_x;
876e88f27b3Smrg	pt.data.y = src_y;
877e88f27b3Smrg	g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
878e88f27b3Smrg	pt.data.x = src_x + w;
879e88f27b3Smrg	pt.data.y = src_y + h;
880e88f27b3Smrg	g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
881e88f27b3Smrg
882e88f27b3Smrg	pt.data.x = dst_x;
883e88f27b3Smrg	pt.data.y = dst_y;
884e88f27b3Smrg	g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
885e88f27b3Smrg	pt.data.x = dst_x + w;
886e88f27b3Smrg	pt.data.y = dst_y + h;
887e88f27b3Smrg	g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
888e88f27b3Smrg
889e6188e58Smrg	return g2d_flush(ctx);
890e88f27b3Smrg}
891e88f27b3Smrg
892e6188e58Smrg/**
893e6188e58Smrg * g2d_scale_and_blend - apply scaling to source buffer and then blend to destination buffer
894e6188e58Smrg *
895e6188e58Smrg * @ctx: a pointer to g2d_context structure.
896e6188e58Smrg * @src: a pointer to g2d_image structure including image and buffer
897e6188e58Smrg *	information to source.
898e6188e58Smrg * @dst: a pointer to g2d_image structure including image and buffer
899e6188e58Smrg *	information to destination.
900e6188e58Smrg * @src_x: x start position to source buffer.
901e6188e58Smrg * @src_y: y start position to source buffer.
902e6188e58Smrg * @src_w: width value to source buffer.
903e6188e58Smrg * @src_h: height value to source buffer.
904e6188e58Smrg * @dst_x: x start position to destination buffer.
905e6188e58Smrg * @dst_y: y start position to destination buffer.
906e6188e58Smrg * @dst_w: width value to destination buffer.
907e6188e58Smrg * @dst_h: height value to destination buffer.
908e6188e58Smrg * @op: blend operation type.
909e6188e58Smrg */
910e6188e58Smrgint
911e6188e58Smrgg2d_scale_and_blend(struct g2d_context *ctx, struct g2d_image *src,
912e6188e58Smrg		struct g2d_image *dst, unsigned int src_x, unsigned int src_y,
913e6188e58Smrg		unsigned int src_w, unsigned int src_h, unsigned int dst_x,
914e6188e58Smrg		unsigned int dst_y, unsigned int dst_w, unsigned int dst_h,
915e6188e58Smrg		enum e_g2d_op op)
916e6188e58Smrg{
917e6188e58Smrg	union g2d_point_val pt;
918e6188e58Smrg	union g2d_bitblt_cmd_val bitblt;
919e6188e58Smrg	union g2d_blend_func_val blend;
9203f012e29Smrg	unsigned int scale, gem_space;
921e6188e58Smrg	unsigned int scale_x, scale_y;
922e6188e58Smrg
9233f012e29Smrg	if (src_w == dst_w && src_h == dst_h)
9243f012e29Smrg		scale = 0;
9253f012e29Smrg	else {
9263f012e29Smrg		scale = 1;
9273f012e29Smrg		scale_x = g2d_get_scaling(src_w, dst_w);
9283f012e29Smrg		scale_y = g2d_get_scaling(src_h, dst_h);
9293f012e29Smrg	}
9303f012e29Smrg
9313f012e29Smrg	if (src_x + src_w > src->width)
9323f012e29Smrg		src_w = src->width - src_x;
9333f012e29Smrg	if (src_y + src_h > src->height)
9343f012e29Smrg		src_h = src->height - src_y;
9353f012e29Smrg
9363f012e29Smrg	if (dst_x + dst_w > dst->width)
9373f012e29Smrg		dst_w = dst->width - dst_x;
9383f012e29Smrg	if (dst_y + dst_h > dst->height)
9393f012e29Smrg		dst_h = dst->height - dst_y;
9403f012e29Smrg
9413f012e29Smrg	if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) {
9423f012e29Smrg		fprintf(stderr, MSG_PREFIX "invalid width or height.\n");
9433f012e29Smrg		return -EINVAL;
9443f012e29Smrg	}
9453f012e29Smrg
9463f012e29Smrg	if (!g2d_validate_select_mode(src->select_mode)) {
9473f012e29Smrg		fprintf(stderr , MSG_PREFIX "invalid select mode for source.\n");
9483f012e29Smrg		return -EINVAL;
9493f012e29Smrg	}
9503f012e29Smrg
9513f012e29Smrg	if (!g2d_validate_blending_op(op)) {
9523f012e29Smrg		fprintf(stderr , MSG_PREFIX "unsupported blending operation.\n");
9533f012e29Smrg		return -EINVAL;
9543f012e29Smrg	}
9553f012e29Smrg
9563f012e29Smrg	gem_space = src->select_mode == G2D_SELECT_MODE_NORMAL ? 2 : 1;
9573f012e29Smrg
9583f012e29Smrg	if (g2d_check_space(ctx, 12 + scale * 3, gem_space))
9593f012e29Smrg		return -ENOSPC;
9603f012e29Smrg
961e6188e58Smrg	bitblt.val = 0;
962e6188e58Smrg	blend.val = 0;
963e6188e58Smrg
964e6188e58Smrg	if (op == G2D_OP_SRC || op == G2D_OP_CLEAR)
965e6188e58Smrg		g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
966e6188e58Smrg	else
967e6188e58Smrg		g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL);
968e6188e58Smrg
969e6188e58Smrg	g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode);
9703f012e29Smrg	g2d_add_base_addr(ctx, dst, g2d_dst);
971e6188e58Smrg	g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride);
972e6188e58Smrg
973e6188e58Smrg	g2d_add_cmd(ctx, SRC_SELECT_REG, src->select_mode);
974e6188e58Smrg	g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode);
975e6188e58Smrg
976e6188e58Smrg	switch (src->select_mode) {
977e6188e58Smrg	case G2D_SELECT_MODE_NORMAL:
9783f012e29Smrg		g2d_add_base_addr(ctx, src, g2d_src);
979e6188e58Smrg		g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride);
980e6188e58Smrg		break;
981e6188e58Smrg	case G2D_SELECT_MODE_FGCOLOR:
982e6188e58Smrg		g2d_add_cmd(ctx, FG_COLOR_REG, src->color);
983e6188e58Smrg		break;
984e6188e58Smrg	case G2D_SELECT_MODE_BGCOLOR:
985e6188e58Smrg		g2d_add_cmd(ctx, BG_COLOR_REG, src->color);
986e6188e58Smrg		break;
987e6188e58Smrg	}
988e6188e58Smrg
989e6188e58Smrg	if (scale) {
990e6188e58Smrg		g2d_add_cmd(ctx, SRC_SCALE_CTRL_REG, G2D_SCALE_MODE_BILINEAR);
991e6188e58Smrg		g2d_add_cmd(ctx, SRC_XSCALE_REG, scale_x);
992e6188e58Smrg		g2d_add_cmd(ctx, SRC_YSCALE_REG, scale_y);
993e6188e58Smrg	}
994e6188e58Smrg
995e6188e58Smrg	bitblt.data.alpha_blend_mode = G2D_ALPHA_BLEND_MODE_ENABLE;
996e6188e58Smrg	blend.val = g2d_get_blend_op(op);
997e6188e58Smrg	g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val);
998e6188e58Smrg	g2d_add_cmd(ctx, BLEND_FUNCTION_REG, blend.val);
999e6188e58Smrg
1000e6188e58Smrg	pt.data.x = src_x;
1001e6188e58Smrg	pt.data.y = src_y;
1002e6188e58Smrg	g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
1003e6188e58Smrg	pt.data.x = src_x + src_w;
1004e6188e58Smrg	pt.data.y = src_y + src_h;
1005e6188e58Smrg	g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
1006e6188e58Smrg
1007e6188e58Smrg	pt.data.x = dst_x;
1008e6188e58Smrg	pt.data.y = dst_y;
1009e6188e58Smrg	g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
1010e6188e58Smrg	pt.data.x = dst_x + dst_w;
1011e6188e58Smrg	pt.data.y = dst_y + dst_h;
1012e6188e58Smrg	g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
1013e6188e58Smrg
1014e6188e58Smrg	return g2d_flush(ctx);
1015e6188e58Smrg}
1016