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