OSDN Git Service

bca884b9ee01392add5d333699a14b4f7607d5f6
[android-x86/external-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  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice (including the next
14  * paragraph) shall be included in all copies or substantial portions of the
15  * Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23  * OTHER DEALINGS IN THE SOFTWARE.
24  */
25
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <assert.h>
31
32 #include <sys/mman.h>
33 #include <linux/stddef.h>
34
35 #include <xf86drm.h>
36
37 #include "libdrm_macros.h"
38 #include "exynos_drm.h"
39 #include "fimg2d_reg.h"
40 #include "exynos_fimg2d.h"
41
42 #define         SET_BF(val, sc, si, scsa, scda, dc, di, dcsa, dcda) \
43                         val.data.src_coeff = sc;                \
44                         val.data.inv_src_color_coeff = si;      \
45                         val.data.src_coeff_src_a = scsa;        \
46                         val.data.src_coeff_dst_a = scda;        \
47                         val.data.dst_coeff = dc;                \
48                         val.data.inv_dst_color_coeff = di;      \
49                         val.data.dst_coeff_src_a = dcsa;        \
50                         val.data.dst_coeff_dst_a = dcda;
51
52 #define MIN(a, b)       ((a) < (b) ? (a) : (b))
53
54 #define MSG_PREFIX "exynos/fimg2d: "
55
56 #define G2D_MAX_CMD_NR          64
57 #define G2D_MAX_GEM_CMD_NR      64
58 #define G2D_MAX_CMD_LIST_NR     64
59
60 struct g2d_context {
61         int                             fd;
62         unsigned int                    major;
63         unsigned int                    minor;
64         struct drm_exynos_g2d_cmd       cmd[G2D_MAX_CMD_NR];
65         struct drm_exynos_g2d_cmd       cmd_buf[G2D_MAX_GEM_CMD_NR];
66         unsigned int                    cmd_nr;
67         unsigned int                    cmd_buf_nr;
68         unsigned int                    cmdlist_nr;
69         void                            *event_userdata;
70 };
71
72 enum g2d_base_addr_reg {
73         g2d_dst = 0,
74         g2d_src
75 };
76
77 enum e_g2d_dir_mode {
78         G2D_DIR_MODE_POSITIVE = 0,
79         G2D_DIR_MODE_NEGATIVE = 1
80 };
81
82 union g2d_direction_val {
83         unsigned int val[2];
84         struct {
85                 /* SRC_MSK_DIRECT_REG [0:1] (source) */
86                 enum e_g2d_dir_mode             src_x_direction:1;
87                 enum e_g2d_dir_mode             src_y_direction:1;
88
89                 /* SRC_MSK_DIRECT_REG [2:3] */
90                 unsigned int                    reversed1:2;
91
92                 /* SRC_MSK_DIRECT_REG [4:5] (mask) */
93                 enum e_g2d_dir_mode             mask_x_direction:1;
94                 enum e_g2d_dir_mode             mask_y_direction:1;
95
96                 /* SRC_MSK_DIRECT_REG [6:31] */
97                 unsigned int                    padding1:26;
98
99                 /* DST_PAT_DIRECT_REG [0:1] (destination) */
100                 enum e_g2d_dir_mode             dst_x_direction:1;
101                 enum e_g2d_dir_mode             dst_y_direction:1;
102
103                 /* DST_PAT_DIRECT_REG [2:3] */
104                 unsigned int                    reversed2:2;
105
106                 /* DST_PAT_DIRECT_REG [4:5] (pattern) */
107                 enum e_g2d_dir_mode             pat_x_direction:1;
108                 enum e_g2d_dir_mode             pat_y_direction:1;
109
110                 /* DST_PAT_DIRECT_REG [6:31] */
111                 unsigned int                    padding2:26;
112         } data;
113 };
114
115 static unsigned int g2d_get_scaling(unsigned int src, unsigned int dst)
116 {
117         /*
118          * The G2D hw scaling factor is a normalized inverse of the scaling factor.
119          * For example: When source width is 100 and destination width is 200
120          * (scaling of 2x), then the hw factor is NC * 100 / 200.
121          * The normalization factor (NC) is 2^16 = 0x10000.
122          */
123
124         return ((src << 16) / dst);
125 }
126
127 static unsigned int g2d_get_blend_op(enum e_g2d_op op)
128 {
129         union g2d_blend_func_val val;
130
131         val.val = 0;
132
133         /*
134          * The switch statement is missing the default branch since
135          * we assume that the caller checks the blending operation
136          * via g2d_validate_blending_op() first.
137          */
138         switch (op) {
139         case G2D_OP_CLEAR:
140         case G2D_OP_DISJOINT_CLEAR:
141         case G2D_OP_CONJOINT_CLEAR:
142                 SET_BF(val, G2D_COEFF_MODE_ZERO, 0, 0, 0, G2D_COEFF_MODE_ZERO,
143                                 0, 0, 0);
144                 break;
145         case G2D_OP_SRC:
146         case G2D_OP_DISJOINT_SRC:
147         case G2D_OP_CONJOINT_SRC:
148                 SET_BF(val, G2D_COEFF_MODE_ONE, 0, 0, 0, G2D_COEFF_MODE_ZERO,
149                                 0, 0, 0);
150                 break;
151         case G2D_OP_DST:
152         case G2D_OP_DISJOINT_DST:
153         case G2D_OP_CONJOINT_DST:
154                 SET_BF(val, G2D_COEFF_MODE_ZERO, 0, 0, 0, G2D_COEFF_MODE_ONE,
155                                 0, 0, 0);
156                 break;
157         case G2D_OP_OVER:
158                 SET_BF(val, G2D_COEFF_MODE_ONE, 0, 0, 0,
159                                 G2D_COEFF_MODE_SRC_ALPHA, 1, 0, 0);
160                 break;
161         case G2D_OP_INTERPOLATE:
162                 SET_BF(val, G2D_COEFF_MODE_SRC_ALPHA, 0, 0, 0,
163                                 G2D_COEFF_MODE_SRC_ALPHA, 1, 0, 0);
164                 break;
165         }
166
167         return val.val;
168 }
169
170 /*
171  * g2d_check_space - check if command buffers have enough space left.
172  *
173  * @ctx: a pointer to g2d_context structure.
174  * @num_cmds: number of (regular) commands.
175  * @num_gem_cmds: number of GEM commands.
176  */
177 static unsigned int g2d_check_space(const struct g2d_context *ctx,
178         unsigned int num_cmds, unsigned int num_gem_cmds)
179 {
180         if (ctx->cmd_nr + num_cmds >= G2D_MAX_CMD_NR ||
181             ctx->cmd_buf_nr + num_gem_cmds >= G2D_MAX_GEM_CMD_NR)
182                 return 1;
183         else
184                 return 0;
185 }
186
187 /*
188  * g2d_validate_select_mode - validate select mode.
189  *
190  * @mode: the mode to validate
191  *
192  * Returns zero for an invalid mode and one otherwise.
193  */
194 static int g2d_validate_select_mode(
195         enum e_g2d_select_mode mode)
196 {
197         switch (mode) {
198         case G2D_SELECT_MODE_NORMAL:
199         case G2D_SELECT_MODE_FGCOLOR:
200         case G2D_SELECT_MODE_BGCOLOR:
201                 return 1;
202         }
203
204         return 0;
205 }
206
207 /*
208  * g2d_validate_blending_op - validate blending operation.
209  *
210  * @operation: the operation to validate
211  *
212  * Returns zero for an invalid mode and one otherwise.
213  */
214 static int g2d_validate_blending_op(
215         enum e_g2d_op operation)
216 {
217         switch (operation) {
218         case G2D_OP_CLEAR:
219         case G2D_OP_SRC:
220         case G2D_OP_DST:
221         case G2D_OP_OVER:
222         case G2D_OP_INTERPOLATE:
223         case G2D_OP_DISJOINT_CLEAR:
224         case G2D_OP_DISJOINT_SRC:
225         case G2D_OP_DISJOINT_DST:
226         case G2D_OP_CONJOINT_CLEAR:
227         case G2D_OP_CONJOINT_SRC:
228         case G2D_OP_CONJOINT_DST:
229                 return 1;
230         }
231
232         return 0;
233 }
234
235 /*
236  * g2d_add_cmd - set given command and value to user side command buffer.
237  *
238  * @ctx: a pointer to g2d_context structure.
239  * @cmd: command data.
240  * @value: value data.
241  *
242  * The caller has to make sure that the commands buffers have enough space
243  * left to hold the command. Use g2d_check_space() to ensure this.
244  */
245 static void g2d_add_cmd(struct g2d_context *ctx, unsigned long cmd,
246                         unsigned long value)
247 {
248         switch (cmd & ~(G2D_BUF_USERPTR)) {
249         case SRC_BASE_ADDR_REG:
250         case SRC_PLANE2_BASE_ADDR_REG:
251         case DST_BASE_ADDR_REG:
252         case DST_PLANE2_BASE_ADDR_REG:
253         case PAT_BASE_ADDR_REG:
254         case MASK_BASE_ADDR_REG:
255                 assert(ctx->cmd_buf_nr < G2D_MAX_GEM_CMD_NR);
256
257                 ctx->cmd_buf[ctx->cmd_buf_nr].offset = cmd;
258                 ctx->cmd_buf[ctx->cmd_buf_nr].data = value;
259                 ctx->cmd_buf_nr++;
260                 break;
261         default:
262                 assert(ctx->cmd_nr < G2D_MAX_CMD_NR);
263
264                 ctx->cmd[ctx->cmd_nr].offset = cmd;
265                 ctx->cmd[ctx->cmd_nr].data = value;
266                 ctx->cmd_nr++;
267                 break;
268         }
269 }
270
271 /*
272  * g2d_add_base_addr - helper function to set dst/src base address register.
273  *
274  * @ctx: a pointer to g2d_context structure.
275  * @img: a pointer to the dst/src g2d_image structure.
276  * @reg: the register that should be set.
277  */
278 static void g2d_add_base_addr(struct g2d_context *ctx, struct g2d_image *img,
279                         enum g2d_base_addr_reg reg)
280 {
281         const unsigned long cmd = (reg == g2d_dst) ?
282                 DST_BASE_ADDR_REG : SRC_BASE_ADDR_REG;
283
284         if (img->buf_type == G2D_IMGBUF_USERPTR)
285                 g2d_add_cmd(ctx, cmd | G2D_BUF_USERPTR,
286                                 (unsigned long)&img->user_ptr[0]);
287         else
288                 g2d_add_cmd(ctx, cmd, img->bo[0]);
289 }
290
291 /*
292  * g2d_set_direction - setup direction register (useful for overlapping blits).
293  *
294  * @ctx: a pointer to g2d_context structure.
295  * @dir: a pointer to the g2d_direction_val structure.
296  */
297 static void g2d_set_direction(struct g2d_context *ctx,
298                         const union g2d_direction_val *dir)
299 {
300         g2d_add_cmd(ctx, SRC_MASK_DIRECT_REG, dir->val[0]);
301         g2d_add_cmd(ctx, DST_PAT_DIRECT_REG, dir->val[1]);
302 }
303
304 /*
305  * g2d_flush - submit all commands and values in user side command buffer
306  *              to command queue aware of fimg2d dma.
307  *
308  * @ctx: a pointer to g2d_context structure.
309  *
310  * This function should be called after all commands and values to user
311  * side command buffer are set. It submits that buffer to the kernel side driver.
312  */
313 static int g2d_flush(struct g2d_context *ctx)
314 {
315         int ret;
316         struct drm_exynos_g2d_set_cmdlist cmdlist = {0};
317
318         if (ctx->cmd_nr == 0 && ctx->cmd_buf_nr == 0)
319                 return 0;
320
321         if (ctx->cmdlist_nr >= G2D_MAX_CMD_LIST_NR) {
322                 fprintf(stderr, MSG_PREFIX "command list overflow.\n");
323                 return -EINVAL;
324         }
325
326         cmdlist.cmd = (uint64_t)(uintptr_t)&ctx->cmd[0];
327         cmdlist.cmd_buf = (uint64_t)(uintptr_t)&ctx->cmd_buf[0];
328         cmdlist.cmd_nr = ctx->cmd_nr;
329         cmdlist.cmd_buf_nr = ctx->cmd_buf_nr;
330
331         if (ctx->event_userdata) {
332                 cmdlist.event_type = G2D_EVENT_NONSTOP;
333                 cmdlist.user_data = (uint64_t)(uintptr_t)(ctx->event_userdata);
334                 ctx->event_userdata = NULL;
335         } else {
336                 cmdlist.event_type = G2D_EVENT_NOT;
337                 cmdlist.user_data = 0;
338         }
339
340         ctx->cmd_nr = 0;
341         ctx->cmd_buf_nr = 0;
342
343         ret = drmIoctl(ctx->fd, DRM_IOCTL_EXYNOS_G2D_SET_CMDLIST, &cmdlist);
344         if (ret < 0) {
345                 fprintf(stderr, MSG_PREFIX "failed to set cmdlist.\n");
346                 return ret;
347         }
348
349         ctx->cmdlist_nr++;
350
351         return ret;
352 }
353
354 /**
355  * g2d_init - create a new g2d context and get hardware version.
356  *
357  * fd: a file descriptor to an opened drm device.
358  */
359 struct g2d_context *g2d_init(int fd)
360 {
361         struct drm_exynos_g2d_get_ver ver;
362         struct g2d_context *ctx;
363         int ret;
364
365         ctx = calloc(1, sizeof(*ctx));
366         if (!ctx) {
367                 fprintf(stderr, MSG_PREFIX "failed to allocate context.\n");
368                 return NULL;
369         }
370
371         ctx->fd = fd;
372
373         ret = drmIoctl(fd, DRM_IOCTL_EXYNOS_G2D_GET_VER, &ver);
374         if (ret < 0) {
375                 fprintf(stderr, MSG_PREFIX "failed to get version.\n");
376                 free(ctx);
377                 return NULL;
378         }
379
380         ctx->major = ver.major;
381         ctx->minor = ver.minor;
382
383         printf(MSG_PREFIX "G2D version (%d.%d).\n", ctx->major, ctx->minor);
384         return ctx;
385 }
386
387 void g2d_fini(struct g2d_context *ctx)
388 {
389         free(ctx);
390 }
391
392 /**
393  * g2d_config_event - setup userdata configuration for a g2d event.
394  *              The next invocation of a g2d call (e.g. g2d_solid_fill) is
395  *              then going to flag the command buffer as 'nonstop'.
396  *              Completion of the command buffer execution can then be
397  *              determined by using drmHandleEvent on the DRM fd.
398  *              The userdata is 'consumed' in the process.
399  *
400  * @ctx: a pointer to g2d_context structure.
401  * @userdata: a pointer to the user data
402  */
403 void g2d_config_event(struct g2d_context *ctx, void *userdata)
404 {
405         ctx->event_userdata = userdata;
406 }
407
408 /**
409  * g2d_exec - start the dma to process all commands summited by g2d_flush().
410  *
411  * @ctx: a pointer to g2d_context structure.
412  */
413 int g2d_exec(struct g2d_context *ctx)
414 {
415         struct drm_exynos_g2d_exec exec;
416         int ret;
417
418         if (ctx->cmdlist_nr == 0)
419                 return -EINVAL;
420
421         exec.async = 0;
422
423         ret = drmIoctl(ctx->fd, DRM_IOCTL_EXYNOS_G2D_EXEC, &exec);
424         if (ret < 0) {
425                 fprintf(stderr, MSG_PREFIX "failed to execute.\n");
426                 return ret;
427         }
428
429         ctx->cmdlist_nr = 0;
430
431         return ret;
432 }
433
434 /**
435  * g2d_solid_fill - fill given buffer with given color data.
436  *
437  * @ctx: a pointer to g2d_context structure.
438  * @img: a pointer to g2d_image structure including image and buffer
439  *      information.
440  * @x: x start position to buffer filled with given color data.
441  * @y: y start position to buffer filled with given color data.
442  * @w: width value to buffer filled with given color data.
443  * @h: height value to buffer filled with given color data.
444  */
445 int
446 g2d_solid_fill(struct g2d_context *ctx, struct g2d_image *img,
447                         unsigned int x, unsigned int y, unsigned int w,
448                         unsigned int h)
449 {
450         union g2d_bitblt_cmd_val bitblt;
451         union g2d_point_val pt;
452
453         if (g2d_check_space(ctx, 7, 1))
454                 return -ENOSPC;
455
456         g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL);
457         g2d_add_cmd(ctx, DST_COLOR_MODE_REG, img->color_mode);
458         g2d_add_base_addr(ctx, img, g2d_dst);
459         g2d_add_cmd(ctx, DST_STRIDE_REG, img->stride);
460
461         if (x + w > img->width)
462                 w = img->width - x;
463         if (y + h > img->height)
464                 h = img->height - y;
465
466         pt.data.x = x;
467         pt.data.y = y;
468         g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
469
470         pt.data.x = x + w;
471         pt.data.y = y + h;
472         g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
473
474         g2d_add_cmd(ctx, SF_COLOR_REG, img->color);
475
476         bitblt.val = 0;
477         bitblt.data.fast_solid_color_fill_en = 1;
478         g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val);
479
480         return g2d_flush(ctx);
481 }
482
483 /**
484  * g2d_copy - copy contents in source buffer to destination buffer.
485  *
486  * @ctx: a pointer to g2d_context structure.
487  * @src: a pointer to g2d_image structure including image and buffer
488  *      information to source.
489  * @dst: a pointer to g2d_image structure including image and buffer
490  *      information to destination.
491  * @src_x: x start position to source buffer.
492  * @src_y: y start position to source buffer.
493  * @dst_x: x start position to destination buffer.
494  * @dst_y: y start position to destination buffer.
495  * @w: width value to source and destination buffers.
496  * @h: height value to source and destination buffers.
497  */
498 int
499 g2d_copy(struct g2d_context *ctx, struct g2d_image *src,
500                 struct g2d_image *dst, unsigned int src_x, unsigned int src_y,
501                 unsigned int dst_x, unsigned dst_y, unsigned int w,
502                 unsigned int h)
503 {
504         union g2d_rop4_val rop4;
505         union g2d_point_val pt;
506         unsigned int src_w, src_h, dst_w, dst_h;
507
508         src_w = w;
509         src_h = h;
510         if (src_x + src->width > w)
511                 src_w = src->width - src_x;
512         if (src_y + src->height > h)
513                 src_h = src->height - src_y;
514
515         dst_w = w;
516         dst_h = w;
517         if (dst_x + dst->width > w)
518                 dst_w = dst->width - dst_x;
519         if (dst_y + dst->height > h)
520                 dst_h = dst->height - dst_y;
521
522         w = MIN(src_w, dst_w);
523         h = MIN(src_h, dst_h);
524
525         if (w <= 0 || h <= 0) {
526                 fprintf(stderr, MSG_PREFIX "invalid width or height.\n");
527                 return -EINVAL;
528         }
529
530         if (g2d_check_space(ctx, 11, 2))
531                 return -ENOSPC;
532
533         g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
534         g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode);
535         g2d_add_base_addr(ctx, dst, g2d_dst);
536         g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride);
537
538         g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL);
539         g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode);
540         g2d_add_base_addr(ctx, src, g2d_src);
541         g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride);
542
543         pt.data.x = src_x;
544         pt.data.y = src_y;
545         g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
546         pt.data.x = src_x + w;
547         pt.data.y = src_y + h;
548         g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
549
550         pt.data.x = dst_x;
551         pt.data.y = dst_y;
552         g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
553         pt.data.x = dst_x + w;
554         pt.data.y = dst_y + h;
555         g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
556
557         rop4.val = 0;
558         rop4.data.unmasked_rop3 = G2D_ROP3_SRC;
559         g2d_add_cmd(ctx, ROP4_REG, rop4.val);
560
561         return g2d_flush(ctx);
562 }
563
564 /**
565  * g2d_move - copy content inside single buffer.
566  *      Similar to libc's memmove() this copies a rectangular
567  *      region of the provided buffer to another location, while
568  *      properly handling the situation where source and
569  *      destination rectangle overlap.
570  *
571  * @ctx: a pointer to g2d_context structure.
572  * @img: a pointer to g2d_image structure providing
573  *      buffer information.
574  * @src_x: x position of source rectangle.
575  * @src_y: y position of source rectangle.
576  * @dst_x: x position of destination rectangle.
577  * @dst_y: y position of destination rectangle.
578  * @w: width of rectangle to move.
579  * @h: height of rectangle to move.
580  */
581 int
582 g2d_move(struct g2d_context *ctx, struct g2d_image *img,
583                 unsigned int src_x, unsigned int src_y,
584                 unsigned int dst_x, unsigned dst_y, unsigned int w,
585                 unsigned int h)
586 {
587         union g2d_rop4_val rop4;
588         union g2d_point_val pt;
589         union g2d_direction_val dir;
590         unsigned int src_w, src_h, dst_w, dst_h;
591
592         src_w = w;
593         src_h = h;
594         if (src_x + img->width > w)
595                 src_w = img->width - src_x;
596         if (src_y + img->height > h)
597                 src_h = img->height - src_y;
598
599         dst_w = w;
600         dst_h = w;
601         if (dst_x + img->width > w)
602                 dst_w = img->width - dst_x;
603         if (dst_y + img->height > h)
604                 dst_h = img->height - dst_y;
605
606         w = MIN(src_w, dst_w);
607         h = MIN(src_h, dst_h);
608
609         if (w == 0 || h == 0) {
610                 fprintf(stderr, MSG_PREFIX "invalid width or height.\n");
611                 return -EINVAL;
612         }
613
614         if (g2d_check_space(ctx, 13, 2))
615                 return -ENOSPC;
616
617         g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
618         g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL);
619
620         g2d_add_cmd(ctx, DST_COLOR_MODE_REG, img->color_mode);
621         g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, img->color_mode);
622
623         g2d_add_base_addr(ctx, img, g2d_dst);
624         g2d_add_base_addr(ctx, img, g2d_src);
625
626         g2d_add_cmd(ctx, DST_STRIDE_REG, img->stride);
627         g2d_add_cmd(ctx, SRC_STRIDE_REG, img->stride);
628
629         dir.val[0] = dir.val[1] = 0;
630
631         if (dst_x >= src_x)
632                 dir.data.src_x_direction = dir.data.dst_x_direction = 1;
633         if (dst_y >= src_y)
634                 dir.data.src_y_direction = dir.data.dst_y_direction = 1;
635
636         g2d_set_direction(ctx, &dir);
637
638         pt.data.x = src_x;
639         pt.data.y = src_y;
640         g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
641         pt.data.x = src_x + w;
642         pt.data.y = src_y + h;
643         g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
644
645         pt.data.x = dst_x;
646         pt.data.y = dst_y;
647         g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
648         pt.data.x = dst_x + w;
649         pt.data.y = dst_y + h;
650         g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
651
652         rop4.val = 0;
653         rop4.data.unmasked_rop3 = G2D_ROP3_SRC;
654         g2d_add_cmd(ctx, ROP4_REG, rop4.val);
655
656         return g2d_flush(ctx);
657 }
658
659 /**
660  * g2d_copy_with_scale - copy contents in source buffer to destination buffer
661  *      scaling up or down properly.
662  *
663  * @ctx: a pointer to g2d_context structure.
664  * @src: a pointer to g2d_image structure including image and buffer
665  *      information to source.
666  * @dst: a pointer to g2d_image structure including image and buffer
667  *      information to destination.
668  * @src_x: x start position to source buffer.
669  * @src_y: y start position to source buffer.
670  * @src_w: width value to source buffer.
671  * @src_h: height value to source buffer.
672  * @dst_x: x start position to destination buffer.
673  * @dst_y: y start position to destination buffer.
674  * @dst_w: width value to destination buffer.
675  * @dst_h: height value to destination buffer.
676  * @negative: indicate that it uses color negative to source and
677  *      destination buffers.
678  */
679 int
680 g2d_copy_with_scale(struct g2d_context *ctx, struct g2d_image *src,
681                                 struct g2d_image *dst, unsigned int src_x,
682                                 unsigned int src_y, unsigned int src_w,
683                                 unsigned int src_h, unsigned int dst_x,
684                                 unsigned int dst_y, unsigned int dst_w,
685                                 unsigned int dst_h, unsigned int negative)
686 {
687         union g2d_rop4_val rop4;
688         union g2d_point_val pt;
689         unsigned int scale, repeat_pad;
690         unsigned int scale_x, scale_y;
691
692         /* Sanitize this parameter to facilitate space computation below. */
693         if (negative)
694                 negative = 1;
695
696         if (src_w == dst_w && src_h == dst_h)
697                 scale = 0;
698         else {
699                 scale = 1;
700                 scale_x = g2d_get_scaling(src_w, dst_w);
701                 scale_y = g2d_get_scaling(src_h, dst_h);
702         }
703
704         repeat_pad = src->repeat_mode == G2D_REPEAT_MODE_PAD ? 1 : 0;
705
706         if (src_x + src_w > src->width)
707                 src_w = src->width - src_x;
708         if (src_y + src_h > src->height)
709                 src_h = src->height - src_y;
710
711         if (dst_x + dst_w > dst->width)
712                 dst_w = dst->width - dst_x;
713         if (dst_y + dst_h > dst->height)
714                 dst_h = dst->height - dst_y;
715
716         if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) {
717                 fprintf(stderr, MSG_PREFIX "invalid width or height.\n");
718                 return -EINVAL;
719         }
720
721         if (g2d_check_space(ctx, 12 + scale * 3 + negative + repeat_pad, 2))
722                 return -ENOSPC;
723
724         g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
725         g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode);
726         g2d_add_base_addr(ctx, dst, g2d_dst);
727         g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride);
728
729         g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL);
730         g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode);
731
732         g2d_add_cmd(ctx, SRC_REPEAT_MODE_REG, src->repeat_mode);
733         if (repeat_pad)
734                 g2d_add_cmd(ctx, SRC_PAD_VALUE_REG, dst->color);
735
736         g2d_add_base_addr(ctx, src, g2d_src);
737         g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride);
738
739         rop4.val = 0;
740         rop4.data.unmasked_rop3 = G2D_ROP3_SRC;
741
742         if (negative) {
743                 g2d_add_cmd(ctx, BG_COLOR_REG, 0x00FFFFFF);
744                 rop4.data.unmasked_rop3 ^= G2D_ROP3_DST;
745         }
746
747         g2d_add_cmd(ctx, ROP4_REG, rop4.val);
748
749         if (scale) {
750                 g2d_add_cmd(ctx, SRC_SCALE_CTRL_REG, G2D_SCALE_MODE_BILINEAR);
751                 g2d_add_cmd(ctx, SRC_XSCALE_REG, scale_x);
752                 g2d_add_cmd(ctx, SRC_YSCALE_REG, scale_y);
753         }
754
755         pt.data.x = src_x;
756         pt.data.y = src_y;
757         g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
758         pt.data.x = src_x + src_w;
759         pt.data.y = src_y + src_h;
760         g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
761
762         pt.data.x = dst_x;
763         pt.data.y = dst_y;
764         g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
765         pt.data.x = dst_x + dst_w;
766         pt.data.y = dst_y + dst_h;
767         g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
768
769         return g2d_flush(ctx);
770 }
771
772 /**
773  * g2d_blend - blend image data in source and destination buffers.
774  *
775  * @ctx: a pointer to g2d_context structure.
776  * @src: a pointer to g2d_image structure including image and buffer
777  *      information to source.
778  * @dst: a pointer to g2d_image structure including image and buffer
779  *      information to destination.
780  * @src_x: x start position to source buffer.
781  * @src_y: y start position to source buffer.
782  * @dst_x: x start position to destination buffer.
783  * @dst_y: y start position to destination buffer.
784  * @w: width value to source and destination buffer.
785  * @h: height value to source and destination buffer.
786  * @op: blend operation type.
787  */
788 int
789 g2d_blend(struct g2d_context *ctx, struct g2d_image *src,
790                 struct g2d_image *dst, unsigned int src_x,
791                 unsigned int src_y, unsigned int dst_x, unsigned int dst_y,
792                 unsigned int w, unsigned int h, enum e_g2d_op op)
793 {
794         union g2d_point_val pt;
795         union g2d_bitblt_cmd_val bitblt;
796         union g2d_blend_func_val blend;
797         unsigned int gem_space;
798         unsigned int src_w, src_h, dst_w, dst_h;
799
800         src_w = w;
801         src_h = h;
802         if (src_x + w > src->width)
803                 src_w = src->width - src_x;
804         if (src_y + h > src->height)
805                 src_h = src->height - src_y;
806
807         dst_w = w;
808         dst_h = h;
809         if (dst_x + w > dst->width)
810                 dst_w = dst->width - dst_x;
811         if (dst_y + h > dst->height)
812                 dst_h = dst->height - dst_y;
813
814         w = MIN(src_w, dst_w);
815         h = MIN(src_h, dst_h);
816
817         if (w <= 0 || h <= 0) {
818                 fprintf(stderr, MSG_PREFIX "invalid width or height.\n");
819                 return -EINVAL;
820         }
821
822         if (!g2d_validate_select_mode(src->select_mode)) {
823                 fprintf(stderr , MSG_PREFIX "invalid select mode for source.\n");
824                 return -EINVAL;
825         }
826
827         if (!g2d_validate_blending_op(op)) {
828                 fprintf(stderr , MSG_PREFIX "unsupported blending operation.\n");
829                 return -EINVAL;
830         }
831
832         gem_space = src->select_mode == G2D_SELECT_MODE_NORMAL ? 2 : 1;
833
834         if (g2d_check_space(ctx, 12, gem_space))
835                 return -ENOSPC;
836
837         bitblt.val = 0;
838         blend.val = 0;
839
840         if (op == G2D_OP_SRC || op == G2D_OP_CLEAR)
841                 g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
842         else
843                 g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL);
844
845         g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode);
846         g2d_add_base_addr(ctx, dst, g2d_dst);
847         g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride);
848
849         g2d_add_cmd(ctx, SRC_SELECT_REG, src->select_mode);
850         g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode);
851
852         switch (src->select_mode) {
853         case G2D_SELECT_MODE_NORMAL:
854                 g2d_add_base_addr(ctx, src, g2d_src);
855                 g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride);
856                 break;
857         case G2D_SELECT_MODE_FGCOLOR:
858                 g2d_add_cmd(ctx, FG_COLOR_REG, src->color);
859                 break;
860         case G2D_SELECT_MODE_BGCOLOR:
861                 g2d_add_cmd(ctx, BG_COLOR_REG, src->color);
862                 break;
863         }
864
865         bitblt.data.alpha_blend_mode = G2D_ALPHA_BLEND_MODE_ENABLE;
866         blend.val = g2d_get_blend_op(op);
867         g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val);
868         g2d_add_cmd(ctx, BLEND_FUNCTION_REG, blend.val);
869
870         pt.data.x = src_x;
871         pt.data.y = src_y;
872         g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
873         pt.data.x = src_x + w;
874         pt.data.y = src_y + h;
875         g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
876
877         pt.data.x = dst_x;
878         pt.data.y = dst_y;
879         g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
880         pt.data.x = dst_x + w;
881         pt.data.y = dst_y + h;
882         g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
883
884         return g2d_flush(ctx);
885 }
886
887 /**
888  * g2d_scale_and_blend - apply scaling to source buffer and then blend to destination buffer
889  *
890  * @ctx: a pointer to g2d_context structure.
891  * @src: a pointer to g2d_image structure including image and buffer
892  *      information to source.
893  * @dst: a pointer to g2d_image structure including image and buffer
894  *      information to destination.
895  * @src_x: x start position to source buffer.
896  * @src_y: y start position to source buffer.
897  * @src_w: width value to source buffer.
898  * @src_h: height value to source buffer.
899  * @dst_x: x start position to destination buffer.
900  * @dst_y: y start position to destination buffer.
901  * @dst_w: width value to destination buffer.
902  * @dst_h: height value to destination buffer.
903  * @op: blend operation type.
904  */
905 int
906 g2d_scale_and_blend(struct g2d_context *ctx, struct g2d_image *src,
907                 struct g2d_image *dst, unsigned int src_x, unsigned int src_y,
908                 unsigned int src_w, unsigned int src_h, unsigned int dst_x,
909                 unsigned int dst_y, unsigned int dst_w, unsigned int dst_h,
910                 enum e_g2d_op op)
911 {
912         union g2d_point_val pt;
913         union g2d_bitblt_cmd_val bitblt;
914         union g2d_blend_func_val blend;
915         unsigned int scale, gem_space;
916         unsigned int scale_x, scale_y;
917
918         if (src_w == dst_w && src_h == dst_h)
919                 scale = 0;
920         else {
921                 scale = 1;
922                 scale_x = g2d_get_scaling(src_w, dst_w);
923                 scale_y = g2d_get_scaling(src_h, dst_h);
924         }
925
926         if (src_x + src_w > src->width)
927                 src_w = src->width - src_x;
928         if (src_y + src_h > src->height)
929                 src_h = src->height - src_y;
930
931         if (dst_x + dst_w > dst->width)
932                 dst_w = dst->width - dst_x;
933         if (dst_y + dst_h > dst->height)
934                 dst_h = dst->height - dst_y;
935
936         if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) {
937                 fprintf(stderr, MSG_PREFIX "invalid width or height.\n");
938                 return -EINVAL;
939         }
940
941         if (!g2d_validate_select_mode(src->select_mode)) {
942                 fprintf(stderr , MSG_PREFIX "invalid select mode for source.\n");
943                 return -EINVAL;
944         }
945
946         if (!g2d_validate_blending_op(op)) {
947                 fprintf(stderr , MSG_PREFIX "unsupported blending operation.\n");
948                 return -EINVAL;
949         }
950
951         gem_space = src->select_mode == G2D_SELECT_MODE_NORMAL ? 2 : 1;
952
953         if (g2d_check_space(ctx, 12 + scale * 3, gem_space))
954                 return -ENOSPC;
955
956         bitblt.val = 0;
957         blend.val = 0;
958
959         if (op == G2D_OP_SRC || op == G2D_OP_CLEAR)
960                 g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
961         else
962                 g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL);
963
964         g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode);
965         g2d_add_base_addr(ctx, dst, g2d_dst);
966         g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride);
967
968         g2d_add_cmd(ctx, SRC_SELECT_REG, src->select_mode);
969         g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode);
970
971         switch (src->select_mode) {
972         case G2D_SELECT_MODE_NORMAL:
973                 g2d_add_base_addr(ctx, src, g2d_src);
974                 g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride);
975                 break;
976         case G2D_SELECT_MODE_FGCOLOR:
977                 g2d_add_cmd(ctx, FG_COLOR_REG, src->color);
978                 break;
979         case G2D_SELECT_MODE_BGCOLOR:
980                 g2d_add_cmd(ctx, BG_COLOR_REG, src->color);
981                 break;
982         }
983
984         if (scale) {
985                 g2d_add_cmd(ctx, SRC_SCALE_CTRL_REG, G2D_SCALE_MODE_BILINEAR);
986                 g2d_add_cmd(ctx, SRC_XSCALE_REG, scale_x);
987                 g2d_add_cmd(ctx, SRC_YSCALE_REG, scale_y);
988         }
989
990         bitblt.data.alpha_blend_mode = G2D_ALPHA_BLEND_MODE_ENABLE;
991         blend.val = g2d_get_blend_op(op);
992         g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val);
993         g2d_add_cmd(ctx, BLEND_FUNCTION_REG, blend.val);
994
995         pt.data.x = src_x;
996         pt.data.y = src_y;
997         g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
998         pt.data.x = src_x + src_w;
999         pt.data.y = src_y + src_h;
1000         g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
1001
1002         pt.data.x = dst_x;
1003         pt.data.y = dst_y;
1004         g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
1005         pt.data.x = dst_x + dst_w;
1006         pt.data.y = dst_y + dst_h;
1007         g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
1008
1009         return g2d_flush(ctx);
1010 }