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