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