exynos_fimg2d.c revision d8807b2f
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_flush - submit all commands and values in user side command buffer
297 *		to command queue aware of fimg2d dma.
298 *
299 * @ctx: a pointer to g2d_context structure.
300 *
301 * This function should be called after all commands and values to user
302 * side command buffer are set. It submits that buffer to the kernel side driver.
303 */
304static int g2d_flush(struct g2d_context *ctx)
305{
306	int ret;
307	struct drm_exynos_g2d_set_cmdlist cmdlist = {0};
308
309	if (ctx->cmd_nr == 0 && ctx->cmd_buf_nr == 0)
310		return 0;
311
312	if (ctx->cmdlist_nr >= G2D_MAX_CMD_LIST_NR) {
313		fprintf(stderr, MSG_PREFIX "command list overflow.\n");
314		return -EINVAL;
315	}
316
317	cmdlist.cmd = (uint64_t)(uintptr_t)&ctx->cmd[0];
318	cmdlist.cmd_buf = (uint64_t)(uintptr_t)&ctx->cmd_buf[0];
319	cmdlist.cmd_nr = ctx->cmd_nr;
320	cmdlist.cmd_buf_nr = ctx->cmd_buf_nr;
321
322	if (ctx->event_userdata) {
323		cmdlist.event_type = G2D_EVENT_NONSTOP;
324		cmdlist.user_data = (uint64_t)(uintptr_t)(ctx->event_userdata);
325		ctx->event_userdata = NULL;
326	} else {
327		cmdlist.event_type = G2D_EVENT_NOT;
328		cmdlist.user_data = 0;
329	}
330
331	ctx->cmd_nr = 0;
332	ctx->cmd_buf_nr = 0;
333
334	ret = drmIoctl(ctx->fd, DRM_IOCTL_EXYNOS_G2D_SET_CMDLIST, &cmdlist);
335	if (ret < 0) {
336		fprintf(stderr, MSG_PREFIX "failed to set cmdlist.\n");
337		return ret;
338	}
339
340	ctx->cmdlist_nr++;
341
342	return ret;
343}
344
345/**
346 * g2d_init - create a new g2d context and get hardware version.
347 *
348 * fd: a file descriptor to an opened drm device.
349 */
350struct g2d_context *g2d_init(int fd)
351{
352	struct drm_exynos_g2d_get_ver ver;
353	struct g2d_context *ctx;
354	int ret;
355
356	ctx = calloc(1, sizeof(*ctx));
357	if (!ctx) {
358		fprintf(stderr, MSG_PREFIX "failed to allocate context.\n");
359		return NULL;
360	}
361
362	ctx->fd = fd;
363
364	ret = drmIoctl(fd, DRM_IOCTL_EXYNOS_G2D_GET_VER, &ver);
365	if (ret < 0) {
366		fprintf(stderr, MSG_PREFIX "failed to get version.\n");
367		free(ctx);
368		return NULL;
369	}
370
371	ctx->major = ver.major;
372	ctx->minor = ver.minor;
373
374	printf(MSG_PREFIX "G2D version (%d.%d).\n", ctx->major, ctx->minor);
375	return ctx;
376}
377
378void g2d_fini(struct g2d_context *ctx)
379{
380	free(ctx);
381}
382
383/**
384 * g2d_config_event - setup userdata configuration for a g2d event.
385 *		The next invocation of a g2d call (e.g. g2d_solid_fill) is
386 *		then going to flag the command buffer as 'nonstop'.
387 *		Completion of the command buffer execution can then be
388 *		determined by using drmHandleEvent on the DRM fd.
389 *		The userdata is 'consumed' in the process.
390 *
391 * @ctx: a pointer to g2d_context structure.
392 * @userdata: a pointer to the user data
393 */
394void g2d_config_event(struct g2d_context *ctx, void *userdata)
395{
396	ctx->event_userdata = userdata;
397}
398
399/**
400 * g2d_exec - start the dma to process all commands summited by g2d_flush().
401 *
402 * @ctx: a pointer to g2d_context structure.
403 */
404int g2d_exec(struct g2d_context *ctx)
405{
406	struct drm_exynos_g2d_exec exec;
407	int ret;
408
409	if (ctx->cmdlist_nr == 0)
410		return -EINVAL;
411
412	exec.async = 0;
413
414	ret = drmIoctl(ctx->fd, DRM_IOCTL_EXYNOS_G2D_EXEC, &exec);
415	if (ret < 0) {
416		fprintf(stderr, MSG_PREFIX "failed to execute.\n");
417		return ret;
418	}
419
420	ctx->cmdlist_nr = 0;
421
422	return ret;
423}
424
425/**
426 * g2d_solid_fill - fill given buffer with given color data.
427 *
428 * @ctx: a pointer to g2d_context structure.
429 * @img: a pointer to g2d_image structure including image and buffer
430 *	information.
431 * @x: x start position to buffer filled with given color data.
432 * @y: y start position to buffer filled with given color data.
433 * @w: width value to buffer filled with given color data.
434 * @h: height value to buffer filled with given color data.
435 */
436int
437g2d_solid_fill(struct g2d_context *ctx, struct g2d_image *img,
438			unsigned int x, unsigned int y, unsigned int w,
439			unsigned int h)
440{
441	union g2d_bitblt_cmd_val bitblt;
442	union g2d_point_val pt;
443
444	if (g2d_check_space(ctx, 7, 1))
445		return -ENOSPC;
446
447	g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL);
448	g2d_add_cmd(ctx, DST_COLOR_MODE_REG, img->color_mode);
449	g2d_add_base_addr(ctx, img, g2d_dst);
450	g2d_add_cmd(ctx, DST_STRIDE_REG, img->stride);
451
452	if (x + w > img->width)
453		w = img->width - x;
454	if (y + h > img->height)
455		h = img->height - y;
456
457	pt.data.x = x;
458	pt.data.y = y;
459	g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
460
461	pt.data.x = x + w;
462	pt.data.y = y + h;
463	g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
464
465	g2d_add_cmd(ctx, SF_COLOR_REG, img->color);
466
467	bitblt.val = 0;
468	bitblt.data.fast_solid_color_fill_en = 1;
469	g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val);
470
471	return g2d_flush(ctx);
472}
473
474/**
475 * g2d_copy - copy contents in source buffer to destination buffer.
476 *
477 * @ctx: a pointer to g2d_context structure.
478 * @src: a pointer to g2d_image structure including image and buffer
479 *	information to source.
480 * @dst: a pointer to g2d_image structure including image and buffer
481 *	information to destination.
482 * @src_x: x start position to source buffer.
483 * @src_y: y start position to source buffer.
484 * @dst_x: x start position to destination buffer.
485 * @dst_y: y start position to destination buffer.
486 * @w: width value to source and destination buffers.
487 * @h: height value to source and destination buffers.
488 */
489int
490g2d_copy(struct g2d_context *ctx, struct g2d_image *src,
491		struct g2d_image *dst, unsigned int src_x, unsigned int src_y,
492		unsigned int dst_x, unsigned dst_y, unsigned int w,
493		unsigned int h)
494{
495	union g2d_rop4_val rop4;
496	union g2d_point_val pt;
497	unsigned int src_w, src_h, dst_w, dst_h;
498
499	src_w = w;
500	src_h = h;
501	if (src_x + src->width > w)
502		src_w = src->width - src_x;
503	if (src_y + src->height > h)
504		src_h = src->height - src_y;
505
506	dst_w = w;
507	dst_h = w;
508	if (dst_x + dst->width > w)
509		dst_w = dst->width - dst_x;
510	if (dst_y + dst->height > h)
511		dst_h = dst->height - dst_y;
512
513	w = MIN(src_w, dst_w);
514	h = MIN(src_h, dst_h);
515
516	if (w <= 0 || h <= 0) {
517		fprintf(stderr, MSG_PREFIX "invalid width or height.\n");
518		return -EINVAL;
519	}
520
521	if (g2d_check_space(ctx, 11, 2))
522		return -ENOSPC;
523
524	g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
525	g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode);
526	g2d_add_base_addr(ctx, dst, g2d_dst);
527	g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride);
528
529	g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL);
530	g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode);
531	g2d_add_base_addr(ctx, src, g2d_src);
532	g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride);
533
534	pt.data.x = src_x;
535	pt.data.y = src_y;
536	g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
537	pt.data.x = src_x + w;
538	pt.data.y = src_y + h;
539	g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
540
541	pt.data.x = dst_x;
542	pt.data.y = dst_y;
543	g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
544	pt.data.x = dst_x + w;
545	pt.data.y = dst_y + h;
546	g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
547
548	rop4.val = 0;
549	rop4.data.unmasked_rop3 = G2D_ROP3_SRC;
550	g2d_add_cmd(ctx, ROP4_REG, rop4.val);
551
552	return g2d_flush(ctx);
553}
554
555/**
556 * g2d_move - copy content inside single buffer.
557 *	Similar to libc's memmove() this copies a rectangular
558 *	region of the provided buffer to another location, while
559 *	properly handling the situation where source and
560 *	destination rectangle overlap.
561 *
562 * @ctx: a pointer to g2d_context structure.
563 * @img: a pointer to g2d_image structure providing
564 *	buffer information.
565 * @src_x: x position of source rectangle.
566 * @src_y: y position of source rectangle.
567 * @dst_x: x position of destination rectangle.
568 * @dst_y: y position of destination rectangle.
569 * @w: width of rectangle to move.
570 * @h: height of rectangle to move.
571 */
572int
573g2d_move(struct g2d_context *ctx, struct g2d_image *img,
574		unsigned int src_x, unsigned int src_y,
575		unsigned int dst_x, unsigned dst_y, unsigned int w,
576		unsigned int h)
577{
578	union g2d_rop4_val rop4;
579	union g2d_point_val pt;
580	union g2d_direction_val dir;
581	unsigned int src_w, src_h, dst_w, dst_h;
582
583	src_w = w;
584	src_h = h;
585	if (src_x + img->width > w)
586		src_w = img->width - src_x;
587	if (src_y + img->height > h)
588		src_h = img->height - src_y;
589
590	dst_w = w;
591	dst_h = w;
592	if (dst_x + img->width > w)
593		dst_w = img->width - dst_x;
594	if (dst_y + img->height > h)
595		dst_h = img->height - dst_y;
596
597	w = MIN(src_w, dst_w);
598	h = MIN(src_h, dst_h);
599
600	if (w == 0 || h == 0) {
601		fprintf(stderr, MSG_PREFIX "invalid width or height.\n");
602		return -EINVAL;
603	}
604
605	if (g2d_check_space(ctx, 13, 2))
606		return -ENOSPC;
607
608	g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
609	g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL);
610
611	g2d_add_cmd(ctx, DST_COLOR_MODE_REG, img->color_mode);
612	g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, img->color_mode);
613
614	g2d_add_base_addr(ctx, img, g2d_dst);
615	g2d_add_base_addr(ctx, img, g2d_src);
616
617	g2d_add_cmd(ctx, DST_STRIDE_REG, img->stride);
618	g2d_add_cmd(ctx, SRC_STRIDE_REG, img->stride);
619
620	dir.val[0] = dir.val[1] = 0;
621
622	if (dst_x >= src_x)
623		dir.data.src_x_direction = dir.data.dst_x_direction = 1;
624	if (dst_y >= src_y)
625		dir.data.src_y_direction = dir.data.dst_y_direction = 1;
626
627	g2d_set_direction(ctx, &dir);
628
629	pt.data.x = src_x;
630	pt.data.y = src_y;
631	g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
632	pt.data.x = src_x + w;
633	pt.data.y = src_y + h;
634	g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
635
636	pt.data.x = dst_x;
637	pt.data.y = dst_y;
638	g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
639	pt.data.x = dst_x + w;
640	pt.data.y = dst_y + h;
641	g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
642
643	rop4.val = 0;
644	rop4.data.unmasked_rop3 = G2D_ROP3_SRC;
645	g2d_add_cmd(ctx, ROP4_REG, rop4.val);
646
647	return g2d_flush(ctx);
648}
649
650/**
651 * g2d_copy_with_scale - copy contents in source buffer to destination buffer
652 *	scaling up or down properly.
653 *
654 * @ctx: a pointer to g2d_context structure.
655 * @src: a pointer to g2d_image structure including image and buffer
656 *	information to source.
657 * @dst: a pointer to g2d_image structure including image and buffer
658 *	information to destination.
659 * @src_x: x start position to source buffer.
660 * @src_y: y start position to source buffer.
661 * @src_w: width value to source buffer.
662 * @src_h: height value to source buffer.
663 * @dst_x: x start position to destination buffer.
664 * @dst_y: y start position to destination buffer.
665 * @dst_w: width value to destination buffer.
666 * @dst_h: height value to destination buffer.
667 * @negative: indicate that it uses color negative to source and
668 *	destination buffers.
669 */
670int
671g2d_copy_with_scale(struct g2d_context *ctx, struct g2d_image *src,
672				struct g2d_image *dst, unsigned int src_x,
673				unsigned int src_y, unsigned int src_w,
674				unsigned int src_h, unsigned int dst_x,
675				unsigned int dst_y, unsigned int dst_w,
676				unsigned int dst_h, unsigned int negative)
677{
678	union g2d_rop4_val rop4;
679	union g2d_point_val pt;
680	unsigned int scale, repeat_pad;
681	unsigned int scale_x, scale_y;
682
683	/* Sanitize this parameter to facilitate space computation below. */
684	if (negative)
685		negative = 1;
686
687	if (src_w == dst_w && src_h == dst_h)
688		scale = 0;
689	else {
690		scale = 1;
691		scale_x = g2d_get_scaling(src_w, dst_w);
692		scale_y = g2d_get_scaling(src_h, dst_h);
693	}
694
695	repeat_pad = src->repeat_mode == G2D_REPEAT_MODE_PAD ? 1 : 0;
696
697	if (src_x + src_w > src->width)
698		src_w = src->width - src_x;
699	if (src_y + src_h > src->height)
700		src_h = src->height - src_y;
701
702	if (dst_x + dst_w > dst->width)
703		dst_w = dst->width - dst_x;
704	if (dst_y + dst_h > dst->height)
705		dst_h = dst->height - dst_y;
706
707	if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) {
708		fprintf(stderr, MSG_PREFIX "invalid width or height.\n");
709		return -EINVAL;
710	}
711
712	if (g2d_check_space(ctx, 12 + scale * 3 + negative + repeat_pad, 2))
713		return -ENOSPC;
714
715	g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
716	g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode);
717	g2d_add_base_addr(ctx, dst, g2d_dst);
718	g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride);
719
720	g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL);
721	g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode);
722
723	g2d_add_cmd(ctx, SRC_REPEAT_MODE_REG, src->repeat_mode);
724	if (repeat_pad)
725		g2d_add_cmd(ctx, SRC_PAD_VALUE_REG, dst->color);
726
727	g2d_add_base_addr(ctx, src, g2d_src);
728	g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride);
729
730	rop4.val = 0;
731	rop4.data.unmasked_rop3 = G2D_ROP3_SRC;
732
733	if (negative) {
734		g2d_add_cmd(ctx, BG_COLOR_REG, 0x00FFFFFF);
735		rop4.data.unmasked_rop3 ^= G2D_ROP3_DST;
736	}
737
738	g2d_add_cmd(ctx, ROP4_REG, rop4.val);
739
740	if (scale) {
741		g2d_add_cmd(ctx, SRC_SCALE_CTRL_REG, G2D_SCALE_MODE_BILINEAR);
742		g2d_add_cmd(ctx, SRC_XSCALE_REG, scale_x);
743		g2d_add_cmd(ctx, SRC_YSCALE_REG, scale_y);
744	}
745
746	pt.data.x = src_x;
747	pt.data.y = src_y;
748	g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
749	pt.data.x = src_x + src_w;
750	pt.data.y = src_y + src_h;
751	g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
752
753	pt.data.x = dst_x;
754	pt.data.y = dst_y;
755	g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
756	pt.data.x = dst_x + dst_w;
757	pt.data.y = dst_y + dst_h;
758	g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
759
760	return g2d_flush(ctx);
761}
762
763/**
764 * g2d_blend - blend image data in source and destination buffers.
765 *
766 * @ctx: a pointer to g2d_context structure.
767 * @src: a pointer to g2d_image structure including image and buffer
768 *	information to source.
769 * @dst: a pointer to g2d_image structure including image and buffer
770 *	information to destination.
771 * @src_x: x start position to source buffer.
772 * @src_y: y start position to source buffer.
773 * @dst_x: x start position to destination buffer.
774 * @dst_y: y start position to destination buffer.
775 * @w: width value to source and destination buffer.
776 * @h: height value to source and destination buffer.
777 * @op: blend operation type.
778 */
779int
780g2d_blend(struct g2d_context *ctx, struct g2d_image *src,
781		struct g2d_image *dst, unsigned int src_x,
782		unsigned int src_y, unsigned int dst_x, unsigned int dst_y,
783		unsigned int w, unsigned int h, enum e_g2d_op op)
784{
785	union g2d_point_val pt;
786	union g2d_bitblt_cmd_val bitblt;
787	union g2d_blend_func_val blend;
788	unsigned int gem_space;
789	unsigned int src_w, src_h, dst_w, dst_h;
790
791	src_w = w;
792	src_h = h;
793	if (src_x + w > src->width)
794		src_w = src->width - src_x;
795	if (src_y + h > src->height)
796		src_h = src->height - src_y;
797
798	dst_w = w;
799	dst_h = h;
800	if (dst_x + w > dst->width)
801		dst_w = dst->width - dst_x;
802	if (dst_y + h > dst->height)
803		dst_h = dst->height - dst_y;
804
805	w = MIN(src_w, dst_w);
806	h = MIN(src_h, dst_h);
807
808	if (w <= 0 || h <= 0) {
809		fprintf(stderr, MSG_PREFIX "invalid width or height.\n");
810		return -EINVAL;
811	}
812
813	if (!g2d_validate_select_mode(src->select_mode)) {
814		fprintf(stderr , MSG_PREFIX "invalid select mode for source.\n");
815		return -EINVAL;
816	}
817
818	if (!g2d_validate_blending_op(op)) {
819		fprintf(stderr , MSG_PREFIX "unsupported blending operation.\n");
820		return -EINVAL;
821	}
822
823	gem_space = src->select_mode == G2D_SELECT_MODE_NORMAL ? 2 : 1;
824
825	if (g2d_check_space(ctx, 12, gem_space))
826		return -ENOSPC;
827
828	bitblt.val = 0;
829	blend.val = 0;
830
831	if (op == G2D_OP_SRC || op == G2D_OP_CLEAR)
832		g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
833	else
834		g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL);
835
836	g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode);
837	g2d_add_base_addr(ctx, dst, g2d_dst);
838	g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride);
839
840	g2d_add_cmd(ctx, SRC_SELECT_REG, src->select_mode);
841	g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode);
842
843	switch (src->select_mode) {
844	case G2D_SELECT_MODE_NORMAL:
845		g2d_add_base_addr(ctx, src, g2d_src);
846		g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride);
847		break;
848	case G2D_SELECT_MODE_FGCOLOR:
849		g2d_add_cmd(ctx, FG_COLOR_REG, src->color);
850		break;
851	case G2D_SELECT_MODE_BGCOLOR:
852		g2d_add_cmd(ctx, BG_COLOR_REG, src->color);
853		break;
854	}
855
856	bitblt.data.alpha_blend_mode = G2D_ALPHA_BLEND_MODE_ENABLE;
857	blend.val = g2d_get_blend_op(op);
858	g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val);
859	g2d_add_cmd(ctx, BLEND_FUNCTION_REG, blend.val);
860
861	pt.data.x = src_x;
862	pt.data.y = src_y;
863	g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
864	pt.data.x = src_x + w;
865	pt.data.y = src_y + h;
866	g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
867
868	pt.data.x = dst_x;
869	pt.data.y = dst_y;
870	g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
871	pt.data.x = dst_x + w;
872	pt.data.y = dst_y + h;
873	g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
874
875	return g2d_flush(ctx);
876}
877
878/**
879 * g2d_scale_and_blend - apply scaling to source buffer and then blend to destination buffer
880 *
881 * @ctx: a pointer to g2d_context structure.
882 * @src: a pointer to g2d_image structure including image and buffer
883 *	information to source.
884 * @dst: a pointer to g2d_image structure including image and buffer
885 *	information to destination.
886 * @src_x: x start position to source buffer.
887 * @src_y: y start position to source buffer.
888 * @src_w: width value to source buffer.
889 * @src_h: height value to source buffer.
890 * @dst_x: x start position to destination buffer.
891 * @dst_y: y start position to destination buffer.
892 * @dst_w: width value to destination buffer.
893 * @dst_h: height value to destination buffer.
894 * @op: blend operation type.
895 */
896int
897g2d_scale_and_blend(struct g2d_context *ctx, struct g2d_image *src,
898		struct g2d_image *dst, unsigned int src_x, unsigned int src_y,
899		unsigned int src_w, unsigned int src_h, unsigned int dst_x,
900		unsigned int dst_y, unsigned int dst_w, unsigned int dst_h,
901		enum e_g2d_op op)
902{
903	union g2d_point_val pt;
904	union g2d_bitblt_cmd_val bitblt;
905	union g2d_blend_func_val blend;
906	unsigned int scale, gem_space;
907	unsigned int scale_x, scale_y;
908
909	if (src_w == dst_w && src_h == dst_h)
910		scale = 0;
911	else {
912		scale = 1;
913		scale_x = g2d_get_scaling(src_w, dst_w);
914		scale_y = g2d_get_scaling(src_h, dst_h);
915	}
916
917	if (src_x + src_w > src->width)
918		src_w = src->width - src_x;
919	if (src_y + src_h > src->height)
920		src_h = src->height - src_y;
921
922	if (dst_x + dst_w > dst->width)
923		dst_w = dst->width - dst_x;
924	if (dst_y + dst_h > dst->height)
925		dst_h = dst->height - dst_y;
926
927	if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) {
928		fprintf(stderr, MSG_PREFIX "invalid width or height.\n");
929		return -EINVAL;
930	}
931
932	if (!g2d_validate_select_mode(src->select_mode)) {
933		fprintf(stderr , MSG_PREFIX "invalid select mode for source.\n");
934		return -EINVAL;
935	}
936
937	if (!g2d_validate_blending_op(op)) {
938		fprintf(stderr , MSG_PREFIX "unsupported blending operation.\n");
939		return -EINVAL;
940	}
941
942	gem_space = src->select_mode == G2D_SELECT_MODE_NORMAL ? 2 : 1;
943
944	if (g2d_check_space(ctx, 12 + scale * 3, gem_space))
945		return -ENOSPC;
946
947	bitblt.val = 0;
948	blend.val = 0;
949
950	if (op == G2D_OP_SRC || op == G2D_OP_CLEAR)
951		g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
952	else
953		g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL);
954
955	g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode);
956	g2d_add_base_addr(ctx, dst, g2d_dst);
957	g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride);
958
959	g2d_add_cmd(ctx, SRC_SELECT_REG, src->select_mode);
960	g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode);
961
962	switch (src->select_mode) {
963	case G2D_SELECT_MODE_NORMAL:
964		g2d_add_base_addr(ctx, src, g2d_src);
965		g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride);
966		break;
967	case G2D_SELECT_MODE_FGCOLOR:
968		g2d_add_cmd(ctx, FG_COLOR_REG, src->color);
969		break;
970	case G2D_SELECT_MODE_BGCOLOR:
971		g2d_add_cmd(ctx, BG_COLOR_REG, src->color);
972		break;
973	}
974
975	if (scale) {
976		g2d_add_cmd(ctx, SRC_SCALE_CTRL_REG, G2D_SCALE_MODE_BILINEAR);
977		g2d_add_cmd(ctx, SRC_XSCALE_REG, scale_x);
978		g2d_add_cmd(ctx, SRC_YSCALE_REG, scale_y);
979	}
980
981	bitblt.data.alpha_blend_mode = G2D_ALPHA_BLEND_MODE_ENABLE;
982	blend.val = g2d_get_blend_op(op);
983	g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val);
984	g2d_add_cmd(ctx, BLEND_FUNCTION_REG, blend.val);
985
986	pt.data.x = src_x;
987	pt.data.y = src_y;
988	g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
989	pt.data.x = src_x + src_w;
990	pt.data.y = src_y + src_h;
991	g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
992
993	pt.data.x = dst_x;
994	pt.data.y = dst_y;
995	g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
996	pt.data.x = dst_x + dst_w;
997	pt.data.y = dst_y + dst_h;
998	g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
999
1000	return g2d_flush(ctx);
1001}
1002