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