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