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