exynos_fimg2d.c revision e6188e58
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>
21e88f27b3Smrg
22e88f27b3Smrg#include <sys/mman.h>
23e88f27b3Smrg#include <linux/stddef.h>
24e88f27b3Smrg
25e88f27b3Smrg#include <xf86drm.h>
26e88f27b3Smrg
27e6188e58Smrg#include "libdrm_macros.h"
28e88f27b3Smrg#include "exynos_drm.h"
29e88f27b3Smrg#include "fimg2d_reg.h"
30e6188e58Smrg#include "exynos_fimg2d.h"
31e88f27b3Smrg
32e88f27b3Smrg#define		SET_BF(val, sc, si, scsa, scda, dc, di, dcsa, dcda) \
33e88f27b3Smrg			val.data.src_coeff = sc;		\
34e88f27b3Smrg			val.data.inv_src_color_coeff = si;	\
35e88f27b3Smrg			val.data.src_coeff_src_a = scsa;	\
36e88f27b3Smrg			val.data.src_coeff_dst_a = scda;	\
37e88f27b3Smrg			val.data.dst_coeff = dc;		\
38e88f27b3Smrg			val.data.inv_dst_color_coeff = di;	\
39e88f27b3Smrg			val.data.dst_coeff_src_a = dcsa;	\
40e88f27b3Smrg			val.data.dst_coeff_dst_a = dcda;
41e88f27b3Smrg
42e88f27b3Smrg#define MIN(a, b)	((a) < (b) ? (a) : (b))
43e88f27b3Smrg
44e6188e58Smrgenum g2d_base_addr_reg {
45e6188e58Smrg	g2d_dst = 0,
46e6188e58Smrg	g2d_src
47e6188e58Smrg};
48e6188e58Smrg
49e6188e58Smrgstatic unsigned int g2d_get_scaling(unsigned int src, unsigned int dst)
50e6188e58Smrg{
51e6188e58Smrg	/*
52e6188e58Smrg	 * The G2D hw scaling factor is a normalized inverse of the scaling factor.
53e6188e58Smrg	 * For example: When source width is 100 and destination width is 200
54e6188e58Smrg	 * (scaling of 2x), then the hw factor is NC * 100 / 200.
55e6188e58Smrg	 * The normalization factor (NC) is 2^16 = 0x10000.
56e6188e58Smrg	 */
57e6188e58Smrg
58e6188e58Smrg	return ((src << 16) / dst);
59e6188e58Smrg}
60e6188e58Smrg
61e88f27b3Smrgstatic unsigned int g2d_get_blend_op(enum e_g2d_op op)
62e88f27b3Smrg{
63e88f27b3Smrg	union g2d_blend_func_val val;
64e88f27b3Smrg
65e88f27b3Smrg	val.val = 0;
66e88f27b3Smrg
67e88f27b3Smrg	switch (op) {
68e88f27b3Smrg	case G2D_OP_CLEAR:
69e88f27b3Smrg	case G2D_OP_DISJOINT_CLEAR:
70e88f27b3Smrg	case G2D_OP_CONJOINT_CLEAR:
71e88f27b3Smrg		SET_BF(val, G2D_COEFF_MODE_ZERO, 0, 0, 0, G2D_COEFF_MODE_ZERO,
72e88f27b3Smrg				0, 0, 0);
73e88f27b3Smrg		break;
74e88f27b3Smrg	case G2D_OP_SRC:
75e88f27b3Smrg	case G2D_OP_DISJOINT_SRC:
76e88f27b3Smrg	case G2D_OP_CONJOINT_SRC:
77e88f27b3Smrg		SET_BF(val, G2D_COEFF_MODE_ONE, 0, 0, 0, G2D_COEFF_MODE_ZERO,
78e88f27b3Smrg				0, 0, 0);
79e88f27b3Smrg		break;
80e88f27b3Smrg	case G2D_OP_DST:
81e88f27b3Smrg	case G2D_OP_DISJOINT_DST:
82e88f27b3Smrg	case G2D_OP_CONJOINT_DST:
83e88f27b3Smrg		SET_BF(val, G2D_COEFF_MODE_ZERO, 0, 0, 0, G2D_COEFF_MODE_ONE,
84e88f27b3Smrg				0, 0, 0);
85e88f27b3Smrg		break;
86e88f27b3Smrg	case G2D_OP_OVER:
87e88f27b3Smrg		SET_BF(val, G2D_COEFF_MODE_ONE, 0, 0, 0,
88e88f27b3Smrg				G2D_COEFF_MODE_SRC_ALPHA, 1, 0, 0);
89e88f27b3Smrg		break;
90e6188e58Smrg	case G2D_OP_INTERPOLATE:
91e6188e58Smrg		SET_BF(val, G2D_COEFF_MODE_SRC_ALPHA, 0, 0, 0,
92e6188e58Smrg				G2D_COEFF_MODE_SRC_ALPHA, 1, 0, 0);
93e6188e58Smrg		break;
94e88f27b3Smrg	default:
95e88f27b3Smrg		fprintf(stderr, "Not support operation(%d).\n", op);
96e88f27b3Smrg		SET_BF(val, G2D_COEFF_MODE_ONE, 0, 0, 0, G2D_COEFF_MODE_ZERO,
97e88f27b3Smrg				0, 0, 0);
98e88f27b3Smrg		break;
99e88f27b3Smrg	}
100e88f27b3Smrg
101e88f27b3Smrg	return val.val;
102e88f27b3Smrg}
103e88f27b3Smrg
104e88f27b3Smrg/*
105e88f27b3Smrg * g2d_add_cmd - set given command and value to user side command buffer.
106e88f27b3Smrg *
107e88f27b3Smrg * @ctx: a pointer to g2d_context structure.
108e88f27b3Smrg * @cmd: command data.
109e88f27b3Smrg * @value: value data.
110e88f27b3Smrg */
111e88f27b3Smrgstatic int g2d_add_cmd(struct g2d_context *ctx, unsigned long cmd,
112e88f27b3Smrg			unsigned long value)
113e88f27b3Smrg{
114e88f27b3Smrg	switch (cmd & ~(G2D_BUF_USERPTR)) {
115e88f27b3Smrg	case SRC_BASE_ADDR_REG:
116e88f27b3Smrg	case SRC_PLANE2_BASE_ADDR_REG:
117e88f27b3Smrg	case DST_BASE_ADDR_REG:
118e88f27b3Smrg	case DST_PLANE2_BASE_ADDR_REG:
119e88f27b3Smrg	case PAT_BASE_ADDR_REG:
120e88f27b3Smrg	case MASK_BASE_ADDR_REG:
121e88f27b3Smrg		if (ctx->cmd_buf_nr >= G2D_MAX_GEM_CMD_NR) {
122e88f27b3Smrg			fprintf(stderr, "Overflow cmd_gem size.\n");
123e88f27b3Smrg			return -EINVAL;
124e88f27b3Smrg		}
125e88f27b3Smrg
126e88f27b3Smrg		ctx->cmd_buf[ctx->cmd_buf_nr].offset = cmd;
127e88f27b3Smrg		ctx->cmd_buf[ctx->cmd_buf_nr].data = value;
128e88f27b3Smrg		ctx->cmd_buf_nr++;
129e88f27b3Smrg		break;
130e88f27b3Smrg	default:
131e88f27b3Smrg		if (ctx->cmd_nr >= G2D_MAX_CMD_NR) {
132e88f27b3Smrg			fprintf(stderr, "Overflow cmd size.\n");
133e88f27b3Smrg			return -EINVAL;
134e88f27b3Smrg		}
135e88f27b3Smrg
136e88f27b3Smrg		ctx->cmd[ctx->cmd_nr].offset = cmd;
137e88f27b3Smrg		ctx->cmd[ctx->cmd_nr].data = value;
138e88f27b3Smrg		ctx->cmd_nr++;
139e88f27b3Smrg		break;
140e88f27b3Smrg	}
141e88f27b3Smrg
142e6188e58Smrg	return 0;
143e6188e58Smrg}
144e6188e58Smrg
145e6188e58Smrg/*
146e6188e58Smrg * g2d_add_base_addr - helper function to set dst/src base address register.
147e6188e58Smrg *
148e6188e58Smrg * @ctx: a pointer to g2d_context structure.
149e6188e58Smrg * @img: a pointer to the dst/src g2d_image structure.
150e6188e58Smrg * @reg: the register that should be set.
151e6188e58Smrg */
152e6188e58Smrgstatic void g2d_add_base_addr(struct g2d_context *ctx, struct g2d_image *img,
153e6188e58Smrg			enum g2d_base_addr_reg reg)
154e6188e58Smrg{
155e6188e58Smrg	const unsigned long cmd = (reg == g2d_dst) ?
156e6188e58Smrg		DST_BASE_ADDR_REG : SRC_BASE_ADDR_REG;
157e6188e58Smrg
158e6188e58Smrg	if (img->buf_type == G2D_IMGBUF_USERPTR)
159e6188e58Smrg		g2d_add_cmd(ctx, cmd | G2D_BUF_USERPTR,
160e6188e58Smrg				(unsigned long)&img->user_ptr[0]);
161e6188e58Smrg	else
162e6188e58Smrg		g2d_add_cmd(ctx, cmd, img->bo[0]);
163e88f27b3Smrg}
164e88f27b3Smrg
165e88f27b3Smrg/*
166e88f27b3Smrg * g2d_reset - reset fimg2d hardware.
167e88f27b3Smrg *
168e88f27b3Smrg * @ctx: a pointer to g2d_context structure.
169e88f27b3Smrg *
170e88f27b3Smrg */
171e88f27b3Smrgstatic void g2d_reset(struct g2d_context *ctx)
172e88f27b3Smrg{
173e88f27b3Smrg	ctx->cmd_nr = 0;
174e88f27b3Smrg	ctx->cmd_buf_nr = 0;
175e88f27b3Smrg
176e88f27b3Smrg	g2d_add_cmd(ctx, SOFT_RESET_REG, 0x01);
177e88f27b3Smrg}
178e88f27b3Smrg
179e88f27b3Smrg/*
180e6188e58Smrg * g2d_flush - submit all commands and values in user side command buffer
181e88f27b3Smrg *		to command queue aware of fimg2d dma.
182e88f27b3Smrg *
183e88f27b3Smrg * @ctx: a pointer to g2d_context structure.
184e88f27b3Smrg *
185e88f27b3Smrg * This function should be called after all commands and values to user
186e6188e58Smrg * side command buffer are set. It submits that buffer to the kernel side driver.
187e88f27b3Smrg */
188e88f27b3Smrgstatic int g2d_flush(struct g2d_context *ctx)
189e88f27b3Smrg{
190e88f27b3Smrg	int ret;
191e6188e58Smrg	struct drm_exynos_g2d_set_cmdlist cmdlist = {0};
192e88f27b3Smrg
193e6188e58Smrg	if (ctx->cmd_nr == 0 && ctx->cmd_buf_nr == 0)
194e6188e58Smrg		return -1;
195e88f27b3Smrg
196e88f27b3Smrg	if (ctx->cmdlist_nr >= G2D_MAX_CMD_LIST_NR) {
197e88f27b3Smrg		fprintf(stderr, "Overflow cmdlist.\n");
198e88f27b3Smrg		return -EINVAL;
199e88f27b3Smrg	}
200e88f27b3Smrg
201baaff307Smrg	cmdlist.cmd = (uint64_t)(uintptr_t)&ctx->cmd[0];
202baaff307Smrg	cmdlist.cmd_buf = (uint64_t)(uintptr_t)&ctx->cmd_buf[0];
203e88f27b3Smrg	cmdlist.cmd_nr = ctx->cmd_nr;
204e88f27b3Smrg	cmdlist.cmd_buf_nr = ctx->cmd_buf_nr;
205e88f27b3Smrg	cmdlist.event_type = G2D_EVENT_NOT;
206e88f27b3Smrg	cmdlist.user_data = 0;
207e88f27b3Smrg
208e88f27b3Smrg	ctx->cmd_nr = 0;
209e88f27b3Smrg	ctx->cmd_buf_nr = 0;
210e88f27b3Smrg
211e88f27b3Smrg	ret = drmIoctl(ctx->fd, DRM_IOCTL_EXYNOS_G2D_SET_CMDLIST, &cmdlist);
212e88f27b3Smrg	if (ret < 0) {
213e88f27b3Smrg		fprintf(stderr, "failed to set cmdlist.\n");
214e88f27b3Smrg		return ret;
215e88f27b3Smrg	}
216e88f27b3Smrg
217e88f27b3Smrg	ctx->cmdlist_nr++;
218e88f27b3Smrg
219e88f27b3Smrg	return ret;
220e88f27b3Smrg}
221e88f27b3Smrg
222e88f27b3Smrg/**
223e88f27b3Smrg * g2d_init - create a new g2d context and get hardware version.
224e88f27b3Smrg *
225e6188e58Smrg * fd: a file descriptor to an opened drm device.
226e88f27b3Smrg */
227e6188e58Smrgstruct g2d_context *g2d_init(int fd)
228e88f27b3Smrg{
229e88f27b3Smrg	struct drm_exynos_g2d_get_ver ver;
230e88f27b3Smrg	struct g2d_context *ctx;
231e88f27b3Smrg	int ret;
232e88f27b3Smrg
233e88f27b3Smrg	ctx = calloc(1, sizeof(*ctx));
234e88f27b3Smrg	if (!ctx) {
235e88f27b3Smrg		fprintf(stderr, "failed to allocate context.\n");
236e88f27b3Smrg		return NULL;
237e88f27b3Smrg	}
238e88f27b3Smrg
239e88f27b3Smrg	ctx->fd = fd;
240e88f27b3Smrg
241e88f27b3Smrg	ret = drmIoctl(fd, DRM_IOCTL_EXYNOS_G2D_GET_VER, &ver);
242e88f27b3Smrg	if (ret < 0) {
243e88f27b3Smrg		fprintf(stderr, "failed to get version.\n");
244e88f27b3Smrg		free(ctx);
245e88f27b3Smrg		return NULL;
246e88f27b3Smrg	}
247e88f27b3Smrg
248e88f27b3Smrg	ctx->major = ver.major;
249e88f27b3Smrg	ctx->minor = ver.minor;
250e88f27b3Smrg
251e88f27b3Smrg	printf("g2d version(%d.%d).\n", ctx->major, ctx->minor);
252e88f27b3Smrg	return ctx;
253e88f27b3Smrg}
254e88f27b3Smrg
255e6188e58Smrgvoid g2d_fini(struct g2d_context *ctx)
256e88f27b3Smrg{
257e6188e58Smrg	free(ctx);
258e88f27b3Smrg}
259e88f27b3Smrg
260e88f27b3Smrg/**
261e88f27b3Smrg * g2d_exec - start the dma to process all commands summited by g2d_flush().
262e88f27b3Smrg *
263e88f27b3Smrg * @ctx: a pointer to g2d_context structure.
264e88f27b3Smrg */
265e6188e58Smrgint g2d_exec(struct g2d_context *ctx)
266e88f27b3Smrg{
267e88f27b3Smrg	struct drm_exynos_g2d_exec exec;
268e88f27b3Smrg	int ret;
269e88f27b3Smrg
270e88f27b3Smrg	if (ctx->cmdlist_nr == 0)
271e88f27b3Smrg		return -EINVAL;
272e88f27b3Smrg
273e88f27b3Smrg	exec.async = 0;
274e88f27b3Smrg
275e88f27b3Smrg	ret = drmIoctl(ctx->fd, DRM_IOCTL_EXYNOS_G2D_EXEC, &exec);
276e88f27b3Smrg	if (ret < 0) {
277e88f27b3Smrg		fprintf(stderr, "failed to execute.\n");
278e88f27b3Smrg		return ret;
279e88f27b3Smrg	}
280e88f27b3Smrg
281e88f27b3Smrg	ctx->cmdlist_nr = 0;
282e88f27b3Smrg
283e88f27b3Smrg	return ret;
284e88f27b3Smrg}
285e88f27b3Smrg
286e88f27b3Smrg/**
287e88f27b3Smrg * g2d_solid_fill - fill given buffer with given color data.
288e88f27b3Smrg *
289e88f27b3Smrg * @ctx: a pointer to g2d_context structure.
290e88f27b3Smrg * @img: a pointer to g2d_image structure including image and buffer
291e88f27b3Smrg *	information.
292e88f27b3Smrg * @x: x start position to buffer filled with given color data.
293e88f27b3Smrg * @y: y start position to buffer filled with given color data.
294e88f27b3Smrg * @w: width value to buffer filled with given color data.
295e88f27b3Smrg * @h: height value to buffer filled with given color data.
296e88f27b3Smrg */
297e6188e58Smrgint
298baaff307Smrgg2d_solid_fill(struct g2d_context *ctx, struct g2d_image *img,
299e88f27b3Smrg			unsigned int x, unsigned int y, unsigned int w,
300e88f27b3Smrg			unsigned int h)
301e88f27b3Smrg{
302e88f27b3Smrg	union g2d_bitblt_cmd_val bitblt;
303e88f27b3Smrg	union g2d_point_val pt;
304e88f27b3Smrg
305e88f27b3Smrg	g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL);
306e88f27b3Smrg	g2d_add_cmd(ctx, DST_COLOR_MODE_REG, img->color_mode);
307e6188e58Smrg	g2d_add_base_addr(ctx, img, g2d_dst);
308e88f27b3Smrg	g2d_add_cmd(ctx, DST_STRIDE_REG, img->stride);
309e88f27b3Smrg
310e88f27b3Smrg	if (x + w > img->width)
311e88f27b3Smrg		w = img->width - x;
312e88f27b3Smrg	if (y + h > img->height)
313e88f27b3Smrg		h = img->height - y;
314e88f27b3Smrg
315e88f27b3Smrg	pt.val = 0;
316e88f27b3Smrg	pt.data.x = x;
317e88f27b3Smrg	pt.data.y = y;
318e88f27b3Smrg	g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
319e88f27b3Smrg
320e88f27b3Smrg	pt.val = 0;
321e88f27b3Smrg	pt.data.x = x + w;
322e88f27b3Smrg	pt.data.y = y + h;
323e88f27b3Smrg
324e88f27b3Smrg	g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
325e88f27b3Smrg
326e88f27b3Smrg	g2d_add_cmd(ctx, SF_COLOR_REG, img->color);
327e88f27b3Smrg
328e88f27b3Smrg	bitblt.val = 0;
329e88f27b3Smrg	bitblt.data.fast_solid_color_fill_en = 1;
330e88f27b3Smrg	g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val);
331e88f27b3Smrg
332e6188e58Smrg	return g2d_flush(ctx);
333e88f27b3Smrg}
334e88f27b3Smrg
335e88f27b3Smrg/**
336e88f27b3Smrg * g2d_copy - copy contents in source buffer to destination buffer.
337e88f27b3Smrg *
338e88f27b3Smrg * @ctx: a pointer to g2d_context structure.
339e88f27b3Smrg * @src: a pointer to g2d_image structure including image and buffer
340e88f27b3Smrg *	information to source.
341e88f27b3Smrg * @dst: a pointer to g2d_image structure including image and buffer
342e88f27b3Smrg *	information to destination.
343e88f27b3Smrg * @src_x: x start position to source buffer.
344e88f27b3Smrg * @src_y: y start position to source buffer.
345e88f27b3Smrg * @dst_x: x start position to destination buffer.
346e88f27b3Smrg * @dst_y: y start position to destination buffer.
347e88f27b3Smrg * @w: width value to source and destination buffers.
348e88f27b3Smrg * @h: height value to source and destination buffers.
349e88f27b3Smrg */
350e6188e58Smrgint
351baaff307Smrgg2d_copy(struct g2d_context *ctx, struct g2d_image *src,
352e88f27b3Smrg		struct g2d_image *dst, unsigned int src_x, unsigned int src_y,
353e88f27b3Smrg		unsigned int dst_x, unsigned dst_y, unsigned int w,
354e88f27b3Smrg		unsigned int h)
355e88f27b3Smrg{
356e88f27b3Smrg	union g2d_rop4_val rop4;
357e88f27b3Smrg	union g2d_point_val pt;
358e88f27b3Smrg	unsigned int src_w = 0, src_h = 0, dst_w = 0, dst_h = 0;
359e88f27b3Smrg
360e88f27b3Smrg	g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
361e88f27b3Smrg	g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode);
362e6188e58Smrg	g2d_add_base_addr(ctx, dst, g2d_dst);
363e88f27b3Smrg	g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride);
364e88f27b3Smrg
365e88f27b3Smrg	g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL);
366e88f27b3Smrg	g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode);
367e6188e58Smrg	g2d_add_base_addr(ctx, src, g2d_src);
368e88f27b3Smrg	g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride);
369e88f27b3Smrg
370e88f27b3Smrg	src_w = w;
371e88f27b3Smrg	src_h = h;
372e88f27b3Smrg	if (src_x + src->width > w)
373e88f27b3Smrg		src_w = src->width - src_x;
374e88f27b3Smrg	if (src_y + src->height > h)
375e88f27b3Smrg		src_h = src->height - src_y;
376e88f27b3Smrg
377e88f27b3Smrg	dst_w = w;
378e88f27b3Smrg	dst_h = w;
379e88f27b3Smrg	if (dst_x + dst->width > w)
380e88f27b3Smrg		dst_w = dst->width - dst_x;
381e88f27b3Smrg	if (dst_y + dst->height > h)
382e88f27b3Smrg		dst_h = dst->height - dst_y;
383e88f27b3Smrg
384e88f27b3Smrg	w = MIN(src_w, dst_w);
385e88f27b3Smrg	h = MIN(src_h, dst_h);
386e88f27b3Smrg
387e88f27b3Smrg	if (w <= 0 || h <= 0) {
388e88f27b3Smrg		fprintf(stderr, "invalid width or height.\n");
389e88f27b3Smrg		g2d_reset(ctx);
390e88f27b3Smrg		return -EINVAL;
391e88f27b3Smrg	}
392e88f27b3Smrg
393e88f27b3Smrg	pt.val = 0;
394e88f27b3Smrg	pt.data.x = src_x;
395e88f27b3Smrg	pt.data.y = src_y;
396e88f27b3Smrg	g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
397e88f27b3Smrg	pt.val = 0;
398e88f27b3Smrg	pt.data.x = src_x + w;
399e88f27b3Smrg	pt.data.y = src_y + h;
400e88f27b3Smrg	g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
401e88f27b3Smrg
402e88f27b3Smrg	pt.val = 0;
403e88f27b3Smrg	pt.data.x = dst_x;
404e88f27b3Smrg	pt.data.y = dst_y;
405e88f27b3Smrg	g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
406e88f27b3Smrg	pt.val = 0;
407e88f27b3Smrg	pt.data.x = dst_x + w;
408baaff307Smrg	pt.data.y = dst_y + h;
409e88f27b3Smrg	g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
410e88f27b3Smrg
411e88f27b3Smrg	rop4.val = 0;
412e88f27b3Smrg	rop4.data.unmasked_rop3 = G2D_ROP3_SRC;
413e88f27b3Smrg	g2d_add_cmd(ctx, ROP4_REG, rop4.val);
414e88f27b3Smrg
415e6188e58Smrg	return g2d_flush(ctx);
416e88f27b3Smrg}
417e88f27b3Smrg
418e88f27b3Smrg/**
419e88f27b3Smrg * g2d_copy_with_scale - copy contents in source buffer to destination buffer
420e88f27b3Smrg *	scaling up or down properly.
421e88f27b3Smrg *
422e88f27b3Smrg * @ctx: a pointer to g2d_context structure.
423e88f27b3Smrg * @src: a pointer to g2d_image structure including image and buffer
424e88f27b3Smrg *	information to source.
425e88f27b3Smrg * @dst: a pointer to g2d_image structure including image and buffer
426e88f27b3Smrg *	information to destination.
427e88f27b3Smrg * @src_x: x start position to source buffer.
428e88f27b3Smrg * @src_y: y start position to source buffer.
429e88f27b3Smrg * @src_w: width value to source buffer.
430e88f27b3Smrg * @src_h: height value to source buffer.
431e88f27b3Smrg * @dst_x: x start position to destination buffer.
432e88f27b3Smrg * @dst_y: y start position to destination buffer.
433e88f27b3Smrg * @dst_w: width value to destination buffer.
434e88f27b3Smrg * @dst_h: height value to destination buffer.
435e88f27b3Smrg * @negative: indicate that it uses color negative to source and
436e88f27b3Smrg *	destination buffers.
437e88f27b3Smrg */
438e6188e58Smrgint
439baaff307Smrgg2d_copy_with_scale(struct g2d_context *ctx, struct g2d_image *src,
440e88f27b3Smrg				struct g2d_image *dst, unsigned int src_x,
441e88f27b3Smrg				unsigned int src_y, unsigned int src_w,
442e88f27b3Smrg				unsigned int src_h, unsigned int dst_x,
443e88f27b3Smrg				unsigned int dst_y, unsigned int dst_w,
444e88f27b3Smrg				unsigned int dst_h, unsigned int negative)
445e88f27b3Smrg{
446e88f27b3Smrg	union g2d_rop4_val rop4;
447e88f27b3Smrg	union g2d_point_val pt;
448e88f27b3Smrg	unsigned int scale;
449e6188e58Smrg	unsigned int scale_x, scale_y;
450e88f27b3Smrg
451e88f27b3Smrg	g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
452e88f27b3Smrg	g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode);
453e6188e58Smrg	g2d_add_base_addr(ctx, dst, g2d_dst);
454e88f27b3Smrg	g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride);
455e88f27b3Smrg
456e88f27b3Smrg	g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL);
457e88f27b3Smrg	g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode);
458e88f27b3Smrg
459e6188e58Smrg	g2d_add_cmd(ctx, SRC_REPEAT_MODE_REG, src->repeat_mode);
460e6188e58Smrg	if (src->repeat_mode == G2D_REPEAT_MODE_PAD)
461e6188e58Smrg		g2d_add_cmd(ctx, SRC_PAD_VALUE_REG, dst->color);
462e6188e58Smrg
463e6188e58Smrg	g2d_add_base_addr(ctx, src, g2d_src);
464e88f27b3Smrg	g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride);
465e88f27b3Smrg
466e88f27b3Smrg	if (src_w == dst_w && src_h == dst_h)
467e88f27b3Smrg		scale = 0;
468e88f27b3Smrg	else {
469e88f27b3Smrg		scale = 1;
470e6188e58Smrg		scale_x = g2d_get_scaling(src_w, dst_w);
471e6188e58Smrg		scale_y = g2d_get_scaling(src_h, dst_h);
472e88f27b3Smrg	}
473e88f27b3Smrg
474e88f27b3Smrg	if (src_x + src_w > src->width)
475e88f27b3Smrg		src_w = src->width - src_x;
476e88f27b3Smrg	if (src_y + src_h > src->height)
477e88f27b3Smrg		src_h = src->height - src_y;
478e88f27b3Smrg
479e88f27b3Smrg	if (dst_x + dst_w > dst->width)
480e88f27b3Smrg		dst_w = dst->width - dst_x;
481e88f27b3Smrg	if (dst_y + dst_h > dst->height)
482e88f27b3Smrg		dst_h = dst->height - dst_y;
483e88f27b3Smrg
484e88f27b3Smrg	if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) {
485e88f27b3Smrg		fprintf(stderr, "invalid width or height.\n");
486e88f27b3Smrg		g2d_reset(ctx);
487e88f27b3Smrg		return -EINVAL;
488e88f27b3Smrg	}
489e88f27b3Smrg
490e88f27b3Smrg	if (negative) {
491e88f27b3Smrg		g2d_add_cmd(ctx, BG_COLOR_REG, 0x00FFFFFF);
492e88f27b3Smrg		rop4.val = 0;
493e88f27b3Smrg		rop4.data.unmasked_rop3 = G2D_ROP3_SRC^G2D_ROP3_DST;
494e88f27b3Smrg		g2d_add_cmd(ctx, ROP4_REG, rop4.val);
495e88f27b3Smrg	} else {
496e88f27b3Smrg		rop4.val = 0;
497e88f27b3Smrg		rop4.data.unmasked_rop3 = G2D_ROP3_SRC;
498e88f27b3Smrg		g2d_add_cmd(ctx, ROP4_REG, rop4.val);
499e88f27b3Smrg	}
500e88f27b3Smrg
501e88f27b3Smrg	if (scale) {
502e88f27b3Smrg		g2d_add_cmd(ctx, SRC_SCALE_CTRL_REG, G2D_SCALE_MODE_BILINEAR);
503e6188e58Smrg		g2d_add_cmd(ctx, SRC_XSCALE_REG, scale_x);
504e6188e58Smrg		g2d_add_cmd(ctx, SRC_YSCALE_REG, scale_y);
505e88f27b3Smrg	}
506e88f27b3Smrg
507e88f27b3Smrg	pt.val = 0;
508e88f27b3Smrg	pt.data.x = src_x;
509e88f27b3Smrg	pt.data.y = src_y;
510e88f27b3Smrg	g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
511e88f27b3Smrg	pt.val = 0;
512e88f27b3Smrg	pt.data.x = src_x + src_w;
513e88f27b3Smrg	pt.data.y = src_y + src_h;
514e88f27b3Smrg	g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
515e88f27b3Smrg
516e88f27b3Smrg	pt.val = 0;
517e88f27b3Smrg	pt.data.x = dst_x;
518e88f27b3Smrg	pt.data.y = dst_y;
519e88f27b3Smrg	g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
520e88f27b3Smrg	pt.val = 0;
521e88f27b3Smrg	pt.data.x = dst_x + dst_w;
522e88f27b3Smrg	pt.data.y = dst_y + dst_h;
523e88f27b3Smrg	g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
524e88f27b3Smrg
525e6188e58Smrg	return g2d_flush(ctx);
526e88f27b3Smrg}
527e88f27b3Smrg
528e88f27b3Smrg/**
529e6188e58Smrg * g2d_blend - blend image data in source and destination buffers.
530e88f27b3Smrg *
531e88f27b3Smrg * @ctx: a pointer to g2d_context structure.
532e88f27b3Smrg * @src: a pointer to g2d_image structure including image and buffer
533e88f27b3Smrg *	information to source.
534e88f27b3Smrg * @dst: a pointer to g2d_image structure including image and buffer
535e88f27b3Smrg *	information to destination.
536e88f27b3Smrg * @src_x: x start position to source buffer.
537e88f27b3Smrg * @src_y: y start position to source buffer.
538e88f27b3Smrg * @dst_x: x start position to destination buffer.
539e88f27b3Smrg * @dst_y: y start position to destination buffer.
540e88f27b3Smrg * @w: width value to source and destination buffer.
541e88f27b3Smrg * @h: height value to source and destination buffer.
542e88f27b3Smrg * @op: blend operation type.
543e88f27b3Smrg */
544e6188e58Smrgint
545baaff307Smrgg2d_blend(struct g2d_context *ctx, struct g2d_image *src,
546e88f27b3Smrg		struct g2d_image *dst, unsigned int src_x,
547e88f27b3Smrg		unsigned int src_y, unsigned int dst_x, unsigned int dst_y,
548e88f27b3Smrg		unsigned int w, unsigned int h, enum e_g2d_op op)
549e88f27b3Smrg{
550e88f27b3Smrg	union g2d_point_val pt;
551e88f27b3Smrg	union g2d_bitblt_cmd_val bitblt;
552e88f27b3Smrg	union g2d_blend_func_val blend;
553e88f27b3Smrg	unsigned int src_w = 0, src_h = 0, dst_w = 0, dst_h = 0;
554e88f27b3Smrg
555e88f27b3Smrg	bitblt.val = 0;
556e88f27b3Smrg	blend.val = 0;
557e88f27b3Smrg
558e88f27b3Smrg	if (op == G2D_OP_SRC || op == G2D_OP_CLEAR)
559e88f27b3Smrg		g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
560e88f27b3Smrg	else
561e88f27b3Smrg		g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL);
562e88f27b3Smrg
563e88f27b3Smrg	g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode);
564e6188e58Smrg	g2d_add_base_addr(ctx, dst, g2d_dst);
565e88f27b3Smrg	g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride);
566e88f27b3Smrg
567e88f27b3Smrg	g2d_add_cmd(ctx, SRC_SELECT_REG, src->select_mode);
568e88f27b3Smrg	g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode);
569e88f27b3Smrg
570e88f27b3Smrg	switch (src->select_mode) {
571e88f27b3Smrg	case G2D_SELECT_MODE_NORMAL:
572e6188e58Smrg		g2d_add_base_addr(ctx, src, g2d_src);
573e88f27b3Smrg		g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride);
574e88f27b3Smrg		break;
575e88f27b3Smrg	case G2D_SELECT_MODE_FGCOLOR:
576e88f27b3Smrg		g2d_add_cmd(ctx, FG_COLOR_REG, src->color);
577e88f27b3Smrg		break;
578e88f27b3Smrg	case G2D_SELECT_MODE_BGCOLOR:
579e88f27b3Smrg		g2d_add_cmd(ctx, BG_COLOR_REG, src->color);
580e88f27b3Smrg		break;
581e88f27b3Smrg	default:
582e88f27b3Smrg		fprintf(stderr , "failed to set src.\n");
583e88f27b3Smrg		return -EINVAL;
584e88f27b3Smrg	}
585e88f27b3Smrg
586e88f27b3Smrg	src_w = w;
587e88f27b3Smrg	src_h = h;
588e88f27b3Smrg	if (src_x + w > src->width)
589e88f27b3Smrg		src_w = src->width - src_x;
590e88f27b3Smrg	if (src_y + h > src->height)
591e88f27b3Smrg		src_h = src->height - src_y;
592e88f27b3Smrg
593e88f27b3Smrg	dst_w = w;
594e88f27b3Smrg	dst_h = h;
595e88f27b3Smrg	if (dst_x + w > dst->width)
596e88f27b3Smrg		dst_w = dst->width - dst_x;
597e88f27b3Smrg	if (dst_y + h > dst->height)
598e88f27b3Smrg		dst_h = dst->height - dst_y;
599e88f27b3Smrg
600e88f27b3Smrg	w = MIN(src_w, dst_w);
601e88f27b3Smrg	h = MIN(src_h, dst_h);
602e88f27b3Smrg
603e88f27b3Smrg	if (w <= 0 || h <= 0) {
604e88f27b3Smrg		fprintf(stderr, "invalid width or height.\n");
605e88f27b3Smrg		g2d_reset(ctx);
606e88f27b3Smrg		return -EINVAL;
607e88f27b3Smrg	}
608e88f27b3Smrg
609e88f27b3Smrg	bitblt.data.alpha_blend_mode = G2D_ALPHA_BLEND_MODE_ENABLE;
610e88f27b3Smrg	blend.val = g2d_get_blend_op(op);
611e88f27b3Smrg	g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val);
612e88f27b3Smrg	g2d_add_cmd(ctx, BLEND_FUNCTION_REG, blend.val);
613e88f27b3Smrg
614e88f27b3Smrg	pt.val = 0;
615e88f27b3Smrg	pt.data.x = src_x;
616e88f27b3Smrg	pt.data.y = src_y;
617e88f27b3Smrg	g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
618e88f27b3Smrg	pt.val = 0;
619e88f27b3Smrg	pt.data.x = src_x + w;
620e88f27b3Smrg	pt.data.y = src_y + h;
621e88f27b3Smrg	g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
622e88f27b3Smrg
623e88f27b3Smrg	pt.val = 0;
624e88f27b3Smrg	pt.data.x = dst_x;
625e88f27b3Smrg	pt.data.y = dst_y;
626e88f27b3Smrg	g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
627e88f27b3Smrg	pt.val = 0;
628e88f27b3Smrg	pt.data.x = dst_x + w;
629e88f27b3Smrg	pt.data.y = dst_y + h;
630e88f27b3Smrg	g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
631e88f27b3Smrg
632e6188e58Smrg	return g2d_flush(ctx);
633e88f27b3Smrg}
634e88f27b3Smrg
635e6188e58Smrg/**
636e6188e58Smrg * g2d_scale_and_blend - apply scaling to source buffer and then blend to destination buffer
637e6188e58Smrg *
638e6188e58Smrg * @ctx: a pointer to g2d_context structure.
639e6188e58Smrg * @src: a pointer to g2d_image structure including image and buffer
640e6188e58Smrg *	information to source.
641e6188e58Smrg * @dst: a pointer to g2d_image structure including image and buffer
642e6188e58Smrg *	information to destination.
643e6188e58Smrg * @src_x: x start position to source buffer.
644e6188e58Smrg * @src_y: y start position to source buffer.
645e6188e58Smrg * @src_w: width value to source buffer.
646e6188e58Smrg * @src_h: height value to source buffer.
647e6188e58Smrg * @dst_x: x start position to destination buffer.
648e6188e58Smrg * @dst_y: y start position to destination buffer.
649e6188e58Smrg * @dst_w: width value to destination buffer.
650e6188e58Smrg * @dst_h: height value to destination buffer.
651e6188e58Smrg * @op: blend operation type.
652e6188e58Smrg */
653e6188e58Smrgint
654e6188e58Smrgg2d_scale_and_blend(struct g2d_context *ctx, struct g2d_image *src,
655e6188e58Smrg		struct g2d_image *dst, unsigned int src_x, unsigned int src_y,
656e6188e58Smrg		unsigned int src_w, unsigned int src_h, unsigned int dst_x,
657e6188e58Smrg		unsigned int dst_y, unsigned int dst_w, unsigned int dst_h,
658e6188e58Smrg		enum e_g2d_op op)
659e6188e58Smrg{
660e6188e58Smrg	union g2d_point_val pt;
661e6188e58Smrg	union g2d_bitblt_cmd_val bitblt;
662e6188e58Smrg	union g2d_blend_func_val blend;
663e6188e58Smrg	unsigned int scale;
664e6188e58Smrg	unsigned int scale_x, scale_y;
665e6188e58Smrg
666e6188e58Smrg	bitblt.val = 0;
667e6188e58Smrg	blend.val = 0;
668e6188e58Smrg
669e6188e58Smrg	if (op == G2D_OP_SRC || op == G2D_OP_CLEAR)
670e6188e58Smrg		g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
671e6188e58Smrg	else
672e6188e58Smrg		g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL);
673e6188e58Smrg
674e6188e58Smrg	g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode);
675e6188e58Smrg	if (dst->buf_type == G2D_IMGBUF_USERPTR)
676e6188e58Smrg		g2d_add_cmd(ctx, DST_BASE_ADDR_REG | G2D_BUF_USERPTR,
677e6188e58Smrg				(unsigned long)&dst->user_ptr[0]);
678e6188e58Smrg	else
679e6188e58Smrg		g2d_add_cmd(ctx, DST_BASE_ADDR_REG, dst->bo[0]);
680e6188e58Smrg
681e6188e58Smrg	g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride);
682e6188e58Smrg
683e6188e58Smrg	g2d_add_cmd(ctx, SRC_SELECT_REG, src->select_mode);
684e6188e58Smrg	g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode);
685e6188e58Smrg
686e6188e58Smrg	switch (src->select_mode) {
687e6188e58Smrg	case G2D_SELECT_MODE_NORMAL:
688e6188e58Smrg		if (src->buf_type == G2D_IMGBUF_USERPTR)
689e6188e58Smrg			g2d_add_cmd(ctx, SRC_BASE_ADDR_REG | G2D_BUF_USERPTR,
690e6188e58Smrg					(unsigned long)&src->user_ptr[0]);
691e6188e58Smrg		else
692e6188e58Smrg			g2d_add_cmd(ctx, SRC_BASE_ADDR_REG, src->bo[0]);
693e6188e58Smrg
694e6188e58Smrg		g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride);
695e6188e58Smrg		break;
696e6188e58Smrg	case G2D_SELECT_MODE_FGCOLOR:
697e6188e58Smrg		g2d_add_cmd(ctx, FG_COLOR_REG, src->color);
698e6188e58Smrg		break;
699e6188e58Smrg	case G2D_SELECT_MODE_BGCOLOR:
700e6188e58Smrg		g2d_add_cmd(ctx, BG_COLOR_REG, src->color);
701e6188e58Smrg		break;
702e6188e58Smrg	default:
703e6188e58Smrg		fprintf(stderr , "failed to set src.\n");
704e6188e58Smrg		return -EINVAL;
705e6188e58Smrg	}
706e6188e58Smrg
707e6188e58Smrg	if (src_w == dst_w && src_h == dst_h)
708e6188e58Smrg		scale = 0;
709e6188e58Smrg	else {
710e6188e58Smrg		scale = 1;
711e6188e58Smrg		scale_x = g2d_get_scaling(src_w, dst_w);
712e6188e58Smrg		scale_y = g2d_get_scaling(src_h, dst_h);
713e6188e58Smrg	}
714e6188e58Smrg
715e6188e58Smrg	if (src_x + src_w > src->width)
716e6188e58Smrg		src_w = src->width - src_x;
717e6188e58Smrg	if (src_y + src_h > src->height)
718e6188e58Smrg		src_h = src->height - src_y;
719e6188e58Smrg
720e6188e58Smrg	if (dst_x + dst_w > dst->width)
721e6188e58Smrg		dst_w = dst->width - dst_x;
722e6188e58Smrg	if (dst_y + dst_h > dst->height)
723e6188e58Smrg		dst_h = dst->height - dst_y;
724e6188e58Smrg
725e6188e58Smrg	if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) {
726e6188e58Smrg		fprintf(stderr, "invalid width or height.\n");
727e6188e58Smrg		g2d_reset(ctx);
728e6188e58Smrg		return -EINVAL;
729e6188e58Smrg	}
730e6188e58Smrg
731e6188e58Smrg	if (scale) {
732e6188e58Smrg		g2d_add_cmd(ctx, SRC_SCALE_CTRL_REG, G2D_SCALE_MODE_BILINEAR);
733e6188e58Smrg		g2d_add_cmd(ctx, SRC_XSCALE_REG, scale_x);
734e6188e58Smrg		g2d_add_cmd(ctx, SRC_YSCALE_REG, scale_y);
735e6188e58Smrg	}
736e6188e58Smrg
737e6188e58Smrg	bitblt.data.alpha_blend_mode = G2D_ALPHA_BLEND_MODE_ENABLE;
738e6188e58Smrg	blend.val = g2d_get_blend_op(op);
739e6188e58Smrg	g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val);
740e6188e58Smrg	g2d_add_cmd(ctx, BLEND_FUNCTION_REG, blend.val);
741e6188e58Smrg
742e6188e58Smrg	pt.val = 0;
743e6188e58Smrg	pt.data.x = src_x;
744e6188e58Smrg	pt.data.y = src_y;
745e6188e58Smrg	g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
746e6188e58Smrg	pt.val = 0;
747e6188e58Smrg	pt.data.x = src_x + src_w;
748e6188e58Smrg	pt.data.y = src_y + src_h;
749e6188e58Smrg	g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
750e6188e58Smrg
751e6188e58Smrg	pt.val = 0;
752e6188e58Smrg	pt.data.x = dst_x;
753e6188e58Smrg	pt.data.y = dst_y;
754e6188e58Smrg	g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
755e6188e58Smrg	pt.val = 0;
756e6188e58Smrg	pt.data.x = dst_x + dst_w;
757e6188e58Smrg	pt.data.y = dst_y + dst_h;
758e6188e58Smrg	g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
759e6188e58Smrg
760e6188e58Smrg	return g2d_flush(ctx);
761e6188e58Smrg}
762