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